Files
jurong_circle_agent_black/routes/commissions.js
2025-09-04 10:49:10 +08:00

540 lines
14 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express');
const router = express.Router();
const { getDB } = require('../database');
const { agentAuth } = require('../middleware/agentAuth');
const { logger } = require('../config/logger');
/**
* 获取代理佣金记录列表
* GET /api/commissions
*/
router.get('/', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const {
page = 1,
limit = 20,
search,
status,
commission_type,
start_date,
end_date,
min_amount,
max_amount,
sort_by = 'created_at',
sort_order = 'desc'
} = req.query;
const pageNum = parseInt(page) || 1;
const limitNum = parseInt(limit) || 20;
const offset = (pageNum - 1) * limitNum;
// 构建查询条件
let whereConditions = ['acr.agent_id = ?'];
let queryParams = [agentId];
if (search) {
whereConditions.push('(u.username LIKE ? OR u.real_name LIKE ? OR u.phone LIKE ?)');
queryParams.push(`%${search}%`, `%${search}%`, `%${search}%`);
}
if (status) {
whereConditions.push('acr.status = ?');
queryParams.push(status);
}
if (commission_type) {
whereConditions.push('acr.commission_type = ?');
queryParams.push(commission_type);
}
if (start_date) {
whereConditions.push('DATE(acr.created_at) >= ?');
queryParams.push(start_date);
}
if (end_date) {
whereConditions.push('DATE(acr.created_at) <= ?');
queryParams.push(end_date);
}
if (min_amount) {
whereConditions.push('acr.commission_amount >= ?');
queryParams.push(parseFloat(min_amount));
}
if (max_amount) {
whereConditions.push('acr.commission_amount <= ?');
queryParams.push(parseFloat(max_amount));
}
const whereClause = whereConditions.join(' AND ');
// 验证排序字段
const allowedSortFields = ['created_at', 'commission_amount', 'status', 'commission_type'];
const sortBy = allowedSortFields.includes(sort_by) ? sort_by : 'created_at';
const sortOrder = sort_order.toLowerCase() === 'asc' ? 'ASC' : 'DESC';
// 查询佣金记录列表
const commissionsQuery = `
SELECT
acr.id,
acr.agent_id,
acr.merchant_id,
acr.commission_type,
acr.commission_amount,
acr.status,
acr.description,
acr.reference_id,
acr.created_at,
acr.updated_at,
acr.paid_at,
u.username,
u.real_name,
u.phone,
u.avatar
FROM agent_commission_records acr
LEFT JOIN users u ON acr.merchant_id = u.id
WHERE ${whereClause}
ORDER BY acr.${sortBy} ${sortOrder}
LIMIT ${limitNum} OFFSET ${offset}
`;
const [commissions] = await getDB().execute(commissionsQuery, queryParams);
// 查询总数
const countQuery = `
SELECT COUNT(*) as total
FROM agent_commission_records acr
LEFT JOIN users u ON acr.merchant_id = u.id
WHERE ${whereClause}
`;
const [countResult] = await getDB().execute(countQuery, queryParams);
const total = countResult[0]?.total || 0;
// 查询统计信息
const [statsResult] = await getDB().execute(`
SELECT
COUNT(*) as total_commissions,
COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending_commissions,
COUNT(CASE WHEN status = 'paid' THEN 1 END) as paid_commissions,
CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2)) as total_amount,
CAST(COALESCE(SUM(CASE WHEN status = 'pending' THEN commission_amount ELSE 0 END), 0) AS DECIMAL(10,2)) as pending_amount,
CAST(COALESCE(SUM(CASE WHEN status = 'paid' THEN commission_amount ELSE 0 END), 0) AS DECIMAL(10,2)) as paid_amount,
CAST(COALESCE(SUM(CASE WHEN DATE(created_at) = CURDATE() THEN commission_amount ELSE 0 END), 0) AS DECIMAL(10,2)) as today_amount
FROM agent_commission_records
WHERE agent_id = ?
`, [agentId]);
const stats = statsResult[0] || {
total_commissions: 0,
pending_commissions: 0,
paid_commissions: 0,
total_amount: '0.00',
pending_amount: '0.00',
paid_amount: '0.00',
today_amount: '0.00'
};
res.json({
success: true,
data: {
commissions,
pagination: {
current_page: pageNum,
per_page: limitNum,
total,
total_pages: Math.ceil(total / limitNum)
},
stats
}
});
} catch (error) {
logger.error('获取佣金记录失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '获取佣金记录失败'
});
}
});
/**
* 获取单个佣金记录详情
* GET /api/commissions/:id
*/
router.get('/:id', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const commissionId = req.params.id;
// 查询佣金记录详情
const [commissions] = await getDB().execute(`
SELECT
acr.id,
acr.agent_id,
acr.merchant_id,
acr.commission_type,
acr.commission_amount,
acr.status,
acr.description,
acr.reference_id,
acr.created_at,
acr.updated_at,
acr.paid_at,
u.username,
u.real_name,
u.phone,
u.avatar,
u.city,
u.district
FROM agent_commission_records acr
LEFT JOIN users u ON acr.merchant_id = u.id
WHERE acr.id = ? AND acr.agent_id = ?
`, [commissionId, agentId]);
if (commissions.length === 0) {
return res.status(404).json({
success: false,
message: '佣金记录不存在或无权限查看'
});
}
const commission = commissions[0];
res.json({
success: true,
data: commission
});
} catch (error) {
logger.error('获取佣金记录详情失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id,
commissionId: req.params.id
});
res.status(500).json({
success: false,
message: '获取佣金记录详情失败'
});
}
});
/**
* 申请佣金发放(单个)
* POST /api/commissions/:id/request-payment
*/
router.post('/:id/request-payment', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const commissionId = req.params.id;
// 检查佣金记录是否存在且属于当前代理
const [commissions] = await getDB().execute(`
SELECT id, status, commission_amount
FROM agent_commission_records
WHERE id = ? AND agent_id = ?
`, [commissionId, agentId]);
if (commissions.length === 0) {
return res.status(404).json({
success: false,
message: '佣金记录不存在或无权限操作'
});
}
const commission = commissions[0];
if (commission.status !== 'pending') {
return res.status(400).json({
success: false,
message: '只能申请待发放状态的佣金'
});
}
// 更新佣金状态为申请中
await getDB().execute(`
UPDATE agent_commission_records
SET status = 'requested', updated_at = NOW()
WHERE id = ?
`, [commissionId]);
logger.info('代理申请佣金发放', {
agentId,
commissionId,
amount: commission.commission_amount
});
res.json({
success: true,
message: '佣金发放申请已提交,请等待审核'
});
} catch (error) {
logger.error('申请佣金发放失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id,
commissionId: req.params.id
});
res.status(500).json({
success: false,
message: '申请佣金发放失败'
});
}
});
/**
* 批量申请佣金发放
* POST /api/commissions/batch-request-payment
*/
router.post('/batch-request-payment', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const { commission_ids } = req.body;
if (!commission_ids || !Array.isArray(commission_ids) || commission_ids.length === 0) {
return res.status(400).json({
success: false,
message: '请选择要申请发放的佣金记录'
});
}
// 检查所有佣金记录是否存在且属于当前代理
const placeholders = commission_ids.map(() => '?').join(',');
const [commissions] = await getDB().execute(`
SELECT id, status, commission_amount
FROM agent_commission_records
WHERE id IN (${placeholders}) AND agent_id = ?
`, [...commission_ids, agentId]);
if (commissions.length !== commission_ids.length) {
return res.status(400).json({
success: false,
message: '部分佣金记录不存在或无权限操作'
});
}
// 检查状态
const invalidCommissions = commissions.filter(c => c.status !== 'pending');
if (invalidCommissions.length > 0) {
return res.status(400).json({
success: false,
message: '只能申请待发放状态的佣金'
});
}
// 批量更新状态
await getDB().execute(`
UPDATE agent_commission_records
SET status = 'requested', updated_at = NOW()
WHERE id IN (${placeholders}) AND agent_id = ?
`, [...commission_ids, agentId]);
const totalAmount = commissions.reduce((sum, c) => sum + parseFloat(c.commission_amount), 0);
logger.info('代理批量申请佣金发放', {
agentId,
commissionIds: commission_ids,
count: commission_ids.length,
totalAmount
});
res.json({
success: true,
message: `已提交${commission_ids.length}条佣金发放申请,总金额${totalAmount.toFixed(2)}`
});
} catch (error) {
logger.error('批量申请佣金发放失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '批量申请佣金发放失败'
});
}
});
/**
* 获取佣金趋势数据
* GET /api/commissions/trend
*/
router.get('/trend/data', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const { days = 7, type = 'amount' } = req.query;
let selectField = 'CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2)) as value';
if (type === 'count') {
selectField = 'COUNT(*) as value';
}
const [trendData] = await getDB().execute(`
SELECT
DATE(created_at) as date,
${selectField}
FROM agent_commission_records
WHERE agent_id = ?
AND created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
GROUP BY DATE(created_at)
ORDER BY date ASC
`, [agentId, parseInt(days)]);
res.json({
success: true,
data: trendData
});
} catch (error) {
logger.error('获取佣金趋势失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '获取佣金趋势失败'
});
}
});
/**
* 导出佣金记录
* GET /api/commissions/export
*/
router.get('/export/data', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const {
format = 'json',
search,
status,
commission_type,
start_date,
end_date,
min_amount,
max_amount
} = req.query;
// 构建查询条件
let whereConditions = ['acr.agent_id = ?'];
let queryParams = [agentId];
if (search) {
whereConditions.push('(u.username LIKE ? OR u.real_name LIKE ? OR u.phone LIKE ?)');
queryParams.push(`%${search}%`, `%${search}%`, `%${search}%`);
}
if (status) {
whereConditions.push('acr.status = ?');
queryParams.push(status);
}
if (commission_type) {
whereConditions.push('acr.commission_type = ?');
queryParams.push(commission_type);
}
if (start_date) {
whereConditions.push('DATE(acr.created_at) >= ?');
queryParams.push(start_date);
}
if (end_date) {
whereConditions.push('DATE(acr.created_at) <= ?');
queryParams.push(end_date);
}
if (min_amount) {
whereConditions.push('acr.commission_amount >= ?');
queryParams.push(parseFloat(min_amount));
}
if (max_amount) {
whereConditions.push('acr.commission_amount <= ?');
queryParams.push(parseFloat(max_amount));
}
const whereClause = whereConditions.join(' AND ');
// 查询佣金记录
const [commissions] = await getDB().execute(`
SELECT
acr.id,
acr.commission_type,
acr.commission_amount,
acr.status,
acr.description,
acr.reference_id,
acr.created_at,
acr.paid_at,
u.username,
u.real_name,
u.phone
FROM agent_commission_records acr
LEFT JOIN users u ON acr.merchant_id = u.id
WHERE ${whereClause}
ORDER BY acr.created_at DESC
`, queryParams);
if (format === 'csv') {
// 生成CSV格式
const csvHeader = 'ID,佣金类型,佣金金额,状态,描述,关联ID,用户名,真实姓名,手机号,创建时间,发放时间\n';
const csvData = commissions.map(commission => {
return [
commission.id,
commission.commission_type || '',
commission.commission_amount,
commission.status || '',
(commission.description || '').replace(/,/g, ''), // 替换逗号避免CSV格式问题
commission.reference_id || '',
commission.username || '',
commission.real_name || '',
commission.phone || '',
commission.created_at || '',
commission.paid_at || ''
].join(',');
}).join('\n');
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename="commissions_${Date.now()}.csv"`);
res.send(csvHeader + csvData);
} else {
// 默认JSON格式
res.json({
success: true,
data: commissions,
exported_at: new Date().toISOString(),
total: commissions.length
});
}
} catch (error) {
logger.error('导出佣金记录失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '导出佣金记录失败'
});
}
});
module.exports = router;