diff --git a/public/js/order-list.js b/public/js/order-list.js
index fc6452f..4209ce7 100644
--- a/public/js/order-list.js
+++ b/public/js/order-list.js
@@ -1,20 +1,38 @@
-let orderPage = 1;
-let orderHasMore = true;
-let orderLoading = false;
+// 全局状态变量(供 app.js 引用)
+var orderPage = 1, orderHasMore = true, orderLoading = false;
+var otherIncomePage = 1, otherIncomeHasMore = true, otherIncomeLoading = false;
+var dispatchPage = 1, dispatchHasMore = true, dispatchLoading = false;
+var redEnvelopePage = 1, redEnvelopeHasMore = true, redEnvelopeLoading = false;
+var milkTeaPage = 1, milkTeaHasMore = true, milkTeaLoading = false;
+
+// 通用分页状态管理
+const listConfig = {
+ orderList: { getPage: () => orderPage, setPage: (v) => orderPage = v, getHasMore: () => orderHasMore, setHasMore: (v) => orderHasMore = v, getLoading: () => orderLoading, setLoading: (v) => orderLoading = v, emptyMsg: '暂无接单记录', endpoint: '/api/records/orders', renderItem: renderOrderItem },
+ otherIncomeList: { getPage: () => otherIncomePage, setPage: (v) => otherIncomePage = v, getHasMore: () => otherIncomeHasMore, setHasMore: (v) => otherIncomeHasMore = v, getLoading: () => otherIncomeLoading, setLoading: (v) => otherIncomeLoading = v, emptyMsg: '暂无红包及其他收入记录', endpoint: '/api/records/other-income', renderItem: renderOtherIncomeItem },
+ dispatchList: { getPage: () => dispatchPage, setPage: (v) => dispatchPage = v, getHasMore: () => dispatchHasMore, setHasMore: (v) => dispatchHasMore = v, getLoading: () => dispatchLoading, setLoading: (v) => dispatchLoading = v, emptyMsg: '暂无派单记录', endpoint: '/api/records/dispatches', renderItem: renderDispatchItem },
+ redEnvelopeList: { getPage: () => redEnvelopePage, setPage: (v) => redEnvelopePage = v, getHasMore: () => redEnvelopeHasMore, setHasMore: (v) => redEnvelopeHasMore = v, getLoading: () => redEnvelopeLoading, setLoading: (v) => redEnvelopeLoading = v, emptyMsg: '暂无红包收入记录', endpoint: '/api/records/red-envelope', renderItem: renderRedEnvelopeItem },
+ milkTeaList: { getPage: () => milkTeaPage, setPage: (v) => milkTeaPage = v, getHasMore: () => milkTeaHasMore, setHasMore: (v) => milkTeaHasMore = v, getLoading: () => milkTeaLoading, setLoading: (v) => milkTeaLoading = v, emptyMsg: '暂无奶茶收入记录', endpoint: '/api/records/milk-tea', renderItem: renderMilkTeaItem }
+};
+
+// 通用列表加载函数
+async function loadList(listId, append) {
+ const config = listConfig[listId];
+ if (!config) return;
+
+ if (config.getLoading() || (!append && !config.getHasMore())) return;
+ config.setLoading(true);
+ document.getElementById(listId.replace('List', 'Loading')).style.display = 'block';
-async function loadOrders(append) {
- if (orderLoading || (!append && !orderHasMore)) return;
- orderLoading = true;
- document.getElementById('orderLoading').style.display = 'block';
try {
- const res = await fetch(API + '/api/records/orders?page=' + orderPage + '&limit=20&year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
+ const res = await fetch(API + config.endpoint + '?page=' + config.getPage() + '&limit=20&year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
if (!res.ok) return;
const data = await res.json();
- const container = document.getElementById('orderList');
+ const container = document.getElementById(listId);
if (!append) container.innerHTML = '';
+
if (data.records.length === 0) {
- if (orderPage === 1) container.innerHTML = '
-
${date}
`;
+ html += '
' + date + '
';
records.forEach(r => {
- const time = formatLocalTime(r.created_at);
- html += `
-
- ${r.boss || '-'}
- ${(r.amount || 0).toFixed(2)}
-
-
- ${r.quantity || 0}单 × ${(r.unit_price || 0).toFixed(2)}
- ${time}${r.partner ? ' · ' + r.partner : ''}
-
-
`;
+ html += config.renderItem(r);
});
html += '
';
}
container.insertAdjacentHTML('beforeend', html);
- orderHasMore = data.records.length >= 20;
- orderPage++;
+ config.setHasMore(data.records.length >= 20);
+ config.setPage(config.getPage() + 1);
}
} catch (e) { console.error(e); }
- orderLoading = false;
- document.getElementById('orderLoading').style.display = 'none';
+
+ config.setLoading(false);
+ document.getElementById(listId.replace('List', 'Loading')).style.display = 'none';
+}
+
+// 通用渲染函数
+function renderOrderItem(r) {
+ const time = formatLocalTime(r.created_at);
+ return '
' +
+ '
' + (r.boss || '-') + '' + (r.amount || 0).toFixed(2) + '
' +
+ '
' + (r.quantity || 0) + '单 × ' + (r.unit_price || 0).toFixed(2) + '' + time + (r.partner ? ' · ' + r.partner : '') + '
';
+}
+
+function renderOtherIncomeItem(r) {
+ const time = formatLocalTime(r.created_at);
+ const displayCategory = r.category === '红包收入' ? '红包' : (r.category || '-');
+ return '
' +
+ '
' + displayCategory + (r.boss ? ' · ' + r.boss : '') + '' + (r.amount || 0).toFixed(2) + '
' +
+ '
' + (r.source || '') + '' + time + '
';
+}
+
+function renderDispatchItem(r) {
+ const time = formatLocalTime(r.created_at);
+ return '
' +
+ '
' + (r.boss || '-') + '' + (r.amount || 0).toFixed(2) + '
' +
+ '
' + (r.quantity || 0) + '单 × ' + ((r.rebate || r.amount / r.quantity) || 0).toFixed(2) + '(单价' + (r.unit_price || 0).toFixed(2) + ')' + time + (r.partner ? ' · ' + r.partner : '') + '
';
+}
+
+function renderRedEnvelopeItem(r) {
+ const time = formatLocalTime(r.created_at);
+ return '
' +
+ '
红包' + (r.amount || 0).toFixed(2) + '
' +
+ '
' + (r.source || '') + '' + time + '
';
+}
+
+function renderMilkTeaItem(r) {
+ const time = formatLocalTime(r.created_at);
+ return '
' +
+ '
奶茶' + (r.amount || 0).toFixed(2) + '
' +
+ '
' + (r.source || '') + '' + time + '
';
}
// 滚动加载
window.addEventListener('scroll', () => {
- if (document.getElementById('orderListPage').classList.contains('active')) {
- if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
- if (orderHasMore && !orderLoading) loadOrders(true);
- }
- }
- if (document.getElementById('dispatchListPage').classList.contains('active')) {
- if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
- if (dispatchHasMore && !dispatchLoading) loadDispatches(true);
- }
- }
- if (document.getElementById('otherIncomeListPage').classList.contains('active')) {
- if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
- if (otherIncomeHasMore && !otherIncomeLoading) loadOtherIncome(true);
- }
- }
- if (document.getElementById('redEnvelopeListPage').classList.contains('active')) {
- if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
- if (redEnvelopeHasMore && !redEnvelopeLoading) loadRedEnvelope(true);
- }
- }
- if (document.getElementById('milkTeaListPage').classList.contains('active')) {
- if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
- if (milkTeaHasMore && !milkTeaLoading) loadMilkTea(true);
+ const pageListMap = [
+ { page: 'orderListPage', list: 'orderList' },
+ { page: 'dispatchListPage', list: 'dispatchList' },
+ { page: 'otherIncomeListPage', list: 'otherIncomeList' },
+ { page: 'redEnvelopeListPage', list: 'redEnvelopeList' },
+ { page: 'milkTeaListPage', list: 'milkTeaList' }
+ ];
+
+ for (const m of pageListMap) {
+ if (document.getElementById(m.page).classList.contains('active')) {
+ if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
+ const config = listConfig[m.list];
+ if (config && config.getHasMore() && !config.getLoading()) {
+ loadList(m.list, true);
+ }
+ }
+ break;
}
}
});
-// 红包及其他收入列表
-let otherIncomePage = 1;
-let otherIncomeHasMore = true;
-let otherIncomeLoading = false;
-
-async function loadOtherIncome(append) {
- if (otherIncomeLoading || (!append && !otherIncomeHasMore)) return;
- otherIncomeLoading = true;
- document.getElementById('otherIncomeLoading').style.display = 'block';
- try {
- const res = await fetch(API + '/api/records/other-income?page=' + otherIncomePage + '&limit=20&year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
- if (!res.ok) return;
- const data = await res.json();
- const container = document.getElementById('otherIncomeList');
- if (!append) container.innerHTML = '';
- if (data.records.length === 0) {
- if (otherIncomePage === 1) container.innerHTML = '
暂无红包及其他收入记录
';
- otherIncomeHasMore = false;
- } else {
- const grouped = {};
- data.records.forEach(r => {
- const date = formatLocalDate(r.created_at);
- if (!grouped[date]) grouped[date] = [];
- grouped[date].push(r);
- });
- let html = '';
- for (const [date, records] of Object.entries(grouped)) {
- html += `
-
${date}
`;
- records.forEach(r => {
- const time = formatLocalTime(r.created_at);
- const displayCategory = r.category === '红包收入' ? '红包' : (r.category || '-');
- html += `
-
- ${displayCategory}${r.boss ? ' · ' + r.boss : ''}
- ${(r.amount || 0).toFixed(2)}
-
-
- ${r.source || ''}
- ${time}
-
-
`;
- });
- html += '
';
- }
- container.insertAdjacentHTML('beforeend', html);
- otherIncomeHasMore = data.records.length >= 20;
- otherIncomePage++;
- }
- } catch (e) { console.error(e); }
- otherIncomeLoading = false;
- document.getElementById('otherIncomeLoading').style.display = 'none';
-}
-
-// 派单列表
-let dispatchPage = 1;
-let dispatchHasMore = true;
-let dispatchLoading = false;
-
-async function loadDispatches(append) {
- if (dispatchLoading || (!append && !dispatchHasMore)) return;
- dispatchLoading = true;
- document.getElementById('dispatchLoading').style.display = 'block';
- try {
- const res = await fetch(API + '/api/records/dispatches?page=' + dispatchPage + '&limit=20&year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
- if (!res.ok) return;
- const data = await res.json();
- const container = document.getElementById('dispatchList');
- if (!append) container.innerHTML = '';
- if (data.records.length === 0) {
- if (dispatchPage === 1) container.innerHTML = '
暂无派单记录
';
- dispatchHasMore = false;
- } else {
- const grouped = {};
- data.records.forEach(r => {
- const date = formatLocalDate(r.created_at);
- if (!grouped[date]) grouped[date] = [];
- grouped[date].push(r);
- });
- let html = '';
- for (const [date, records] of Object.entries(grouped)) {
- html += `
-
${date}
`;
- records.forEach(r => {
- const time = formatLocalTime(r.created_at);
- html += `
-
- ${r.boss || '-'}
- ${(r.amount || 0).toFixed(2)}
-
-
- ${r.quantity || 0}单 × ${(r.rebate || r.amount / r.quantity).toFixed(2)}(单价${(r.unit_price || 0).toFixed(2)})
- ${time}${r.partner ? ' · ' + r.partner : ''}
-
-
`;
- });
- html += '
';
- }
- container.insertAdjacentHTML('beforeend', html);
- dispatchHasMore = data.records.length >= 20;
- dispatchPage++;
- }
- } catch (e) { console.error(e); }
- dispatchLoading = false;
- document.getElementById('dispatchLoading').style.display = 'none';
-}
-
-// 红包收入列表
-let redEnvelopePage = 1;
-let redEnvelopeHasMore = true;
-let redEnvelopeLoading = false;
-
-async function loadRedEnvelope(append) {
- if (redEnvelopeLoading || (!append && !redEnvelopeHasMore)) return;
- redEnvelopeLoading = true;
- document.getElementById('redEnvelopeLoading').style.display = 'block';
- try {
- const res = await fetch(API + '/api/records/red-envelope?page=' + redEnvelopePage + '&limit=20&year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
- if (!res.ok) return;
- const data = await res.json();
- const container = document.getElementById('redEnvelopeList');
- if (!append) container.innerHTML = '';
- if (data.records.length === 0) {
- if (redEnvelopePage === 1) container.innerHTML = '
暂无红包收入记录
';
- redEnvelopeHasMore = false;
- } else {
- const grouped = {};
- data.records.forEach(r => {
- const date = formatLocalDate(r.created_at);
- if (!grouped[date]) grouped[date] = [];
- grouped[date].push(r);
- });
- let html = '';
- for (const [date, records] of Object.entries(grouped)) {
- html += `
-
${date}
`;
- records.forEach(r => {
- const time = formatLocalTime(r.created_at);
- html += `
-
- 红包
- ${(r.amount || 0).toFixed(2)}
-
-
- ${r.source || ''}
- ${time}
-
-
`;
- });
- html += '
';
- }
- container.insertAdjacentHTML('beforeend', html);
- redEnvelopeHasMore = data.records.length >= 20;
- redEnvelopePage++;
- }
- } catch (e) { console.error(e); }
- redEnvelopeLoading = false;
- document.getElementById('redEnvelopeLoading').style.display = 'none';
-}
-
-// 奶茶收入列表
-let milkTeaPage = 1;
-let milkTeaHasMore = true;
-let milkTeaLoading = false;
-
-async function loadMilkTea(append) {
- if (milkTeaLoading || (!append && !milkTeaHasMore)) return;
- milkTeaLoading = true;
- document.getElementById('milkTeaLoading').style.display = 'block';
- try {
- const res = await fetch(API + '/api/records/milk-tea?page=' + milkTeaPage + '&limit=20&year=' + currentYear + '&month=' + currentMonth, { headers: headers() });
- if (!res.ok) return;
- const data = await res.json();
- const container = document.getElementById('milkTeaList');
- if (!append) container.innerHTML = '';
- if (data.records.length === 0) {
- if (milkTeaPage === 1) container.innerHTML = '
暂无奶茶收入记录
';
- milkTeaHasMore = false;
- } else {
- const grouped = {};
- data.records.forEach(r => {
- const date = formatLocalDate(r.created_at);
- if (!grouped[date]) grouped[date] = [];
- grouped[date].push(r);
- });
- let html = '';
- for (const [date, records] of Object.entries(grouped)) {
- html += `
-
${date}
`;
- records.forEach(r => {
- const time = formatLocalTime(r.created_at);
- html += `
-
- 奶茶
- ${(r.amount || 0).toFixed(2)}
-
-
- ${r.source || ''}
- ${time}
-
-
`;
- });
- html += '
';
- }
- container.insertAdjacentHTML('beforeend', html);
- milkTeaHasMore = data.records.length >= 20;
- milkTeaPage++;
- }
- } catch (e) { console.error(e); }
- milkTeaLoading = false;
- document.getElementById('milkTeaLoading').style.display = 'none';
-}
+// 兼容旧接口
+async function loadOrders(append) { return loadList('orderList', append); }
+async function loadOtherIncome(append) { return loadList('otherIncomeList', append); }
+async function loadDispatches(append) { return loadList('dispatchList', append); }
+async function loadRedEnvelope(append) { return loadList('redEnvelopeList', append); }
+async function loadMilkTea(append) { return loadList('milkTeaList', append); }
diff --git a/server/routes/records.js b/server/routes/records.js
index c28fae4..7f5a8b4 100644
--- a/server/routes/records.js
+++ b/server/routes/records.js
@@ -15,89 +15,80 @@ router.post('/', (req, res) => {
res.json({ id: result.lastInsertRowid });
});
-// 接单列表
-router.get('/orders', (req, res) => {
- const { page = 1, limit = 20 } = req.query;
+// 通用分页查询函数
+function queryPagedRecords(userId, year, month, options) {
+ const { page = 1, limit = 20 } = options;
const offset = (Number(page) - 1) * Number(limit);
- const { startDate, endDate } = getMonthRange(req.query.year, req.query.month);
+ const { startDate, endDate } = getMonthRange(year, month);
+
+ let whereClause = `user_id = ? AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ?`;
+ const params = [userId, startDate, endDate];
+
+ if (options.category) {
+ whereClause += ` AND category = ?`;
+ params.push(options.category);
+ }
+ if (options.type) {
+ whereClause += ` AND type = ?`;
+ params.push(options.type);
+ }
+ if (options.excludeCategories && options.excludeCategories.length > 0) {
+ whereClause += ` AND category NOT IN (${options.excludeCategories.map(() => '?').join(',')})`;
+ params.push(...options.excludeCategories);
+ }
const total = db.prepare(
- `SELECT COUNT(*) as count FROM records WHERE user_id = ? AND category = '接单' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ?`
- ).get(req.user.id, startDate, endDate).count;
+ `SELECT COUNT(*) as count FROM records WHERE ${whereClause}`
+ ).get(...params).count;
const records = db.prepare(
- `SELECT * FROM records WHERE user_id = ? AND category = '接单' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? ORDER BY created_at DESC LIMIT ? OFFSET ?`
- ).all(req.user.id, startDate, endDate, Number(limit), offset);
+ `SELECT * FROM records WHERE ${whereClause} ORDER BY created_at DESC LIMIT ? OFFSET ?`
+ ).all(...params, Number(limit), offset);
- res.json({ records, total, page: Number(page), limit: Number(limit) });
+ return { records, total, page: Number(page), limit: Number(limit) };
+}
+
+// 接单列表
+router.get('/orders', (req, res) => {
+ const result = queryPagedRecords(req.user.id, req.query.year, req.query.month, {
+ category: '接单'
+ });
+ res.json(result);
});
// 红包及其他收入列表
router.get('/other-income', (req, res) => {
- const { page = 1, limit = 20 } = req.query;
- const offset = (Number(page) - 1) * Number(limit);
- const { startDate, endDate } = getMonthRange(req.query.year, req.query.month);
-
- const total = db.prepare(
- `SELECT COUNT(*) as count FROM records WHERE user_id = ? AND type = 'income' AND category != '接单' AND category != '派单' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ?`
- ).get(req.user.id, startDate, endDate).count;
-
- const records = db.prepare(
- `SELECT * FROM records WHERE user_id = ? AND type = 'income' AND category != '接单' AND category != '派单' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? ORDER BY created_at DESC LIMIT ? OFFSET ?`
- ).all(req.user.id, startDate, endDate, Number(limit), offset);
-
- res.json({ records, total, page: Number(page), limit: Number(limit) });
+ const result = queryPagedRecords(req.user.id, req.query.year, req.query.month, {
+ type: 'income',
+ excludeCategories: ['接单', '派单']
+ });
+ res.json(result);
});
// 红包收入列表
router.get('/red-envelope', (req, res) => {
- const { page = 1, limit = 20 } = req.query;
- const offset = (Number(page) - 1) * Number(limit);
- const { startDate, endDate } = getMonthRange(req.query.year, req.query.month);
-
- const total = db.prepare(
- `SELECT COUNT(*) as count FROM records WHERE user_id = ? AND type = 'income' AND category = '红包收入' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ?`
- ).get(req.user.id, startDate, endDate).count;
-
- const records = db.prepare(
- `SELECT * FROM records WHERE user_id = ? AND type = 'income' AND category = '红包收入' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? ORDER BY created_at DESC LIMIT ? OFFSET ?`
- ).all(req.user.id, startDate, endDate, Number(limit), offset);
-
- res.json({ records, total, page: Number(page), limit: Number(limit) });
+ const result = queryPagedRecords(req.user.id, req.query.year, req.query.month, {
+ type: 'income',
+ category: '红包收入'
+ });
+ res.json(result);
});
// 奶茶收入列表
router.get('/milk-tea', (req, res) => {
- const { page = 1, limit = 20 } = req.query;
- const offset = (Number(page) - 1) * Number(limit);
- const { startDate, endDate } = getMonthRange(req.query.year, req.query.month);
-
- const total = db.prepare(
- `SELECT COUNT(*) as count FROM records WHERE user_id = ? AND type = 'income' AND category = '奶茶' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ?`
- ).get(req.user.id, startDate, endDate).count;
-
- const records = db.prepare(
- `SELECT * FROM records WHERE user_id = ? AND type = 'income' AND category = '奶茶' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? ORDER BY created_at DESC LIMIT ? OFFSET ?`
- ).all(req.user.id, startDate, endDate, Number(limit), offset);
-
- res.json({ records, total, page: Number(page), limit: Number(limit) });
+ const result = queryPagedRecords(req.user.id, req.query.year, req.query.month, {
+ type: 'income',
+ category: '奶茶'
+ });
+ res.json(result);
});
// 派单列表
router.get('/dispatches', (req, res) => {
- const { page = 1, limit = 20 } = req.query;
- const offset = (Number(page) - 1) * Number(limit);
- const { startDate, endDate } = getMonthRange(req.query.year, req.query.month);
-
- const total = db.prepare(
- `SELECT COUNT(*) as count FROM records WHERE user_id = ? AND category = '派单' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ?`
- ).get(req.user.id, startDate, endDate).count;
-
- const records = db.prepare(
- `SELECT * FROM records WHERE user_id = ? AND category = '派单' AND DATE(created_at, 'localtime') >= ? AND DATE(created_at, 'localtime') < ? ORDER BY created_at DESC LIMIT ? OFFSET ?`
- ).all(req.user.id, startDate, endDate, Number(limit), offset);
-
- res.json({ records, total, page: Number(page), limit: Number(limit) });
+ const result = queryPagedRecords(req.user.id, req.query.year, req.query.month, {
+ category: '派单'
+ });
+ res.json(result);
});
// 获取记录列表
diff --git a/server/routes/stats.js b/server/routes/stats.js
index 74fe008..da02d33 100644
--- a/server/routes/stats.js
+++ b/server/routes/stats.js
@@ -35,22 +35,28 @@ router.get('/monthly', (req, res) => {
// 月收入/支出记录列表
router.get('/monthly-records', (req, res) => {
- const { type } = req.query;
+ 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);
const summary = db.prepare(`
SELECT COALESCE(SUM(amount), 0) as total
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 total = db.prepare(`
+ SELECT 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).count;
+
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
- `).all(req.user.id, type, startDate, endDate);
+ ORDER BY created_at DESC LIMIT ? OFFSET ?
+ `).all(req.user.id, type, startDate, endDate, Number(limit), offset);
- res.json({ total: summary.total, records });
+ res.json({ total: summary.total, records, page: Number(page), limit: Number(limit), totalCount: total });
});
// 年度净收入按年统计
@@ -236,7 +242,9 @@ router.get('/total', (req, res) => {
// 老板交易记录
router.get('/boss-records', (req, res) => {
const boss = req.query.boss;
+ const { page = 1, limit = 20 } = req.query;
if (!boss) return res.status(400).json({ error: '缺少老板参数' });
+ const offset = (Number(page) - 1) * Number(limit);
// 我的收入:只统计 type='income' 的记录(接单、派单返点、红包收入等)
// 使用 COALESCE(boss, source) 兼容旧数据(之前的红包收入只有 source 字段)
@@ -256,21 +264,30 @@ router.get('/boss-records', (req, res) => {
const boss_total_expense = expenseSummary.dispatch_expense + expenseSummary.order_expense + expenseSummary.redEnvelope_expense;
+ const total = db.prepare(`
+ SELECT COUNT(*) as count FROM records WHERE user_id = ? AND COALESCE(boss, source) = ?
+ `).get(req.user.id, boss).count;
+
const records = db.prepare(`
- SELECT * FROM records WHERE user_id = ? AND COALESCE(boss, source) = ? ORDER BY created_at DESC
- `).all(req.user.id, boss);
+ SELECT * FROM records WHERE user_id = ? AND COALESCE(boss, source) = ? ORDER BY created_at DESC LIMIT ? OFFSET ?
+ `).all(req.user.id, boss, Number(limit), offset);
res.json({
my_income: myIncomeSummary.total,
boss_total_expense: boss_total_expense,
- records
+ records,
+ page: Number(page),
+ limit: Number(limit),
+ totalCount: total
});
});
// 陪玩交易记录
router.get('/partner-records', (req, res) => {
const partner = req.query.partner;
+ const { page = 1, limit = 20 } = req.query;
if (!partner) return res.status(400).json({ error: '缺少陪玩参数' });
+ const offset = (Number(page) - 1) * Number(limit);
const dispatchStats = db.prepare(`
SELECT
@@ -285,20 +302,31 @@ router.get('/partner-records', (req, res) => {
FROM records WHERE user_id = ? AND type = 'income' AND category != '派单' AND category != '接单' AND source = ?
`).get(req.user.id, partner);
+ const total = db.prepare(`
+ SELECT COUNT(*) as count FROM records
+ WHERE user_id = ? AND (
+ (category = '派单' AND partner = ?) OR
+ (type = 'income' AND category != '派单' AND category != '接单' AND source = ?)
+ )
+ `).get(req.user.id, partner, partner).count;
+
const records = db.prepare(`
SELECT * FROM records
WHERE user_id = ? AND (
(category = '派单' AND partner = ?) OR
(type = 'income' AND category != '派单' AND category != '接单' AND source = ?)
- ) ORDER BY created_at DESC
- `).all(req.user.id, partner, partner);
+ ) ORDER BY created_at DESC LIMIT ? OFFSET ?
+ `).all(req.user.id, partner, partner, Number(limit), offset);
res.json({
dispatch_count: dispatchStats.dispatch_count,
spread_income: dispatchStats.spread_income,
rebate_income: dispatchStats.rebate_income,
other_income: otherIncome.total,
- records
+ records,
+ page: Number(page),
+ limit: Number(limit),
+ totalCount: total
});
});
@@ -329,8 +357,9 @@ router.get('/daily-income', (req, res) => {
// 某天收入记录详情
router.get('/daily-detail', (req, res) => {
- const { date } = req.query;
+ 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);
@@ -340,12 +369,16 @@ router.get('/daily-detail', (req, res) => {
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
- `).all(req.user.id, date, endDate);
+ ORDER BY created_at DESC LIMIT ? OFFSET ?
+ `).all(req.user.id, date, endDate, Number(limit), offset);
- res.json({ total_income: summary.total_income, records });
+ res.json({ total_income: summary.total_income, records, page: Number(page), limit: Number(limit), totalCount: total });
});
// 老板收入排行 - 首页预览