Files
jurong_circle_agent_black/routes/agent.js
2025-09-05 16:49:23 +08:00

420 lines
12 KiB
JavaScript
Raw 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');
const dayjs = require('dayjs');
/**
* 获取代理统计数据
* GET /api/agent/stats
*/
router.get('/stats', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
// 获取下级用户统计
const [userStats] = await getDB().execute(`
SELECT
COUNT(*) as total_users,
COUNT(CASE WHEN DATE(created_at) = CURDATE() THEN 1 END) as today_new_users,
COUNT(CASE WHEN updated_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 1 END) as active_users,
0 as total_balance
FROM agent_merchants
WHERE agent_id = ?
`, [agentId]);
// 获取佣金统计
const [commissionStats] = await getDB().execute(`
SELECT
CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2)) as total_commission,
CAST(COALESCE(SUM(CASE WHEN DATE(created_at) = CURDATE() THEN commission_amount ELSE 0 END), 0) AS DECIMAL(10,2)) as today_commission,
CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2)) as paid_commission,
0 as pending_commission
FROM agent_commission_records
WHERE agent_id = ?
`, [agentId]);
// 获取转账统计
const [transferStats] = await getDB().execute(`
SELECT
COUNT(*) as total_transfers,
COUNT(CASE WHEN DATE(t.created_at) = CURDATE() THEN 1 END) as today_transfers,
CAST(COALESCE(SUM(t.amount), 0) AS DECIMAL(10,2)) as total_amount,
CAST(COALESCE(SUM(CASE WHEN DATE(t.created_at) = CURDATE() THEN t.amount ELSE 0 END), 0) AS DECIMAL(10,2)) as today_amount
FROM transfers t
INNER JOIN agent_merchants am ON (t.from_user_id = am.merchant_id OR t.to_user_id = am.merchant_id)
WHERE am.agent_id = ?
`, [agentId]);
const stats = {
users: userStats[0] || {
total_users: 0,
today_new_users: 0,
active_users: 0,
total_balance: '0.00'
},
commissions: commissionStats[0] || {
total_commission: '0.00',
today_commission: '0.00',
paid_commission: '0.00',
pending_commission: '0.00'
},
transfers: transferStats[0] || {
total_transfers: 0,
today_transfers: 0,
total_amount: '0.00',
today_amount: '0.00'
}
};
res.json({
success: true,
data: stats
});
} catch (error) {
logger.error('获取代理统计数据失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '获取统计数据失败'
});
}
});
/**
* 获取用户增长趋势数据
* GET /api/agent/user-growth-trend
*/
router.get('/user-growth-trend', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const { days = 7 } = req.query;
const [trendData] = await getDB().execute(`
SELECT
DATE(created_at) as date,
COUNT(*) as count
FROM agent_merchants
WHERE agent_id = ?
AND created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
GROUP BY DATE(created_at)
ORDER BY date ASC
`, [agentId, parseInt(days)]);
// 填充缺失的日期注册数为0
const result = [];
for (let i = days - 1; i >= 0; i--) {
const date = dayjs().subtract(i, 'day');
const dateStr = date.format('YYYY-MM-DD');
// 修复日期比较将数据库返回的Date对象转换为字符串进行比较
const existingData = trendData.find(item => {
const itemDateStr = dayjs(item.date).format('YYYY-MM-DD');
return itemDateStr === dateStr;
});
result.push({
date: date.format('MM-DD'),
count: existingData ? existingData.count : 0
});
}
res.json({
success: true,
data: result
});
} catch (error) {
logger.error('获取用户增长趋势失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '获取趋势数据失败'
});
}
});
/**
* 获取佣金收入趋势数据
* GET /api/agent/commission-trend
*/
router.get('/commission-trend', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const { days = 7 } = req.query;
const [trendData] = await getDB().execute(`
SELECT
DATE(created_at) as date,
CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2)) as amount
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)]);
// 填充缺失的日期佣金为0
const result = [];
for (let i = days - 1; i >= 0; i--) {
const date = dayjs().subtract(i, 'day');
const dateStr = date.format('YYYY-MM-DD');
const existingData = trendData.find(item => {
const itemDateStr = dayjs(item.date).format('YYYY-MM-DD');
return itemDateStr === dateStr;
});
result.push({
date: date.format('MM-DD'),
amount: existingData ? parseFloat(existingData.amount) : 0
});
}
res.json({
success: true,
data: result
});
} catch (error) {
logger.error('获取佣金趋势失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '获取趋势数据失败'
});
}
});
/**
* 获取用户活跃度数据
* GET /api/agent/user-activity
*/
router.get('/user-activity', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const { days = 7 } = req.query;
// 获取活跃用户趋势(基于转账活动)
const [activityTrend] = await getDB().execute(`
SELECT
DATE(t.created_at) as date,
COUNT(DISTINCT CASE WHEN t.from_user_id = am.merchant_id THEN t.from_user_id END) as active_senders,
COUNT(DISTINCT CASE WHEN t.to_user_id = am.merchant_id THEN t.to_user_id END) as active_receivers,
COUNT(DISTINCT am.merchant_id) as total_active_users
FROM transfers t
INNER JOIN agent_merchants am ON (t.from_user_id = am.merchant_id OR t.to_user_id = am.merchant_id)
WHERE am.agent_id = ?
AND t.created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
AND t.status = 'completed'
GROUP BY DATE(t.created_at)
ORDER BY date ASC
`, [agentId, parseInt(days)]);
// 获取用户活跃度统计
const [activityStats] = await getDB().execute(`
SELECT
COUNT(DISTINCT am.merchant_id) as total_users,
COUNT(DISTINCT CASE WHEN t.created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY) THEN am.merchant_id END) as daily_active_users,
COUNT(DISTINCT CASE WHEN t.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN am.merchant_id END) as weekly_active_users,
COUNT(DISTINCT CASE WHEN t.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN am.merchant_id END) as monthly_active_users,
ROUND(
COUNT(DISTINCT CASE WHEN t.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN am.merchant_id END) * 100.0 /
NULLIF(COUNT(DISTINCT am.merchant_id), 0), 2
) as weekly_activity_rate
FROM agent_merchants am
LEFT JOIN transfers t ON (t.from_user_id = am.merchant_id OR t.to_user_id = am.merchant_id)
AND t.status = 'completed'
WHERE am.agent_id = ?
`, [agentId]);
// 填充缺失的日期
const trendResult = [];
for (let i = days - 1; i >= 0; i--) {
const date = dayjs().subtract(i, 'day');
const dateStr = date.format('YYYY-MM-DD');
const existingData = activityTrend.find(item => {
const itemDateStr = dayjs(item.date).format('YYYY-MM-DD');
return itemDateStr === dateStr;
});
trendResult.push({
date: date.format('MM-DD'),
active_users: existingData ? existingData.total_active_users : 0,
active_senders: existingData ? existingData.active_senders : 0,
active_receivers: existingData ? existingData.active_receivers : 0
});
}
res.json({
success: true,
data: {
trend: trendResult,
stats: activityStats[0] || {
total_users: 0,
daily_active_users: 0,
weekly_active_users: 0,
monthly_active_users: 0,
weekly_activity_rate: 0
}
}
});
} catch (error) {
logger.error('获取用户活跃度失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '获取用户活跃度数据失败'
});
}
});
/**
* 获取佣金类型分布数据
* GET /api/agent/commission-distribution
*/
router.get('/commission-distribution', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const [distributionData] = await getDB().execute(`
SELECT
commission_type as type,
COUNT(*) as count,
CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2)) as amount
FROM agent_commission_records
WHERE agent_id = ?
GROUP BY commission_type
ORDER BY amount DESC
`, [agentId]);
res.json({
success: true,
data: distributionData
});
} catch (error) {
logger.error('获取佣金分布失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '获取分布数据失败'
});
}
});
/**
* 获取最新下级用户
* GET /api/agent/recent-users
*/
router.get('/recent-users', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const { limit = 10 } = req.query;
const limitValue = Math.max(1, Math.min(100, parseInt(limit))); // 限制在1-100之间
const [recentUsers] = await getDB().execute(`
SELECT
u.id,
u.username,
u.real_name,
u.phone,
u.avatar,
u.balance,
u.created_at,
am.created_at as join_date
FROM agent_merchants am
LEFT JOIN users u ON am.merchant_id = u.id
WHERE am.agent_id = ?
ORDER BY am.created_at DESC
LIMIT ${limitValue}
`, [agentId]);
res.json({
success: true,
data: recentUsers
});
} catch (error) {
logger.error('获取最新用户失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '获取最新用户失败'
});
}
});
/**
* 获取最新佣金记录
* GET /api/agent/recent-commissions
*/
router.get('/recent-commissions', agentAuth, async (req, res) => {
try {
const agentId = req.agent.id;
const { limit = 10 } = req.query;
const limitValue = Math.max(1, Math.min(100, parseInt(limit))); // 限制在1-100之间
const [recentCommissions] = await getDB().execute(`
SELECT
acr.id,
acr.commission_type,
acr.commission_amount,
acr.created_at,
u.username,
u.real_name
FROM agent_commission_records acr
LEFT JOIN users u ON acr.merchant_id = u.id
WHERE acr.agent_id = ?
ORDER BY acr.created_at DESC
LIMIT ${limitValue}
`, [agentId]);
res.json({
success: true,
data: recentCommissions
});
} catch (error) {
logger.error('获取最新佣金失败', {
error: error.message,
stack: error.stack,
agentId: req.agent?.id
});
res.status(500).json({
success: false,
message: '获取最新佣金失败'
});
}
});
module.exports = router;