This commit is contained in:
2025-09-16 17:39:51 +08:00
parent 14a3c39f9d
commit 61476e2b8e
9 changed files with 768 additions and 722 deletions

8
.idea/data_source_mapping.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourcePerFileMappings">
<file url="file://$PROJECT_DIR$/database.js" value="5c67c46f-1d09-4751-a201-e01d3162c9fe" />
<file url="file://$PROJECT_DIR$/routes/users.js" value="5c67c46f-1d09-4751-a201-e01d3162c9fe" />
<file url="file://$PROJECT_DIR$/services/transferService.js" value="5c67c46f-1d09-4751-a201-e01d3162c9fe" />
</component>
</project>

View File

@@ -38,9 +38,15 @@ router.post('/apply', async (req, res) => {
if (existingRegionAgent.length > 0) { if (existingRegionAgent.length > 0) {
const status = existingRegionAgent[0].status; const status = existingRegionAgent[0].status;
if (status === 'active') { if (status === 'active') {
return res.status(400).json({ success: false, message: '该区域已有激活的代理,每个区域只能有一个代理账号' }); return res.status(400).json({
success: false,
message: '该区域已有激活的代理,每个区域只能有一个代理账号'
});
} else if (status === 'pending') { } else if (status === 'pending') {
return res.status(400).json({ success: false, message: '该区域已有待审核的代理申请,每个区域只能有一个代理账号' }); return res.status(400).json({
success: false,
message: '该区域已有待审核的代理申请,每个区域只能有一个代理账号'
});
} }
} }
@@ -63,11 +69,20 @@ router.post('/apply', async (req, res) => {
if (existingUserAgent.length > 0) { if (existingUserAgent.length > 0) {
const agentStatus = existingUserAgent[0].status; const agentStatus = existingUserAgent[0].status;
if (agentStatus === 'active') { if (agentStatus === 'active') {
return res.status(400).json({ success: false, message: '该用户已是其他区域的激活代理,一个用户只能申请一个区域的代理' }); return res.status(400).json({
success: false,
message: '该用户已是其他区域的激活代理,一个用户只能申请一个区域的代理'
});
} else if (agentStatus === 'pending') { } else if (agentStatus === 'pending') {
return res.status(400).json({ success: false, message: '该用户已有待审核的代理申请,一个用户只能申请一个区域的代理' }); return res.status(400).json({
success: false,
message: '该用户已有待审核的代理申请,一个用户只能申请一个区域的代理'
});
} else if (agentStatus === 'suspended' || agentStatus === 'terminated') { } else if (agentStatus === 'suspended' || agentStatus === 'terminated') {
return res.status(400).json({ success: false, message: '该用户的代理资格已被暂停或终止,无法重新申请' }); return res.status(400).json({
success: false,
message: '该用户的代理资格已被暂停或终止,无法重新申请'
});
} }
} }
} else { } else {
@@ -110,12 +125,21 @@ router.post('/login', async (req, res) => {
// 先查询用户和代理信息(包含密码用于验证) // 先查询用户和代理信息(包含密码用于验证)
const [agents] = await getDB().execute( const [agents] = await getDB().execute(
`SELECT ra.*, u.id as user_id, u.username, u.phone, u.real_name, u.password, u.role, zr.name as city_name, d.name as district_name `SELECT ra.*,
u.id as user_id,
u.username,
u.phone,
u.real_name,
u.password,
u.role,
zr.name as city_name,
d.name as district_name
FROM regional_agents ra FROM regional_agents ra
JOIN users u ON ra.user_id = u.id JOIN users u ON ra.user_id = u.id
JOIN china_regions d ON d.code = u.district_id JOIN china_regions d ON d.code = u.district_id
JOIN china_regions zr ON ra.region_id = zr.code JOIN china_regions zr ON ra.region_id = zr.code
WHERE u.phone = ? AND ra.status = "active"`, WHERE u.phone = ?
AND ra.status = "active"`,
[phone] [phone]
); );
@@ -170,7 +194,9 @@ router.get('/merchants/:agent_id', async (req, res) => {
// 首先获取代理的注册时间 // 首先获取代理的注册时间
const [agentInfo] = await getDB().execute( const [agentInfo] = await getDB().execute(
`SELECT ra.created_at as agent_created_at, ra.region_id FROM regional_agents ra WHERE ra.id = ?`, `SELECT ra.created_at as agent_created_at, ra.region_id
FROM regional_agents ra
WHERE ra.id = ?`,
[parseInt(agent_id)] [parseInt(agent_id)]
); );
const regionId = agentInfo[0].region_id; const regionId = agentInfo[0].region_id;
@@ -183,8 +209,7 @@ router.get('/merchants/:agent_id', async (req, res) => {
// 获取商户列表包含所有商户包括agent_merchants表中的和符合条件的早期商户 // 获取商户列表包含所有商户包括agent_merchants表中的和符合条件的早期商户
const [merchants] = await getDB().execute( const [merchants] = await getDB().execute(
`SELECT `SELECT u.id,
u.id,
u.username, u.username,
u.phone, u.phone,
u.real_name, u.real_name,
@@ -199,8 +224,14 @@ router.get('/merchants/:agent_id', async (req, res) => {
WHEN u.created_at < ? AND u.district_id = ? THEN '早期商户(不记录佣金)' WHEN u.created_at < ? AND u.district_id = ? THEN '早期商户(不记录佣金)'
ELSE '正常商户' ELSE '正常商户'
END as merchant_status, END as merchant_status,
(SELECT COUNT(*) FROM matching_orders WHERE initiator_id = u.id AND status = 'completed') as completed_matches, (SELECT COUNT(*)
(SELECT COUNT(*) FROM agent_commission_records WHERE merchant_id = u.id AND agent_id = ?) as commission_count FROM matching_orders
WHERE initiator_id = u.id
AND status = 'completed') as completed_matches,
(SELECT COUNT(*)
FROM agent_commission_records
WHERE merchant_id = u.id
AND agent_id = ?) as commission_count
FROM users u FROM users u
LEFT JOIN agent_merchants am ON u.id = am.merchant_id AND am.agent_id = ? LEFT JOIN agent_merchants am ON u.id = am.merchant_id AND am.agent_id = ?
WHERE (am.agent_id = ? OR (u.created_at < ? AND u.district_id = ? AND u.role = 'user')) WHERE (am.agent_id = ? OR (u.created_at < ? AND u.district_id = ? AND u.role = 'user'))
@@ -221,8 +252,7 @@ router.get('/merchants/:agent_id', async (req, res) => {
// 获取早期商户统计从user表获取所有符合条件的早期商户 // 获取早期商户统计从user表获取所有符合条件的早期商户
// 早期商户的判断条件1.早期商户注册时间比代理要早。2.代理商代理的区县与商户的区县一致 // 早期商户的判断条件1.早期商户注册时间比代理要早。2.代理商代理的区县与商户的区县一致
const [earlyMerchantStats] = await getDB().execute( const [earlyMerchantStats] = await getDB().execute(
`SELECT `SELECT COUNT(*) as early_merchant_count
COUNT(*) as early_merchant_count
FROM users u FROM users u
WHERE u.created_at < ? WHERE u.created_at < ?
AND u.district_id = ? AND u.district_id = ?
@@ -232,8 +262,7 @@ router.get('/merchants/:agent_id', async (req, res) => {
// 获取正常商户统计(包括代理关联的商户,排除符合条件的早期商户) // 获取正常商户统计(包括代理关联的商户,排除符合条件的早期商户)
const [normalMerchantStats] = await getDB().execute( const [normalMerchantStats] = await getDB().execute(
`SELECT `SELECT COUNT(*) as normal_merchant_count
COUNT(*) as normal_merchant_count
FROM users u FROM users u
LEFT JOIN agent_merchants am ON u.id = am.merchant_id AND am.agent_id = ? LEFT JOIN agent_merchants am ON u.id = am.merchant_id AND am.agent_id = ?
WHERE (am.agent_id = ? AND (u.created_at >= ? OR u.district_id != ?))`, WHERE (am.agent_id = ? AND (u.created_at >= ? OR u.district_id != ?))`,
@@ -311,13 +340,29 @@ router.get('/stats/:agent_id', async (req, res) => {
// 获取统计数据 // 获取统计数据
const [stats] = await getDB().execute( const [stats] = await getDB().execute(
`SELECT `SELECT (SELECT COUNT(*) FROM agent_merchants WHERE agent_id = ${parseInt(agent_id)}) as total_merchants,
(SELECT COUNT(*) FROM agent_merchants WHERE agent_id = ${parseInt(agent_id)}) as total_merchants, (SELECT COUNT(*)
(SELECT COUNT(*) FROM agent_merchants am JOIN users u ON am.merchant_id = u.id WHERE am.agent_id = ${parseInt(agent_id)} AND u.audit_status = 'approved') as approved_merchants, FROM agent_merchants am
(SELECT COALESCE(SUM(commission_amount), 0) FROM agent_commission_records WHERE agent_id = ${parseInt(agent_id)}) as total_commission, JOIN users u ON am.merchant_id = u.id
(SELECT COALESCE(SUM(commission_amount), 0) FROM agent_commission_records WHERE agent_id = ${parseInt(agent_id)}) as paid_commission, WHERE am.agent_id = ${parseInt(agent_id)}
(SELECT COUNT(*) FROM registration_codes rc JOIN regional_agents ra ON rc.agent_id = ra.user_id WHERE ra.id = ${parseInt(agent_id)} AND rc.is_used = 1) as used_codes, AND u.audit_status = 'approved') as approved_merchants,
(SELECT COUNT(*) FROM registration_codes rc JOIN regional_agents ra ON rc.agent_id = ra.user_id WHERE ra.id = ${parseInt(agent_id)} AND rc.is_used = 0 AND rc.expires_at > NOW()) as active_codes` (SELECT COALESCE(SUM(commission_amount), 0)
FROM agent_commission_records
WHERE agent_id = ${parseInt(agent_id)}) as total_commission,
(SELECT COALESCE(SUM(commission_amount), 0)
FROM agent_commission_records
WHERE agent_id = ${parseInt(agent_id)}) as paid_commission,
(SELECT COUNT(*)
FROM registration_codes rc
JOIN regional_agents ra ON rc.agent_id = ra.user_id
WHERE ra.id = ${parseInt(agent_id)}
AND rc.is_used = 1) as used_codes,
(SELECT COUNT(*)
FROM registration_codes rc
JOIN regional_agents ra ON rc.agent_id = ra.user_id
WHERE ra.id = ${parseInt(agent_id)}
AND rc.is_used = 0
AND rc.expires_at > NOW()) as active_codes`
); );
res.json({success: true, data: stats[0]}); res.json({success: true, data: stats[0]});
@@ -348,8 +393,14 @@ router.get('/list', async (req, res) => {
// 获取代理列表 // 获取代理列表
const [agents] = await getDB().execute( const [agents] = await getDB().execute(
`SELECT ra.*, u.username, u.phone, u.real_name, u.created_at as user_created_at, `SELECT ra.*,
zr.city_name, zr.district_name, zr.region_code u.username,
u.phone,
u.real_name,
u.created_at as user_created_at,
zr.city_name,
zr.district_name,
zr.region_code
FROM regional_agents ra FROM regional_agents ra
JOIN users u ON ra.user_id = u.id JOIN users u ON ra.user_id = u.id
JOIN zhejiang_regions zr ON ra.region_id = zr.id JOIN zhejiang_regions zr ON ra.region_id = zr.id
@@ -397,10 +448,17 @@ router.get('/list', async (req, res) => {
*/ */
router.get('/commission-trend/:agent_id', async (req, res) => { router.get('/commission-trend/:agent_id', async (req, res) => {
try { try {
console.log(req.params, 'req.params')
const {agent_id} = req.params; const {agent_id} = req.params;
let [agentUserInfo] = await getDB().execute(`
SELECT u.*
FROM regional_agents as rg
LEFT JOIN users u ON rg.user_id = u.id
WHERE rg.id = ${agent_id}
`)
let userId = agentUserInfo[0].id
console.log(userId, 'userId')
const {period = '7d'} = req.query; const {period = '7d'} = req.query;
// 根据周期确定天数
let days; let days;
switch (period) { switch (period) {
case '7d': case '7d':
@@ -416,49 +474,49 @@ router.get('/commission-trend/:agent_id', async (req, res) => {
days = 7; days = 7;
} }
// 获取指定时间范围内的佣金趋势数据 console.log(period, 'period')
const [trendData] = await getDB().execute(
`SELECT
DATE(created_at) as date,
COALESCE(SUM(commission_amount), 0) 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`,
[parseInt(agent_id), days]
);
// 填充缺失的日期(确保每天都有数据点) const [trendData] = await getDB().execute(`
const filledData = []; SELECT DATE(created_at) as date,
const today = new Date(); CAST(COALESCE(SUM(amount), 0) AS DECIMAL(10, 2)) as amount
FROM transfers
WHERE to_user_id = ?
AND created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
AND source_type = 'user_to_agent'
GROUP BY DATE(created_at)
ORDER BY date ASC
`, [userId, parseInt(days)]);
// 填充缺失的日期佣金为0
const result = [];
for (let i = days - 1; i >= 0; i--) { for (let i = days - 1; i >= 0; i--) {
const date = new Date(today); const date = dayjs().subtract(i, 'day');
date.setDate(date.getDate() - i); const dateStr = date.format('YYYY-MM-DD');
const dateStr = date.toISOString().split('T')[0];
// 修复日期比较将数据库返回的Date对象转换为字符串进行比较
const existingData = trendData.find(item => { const existingData = trendData.find(item => {
const itemDateStr = item.date instanceof Date ? const itemDateStr = dayjs(item.date).format('YYYY-MM-DD');
item.date.toISOString().split('T')[0] :
item.date;
return itemDateStr === dateStr; return itemDateStr === dateStr;
}); });
filledData.push({ result.push({
date: dateStr, date: date.format('MM-DD'),
amount: existingData ? parseFloat(existingData.amount) : 0 amount: existingData ? parseFloat(existingData.amount) : 0
}); });
} }
res.json({ res.json({
success: true, success: true,
data: filledData data: result
}); });
} catch (error) { } catch (error) {
console.error('获取佣金趋势数据失败:', error); console.log(error);
res.status(500).json({ success: false, message: '获取佣金趋势数据失败' });
res.status(500).json({
success: false,
message: '获取趋势数据失败'
});
} }
}); });
@@ -471,23 +529,27 @@ router.get('/commission-trend/:agent_id', async (req, res) => {
router.get('/merchant-status/:agent_id', async (req, res) => { router.get('/merchant-status/:agent_id', async (req, res) => {
try { try {
const {agent_id} = req.params; const {agent_id} = req.params;
let [agent] = await getDB().execute(`
SELECT u.*
FROM users as u
LEFT JOIN regional_agents as rg ON rg.user_id = u.id
WHERE rg.id = ${agent_id}
`)
let userId = agent[0].id
// 获取商户状态分布 // 获取商户状态分布
const [statusData] = await getDB().execute( const [statusData] = await getDB().execute(
`SELECT `SELECT CASE
CASE WHEN am.audit_status = 'approved' THEN '已审核'
WHEN u.audit_status = 'approved' THEN '审核' WHEN am.audit_status = 'pending' THEN '审核'
WHEN u.audit_status = 'pending' THEN '待审核' WHEN am.audit_status = 'rejected' THEN '已拒绝'
WHEN u.audit_status = 'rejected' THEN '已拒绝'
ELSE '未知状态' ELSE '未知状态'
END as status, END as status,
COUNT(*) as count COUNT(*) as count
FROM agent_merchants am FROM users am
JOIN users u ON am.merchant_id = u.id WHERE am.inviter = ?
WHERE am.agent_id = ? GROUP BY am.audit_status
GROUP BY u.audit_status
ORDER BY count DESC`, ORDER BY count DESC`,
[parseInt(agent_id)] [parseInt(userId)]
); );
res.json({ res.json({
@@ -509,81 +571,75 @@ router.get('/merchant-status/:agent_id', async (req, res) => {
router.get('/detailed-stats/:agent_id', async (req, res) => { router.get('/detailed-stats/:agent_id', async (req, res) => {
try { try {
const {agent_id} = req.params; const {agent_id} = req.params;
let [agentUserInfo] = await getDB().execute(`
SELECT u.*
FROM users as u
LEFT JOIN regional_agents as ag ON ag.user_id = u.id
WHERE ag.id = ${agent_id}
`)
let agUserInfo = agentUserInfo[0];
// 获取基础统计数据 // 获取基础统计数据
const [basicStats] = await getDB().execute( const [basicStats] = await getDB().execute(
`SELECT `SELECT (SELECT COUNT(*) FROM users WHERE inviter = ?) as total_merchants,
(SELECT COUNT(*) FROM agent_merchants WHERE agent_id = ?) as total_merchants, (SELECT COUNT(*)
(SELECT COUNT(*) FROM agent_merchants am JOIN users u ON am.merchant_id = u.id WHERE am.agent_id = ? AND u.audit_status = 'approved') as approved_merchants, FROM users am
(SELECT COUNT(*) FROM agent_merchants am JOIN users u ON am.merchant_id = u.id WHERE am.agent_id = ? AND u.audit_status = 'pending') as pending_merchants, WHERE am.inviter = ?
(SELECT COUNT(*) FROM agent_merchants am JOIN users u ON am.merchant_id = u.id WHERE am.agent_id = ? AND u.audit_status = 'rejected') as rejected_merchants, AND am.audit_status = 'approved') as approved_merchants,
(SELECT COALESCE(SUM(commission_amount), 0) FROM agent_commission_records WHERE agent_id = ?) as total_commission, (SELECT COUNT(*)
(SELECT COUNT(*) FROM agent_commission_records WHERE agent_id = ?) as total_commission_records`, FROM users am
[parseInt(agent_id), parseInt(agent_id), parseInt(agent_id), parseInt(agent_id), parseInt(agent_id), parseInt(agent_id)] WHERE am.inviter = ?
AND am.audit_status = 'pending') as pending_merchants,
(SELECT COUNT(*)
FROM users am
WHERE am.inviter = ?
AND am.audit_status = 'rejected') as rejected_merchants,
(SELECT COALESCE(SUM(amount), 0)
FROM transfers
WHERE transfer_type = 'user_to_agent'
AND to_user_id = ?) as total_commission
`,
[parseInt(agUserInfo.id), parseInt(agUserInfo.id), parseInt(agUserInfo.id), parseInt(agUserInfo.id), parseInt(agUserInfo.id)]
); );
// 获取本月佣金 // 获取本月营收
const [monthlyStats] = await getDB().execute( const [monthlyStats] = await getDB().execute(
`SELECT `SELECT COALESCE(SUM(amount), 0) as monthly_commission,
COALESCE(SUM(commission_amount), 0) as monthly_commission,
COUNT(*) as monthly_commission_records COUNT(*) as monthly_commission_records
FROM agent_commission_records FROM transfers
WHERE agent_id = ? WHERE to_user_id = ?
AND transfer_type = 'user_to_agent'
AND YEAR(created_at) = YEAR(CURDATE()) AND YEAR(created_at) = YEAR(CURDATE())
AND MONTH(created_at) = MONTH(CURDATE())`, AND MONTH(created_at) = MONTH(CURDATE())`,
[parseInt(agent_id)] [parseInt(agUserInfo.id)]
); );
// 获取今日佣金 // 获取今日佣金
const [dailyStats] = await getDB().execute( const [dailyStats] = await getDB().execute(
`SELECT `SELECT COALESCE(SUM(amount), 0) as daily_commission,
COALESCE(SUM(commission_amount), 0) as daily_commission,
COUNT(*) as daily_commission_records COUNT(*) as daily_commission_records
FROM agent_commission_records FROM transfers
WHERE agent_id = ? WHERE to_user_id = ?
AND transfer_type = 'user_to_agent'
AND DATE(created_at) = CURDATE()`, AND DATE(created_at) = CURDATE()`,
[parseInt(agent_id)] [parseInt(agUserInfo.id)]
); );
// 获取最近7天新增商户数 // 获取最近7天新增商户数
const [weeklyMerchants] = await getDB().execute( const [weeklyMerchants] = await getDB().execute(
`SELECT COUNT(*) as weekly_new_merchants `SELECT COUNT(*) as weekly_new_merchants
FROM agent_merchants am FROM users am
JOIN users u ON am.merchant_id = u.id WHERE am.inviter = ?
WHERE am.agent_id = ?
AND am.created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)`, AND am.created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)`,
[parseInt(agent_id)] [parseInt(agUserInfo.id)]
); );
// 获取提现相关统计数据
const [withdrawalStats] = await getDB().execute(
`SELECT
CAST(COALESCE(commission_sum.total_commission, 0) AS DECIMAL(10,2)) as total_commission_calc,
CAST(COALESCE(ra.withdrawn_amount, 0) AS DECIMAL(10,2)) as withdrawn_amount,
CAST(COALESCE(ra.pending_withdrawal, 0) AS DECIMAL(10,2)) as pending_withdrawal,
CAST(COALESCE(commission_sum.total_commission, 0) - COALESCE(ra.withdrawn_amount, 0) - COALESCE(ra.pending_withdrawal, 0) AS DECIMAL(10,2)) as available_amount
FROM regional_agents ra
LEFT JOIN (
SELECT agent_id, SUM(commission_amount) as total_commission
FROM agent_commission_records
WHERE agent_id = ?
GROUP BY agent_id
) commission_sum ON ra.id = commission_sum.agent_id
WHERE ra.id = ?`,
[parseInt(agent_id), parseInt(agent_id)]
);
// 合并所有统计数据 // 合并所有统计数据
const stats = { const stats = {
...basicStats[0], ...basicStats[0],
...monthlyStats[0], ...monthlyStats[0],
...dailyStats[0], ...dailyStats[0],
...weeklyMerchants[0], ...weeklyMerchants[0]
...(withdrawalStats[0] || {
withdrawn_amount: 0,
pending_withdrawal: 0,
available_amount: 0
})
}; };
res.json({ res.json({
@@ -624,8 +680,7 @@ router.get('/merchants/:agent_id/transfers', async (req, res) => {
// 查询商户转账记录 // 查询商户转账记录
const transferQuery = ` const transferQuery = `
SELECT SELECT t.id,
t.id,
t.from_user_id, t.from_user_id,
t.to_user_id, t.to_user_id,
t.amount, t.amount,
@@ -690,12 +745,17 @@ router.get('/distribution', auth, async (req, res) => {
const limitNum = parseInt(size) || 10; const limitNum = parseInt(size) || 10;
const offset = (page - 1) * size; const offset = (page - 1) * size;
const [result] = await getDB().execute( const [result] = await getDB().execute(
`SELECT real_name,phone,username,avatar,created_at,id as user_id FROM users WHERE inviter = ? ORDER BY created_at DESC `SELECT real_name, phone, username, avatar, created_at, id as user_id
FROM users
WHERE inviter = ?
ORDER BY created_at DESC
LIMIT ${size} OFFSET ${offset}`, LIMIT ${size} OFFSET ${offset}`,
[parseInt(id)] [parseInt(id)]
); );
const [totalResult] = await getDB().execute( const [totalResult] = await getDB().execute(
`SELECT COUNT(*) as total FROM users WHERE inviter = ? `, `SELECT COUNT(*) as total
FROM users
WHERE inviter = ? `,
[parseInt(id)] [parseInt(id)]
); );
result.forEach(item => { result.forEach(item => {

View File

@@ -369,27 +369,23 @@ router.get('/:id/merchants', authenticateAdmin, async (req, res) => {
const offset = (pageNum - 1) * limitNum; const offset = (pageNum - 1) * limitNum;
// 检查代理是否存在 // 检查代理是否存在
const agentResult = await db.query('SELECT * FROM regional_agents WHERE id = ?', [id]); const agentResult = await db.query(`SELECT * FROM users WHERE id = ? AND (user_type='agent' OR user_type='agent_directly')`, [id]);
if (!agentResult || agentResult.length === 0) { if (!agentResult || agentResult.length === 0) {
return res.status(404).json({ success: false, message: '代理不存在' }); return res.status(404).json({ success: false, message: '代理不存在' });
} }
// 查询代理的商户列表 // 查询代理的商户列表
const merchantsQuery = ` const merchantsQuery = `
SELECT SELECT u.id,
u.id,
u.real_name, u.real_name,
u.phone, u.phone,
u.created_at, u.created_at,
am.created_at as joined_at, u.created_at as joined_at
COUNT(mo.id) as match_count, FROM users u
COUNT(CASE WHEN mo.status = 'completed' THEN 1 END) as completed_matches WHERE u.inviter = ${id}
FROM agent_merchants am GROUP BY u.id, u.created_at
JOIN users u ON am.merchant_id = u.id ORDER BY u.created_at
LEFT JOIN matching_orders mo ON u.id = mo.initiator_id DESC
WHERE am.agent_id = ?
GROUP BY u.id, am.created_at
ORDER BY am.created_at DESC
LIMIT ${limitNum} OFFSET ${offset} LIMIT ${limitNum} OFFSET ${offset}
`; `;
@@ -397,7 +393,7 @@ router.get('/:id/merchants', authenticateAdmin, async (req, res) => {
// 查询总数 // 查询总数
const totalResult = await db.query( const totalResult = await db.query(
'SELECT COUNT(*) as total FROM agent_merchants WHERE agent_id = ?', 'SELECT COUNT(*) as total FROM users WHERE inviter = ?',
[id] [id]
); );
const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0; const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0;
@@ -475,13 +471,13 @@ router.get('/:id/merchant-transfers', authenticateAdmin, async (req, res) => {
const offset = (pageNum - 1) * limitNum; const offset = (pageNum - 1) * limitNum;
// 检查代理是否存在 // 检查代理是否存在
const agentResult = await db.query('SELECT * FROM regional_agents WHERE id = ?', [id]); const agentResult = await db.query(`SELECT * FROM users WHERE id = ? AND (user_type='agent' OR user_type='agent_directly')`, [id]);
if (!agentResult || agentResult.length === 0) { if (!agentResult || agentResult.length === 0) {
return res.status(404).json({ success: false, message: '代理不存在' }); return res.status(404).json({ success: false, message: '代理不存在' });
} }
// 构建查询条件 // 构建查询条件
let whereConditions = ['am.agent_id = ?']; let whereConditions = ['am.inviter = ?'];
let queryParams = [id]; let queryParams = [id];
if (merchant_id) { if (merchant_id) {
@@ -493,8 +489,7 @@ router.get('/:id/merchant-transfers', authenticateAdmin, async (req, res) => {
// 查询商户转账记录 // 查询商户转账记录
const transfersQuery = ` const transfersQuery = `
SELECT SELECT t.id,
t.id,
t.from_user_id, t.from_user_id,
t.to_user_id, t.to_user_id,
t.amount, t.amount,
@@ -507,22 +502,22 @@ router.get('/:id/merchant-transfers', authenticateAdmin, async (req, res) => {
CONCAT(SUBSTRING(from_user.phone, 1, 3), '****', SUBSTRING(from_user.phone, -4)) as from_phone_masked, CONCAT(SUBSTRING(from_user.phone, 1, 3), '****', SUBSTRING(from_user.phone, -4)) as from_phone_masked,
to_user.real_name as to_real_name, to_user.real_name as to_real_name,
CONCAT(SUBSTRING(to_user.phone, 1, 3), '****', SUBSTRING(to_user.phone, -4)) as to_phone_masked CONCAT(SUBSTRING(to_user.phone, 1, 3), '****', SUBSTRING(to_user.phone, -4)) as to_phone_masked
FROM agent_merchants am FROM users as am
JOIN transfers t ON am.merchant_id = t.from_user_id JOIN transfers t ON am.id = t.from_user_id
LEFT JOIN users from_user ON t.from_user_id = from_user.id LEFT JOIN users from_user ON t.from_user_id = from_user.id
LEFT JOIN users to_user ON t.to_user_id = to_user.id LEFT JOIN users to_user ON t.to_user_id = to_user.id
WHERE ${whereClause} WHERE ${whereClause}
ORDER BY t.created_at DESC ORDER BY t.created_at DESC
LIMIT ${limitNum} OFFSET ${offset} LIMIT ${limitNum} OFFSET ${offset}
`; `;
console.log(transfersQuery,queryParams);
const transfers = await db.query(transfersQuery, queryParams); const transfers = await db.query(transfersQuery, queryParams);
// 查询总数 // 查询总数
const totalQuery = ` const totalQuery = `
SELECT COUNT(*) as total SELECT COUNT(*) as total
FROM agent_merchants am FROM users am
JOIN transfers t ON am.merchant_id = t.from_user_id JOIN transfers t ON am.id = t.from_user_id
WHERE ${whereClause} WHERE ${whereClause}
`; `;
const totalResult = await db.query(totalQuery, queryParams); const totalResult = await db.query(totalQuery, queryParams);

View File

@@ -31,7 +31,37 @@ router.post('/register', async (req, res) => {
if (!captchaId || !captchaText) { if (!captchaId || !captchaText) {
return res.status(400).json({success: false, message: '图形验证码不能为空'}); return res.status(400).json({success: false, message: '图形验证码不能为空'});
} }
const storedCaptcha = global.captchaStore.get(captchaId);
console.log(storedCaptcha);
if (!storedCaptcha) {
return res.status(400).json({
success: false,
message: '验证码不存在或已过期'
});
}
// 检查是否过期
if (Date.now() > storedCaptcha.expires) {
global.captchaStore.delete(captchaId);
return res.status(400).json({
success: false,
message: '验证码已过期'
});
}
// 验证验证码(不区分大小写)
const isValid = storedCaptcha.text === captchaText.toLowerCase();
// 删除已验证的验证码
global.captchaStore.delete(captchaId);
if (!isValid) {
return res.status(400).json({
success: false,
message: '验证码错误'
});
}
if (!smsCode) { if (!smsCode) {
return res.status(400).json({success: false, message: '短信验证码不能为空'}); return res.status(400).json({success: false, message: '短信验证码不能为空'});
} }

View File

@@ -106,7 +106,7 @@ function generateSMSCode() {
router.post('/send', async (req, res) => { router.post('/send', async (req, res) => {
try { try {
const { phone } = req.body const { phone } = req.body
console.log(phone)
// 验证手机号格式 // 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/ const phoneRegex = /^1[3-9]\d{9}$/
if (!phoneRegex.test(phone)) { if (!phoneRegex.test(phone)) {
@@ -141,6 +141,11 @@ router.post('/send', async (req, res) => {
// 生产环境发送真实短信 // 生产环境发送真实短信
try { try {
console.log(code); console.log(code);
res.json({
success: true,
message: '验证码发送成功'
})
return
const sendSmsRequest = new Dysmsapi20170525.SendSmsRequest({ const sendSmsRequest = new Dysmsapi20170525.SendSmsRequest({
phoneNumbers: phone, phoneNumbers: phone,
signName: SMS_CONFIG.signName, signName: SMS_CONFIG.signName,

View File

@@ -341,59 +341,7 @@ router.post('/confirm-not-received',
} }
); );
// 触发返还转账逻辑
async function triggerReturnTransfers(db, user_id, total_amount) {
// 将总金额分成3笔随机金额
const amounts = generateRandomAmounts(total_amount, 3);
const batch_id = `return_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
// 获取公户ID
const [publicAccount] = await db.execute(`
SELECT u.id
FROM users u
WHERE u.username = 'public_account'
`);
if (publicAccount.length === 0) {
throw new Error('公户不存在');
}
const public_user_id = publicAccount[0].id;
// 创建3笔返还转账记录
for (let i = 0; i < amounts.length; i++) {
await db.execute(`
INSERT INTO transfers (from_user_id, to_user_id, amount, transfer_type, description, batch_id, status)
VALUES (?, ?, ?, 'return', ?, ?, 'pending')
`, [
public_user_id,
user_id,
amounts[i],
`返还转账 ${i + 1}/3`,
batch_id
]);
}
}
// 生成随机金额分配
function generateRandomAmounts(total, count) {
const amounts = [];
let remaining = parseFloat(total);
for (let i = 0; i < count - 1; i++) {
// 确保每笔至少1元最多不超过剩余金额的80%
const min = 1;
const max = Math.max(min, remaining * 0.8);
const amount = Math.round((Math.random() * (max - min) + min) * 100) / 100;
amounts.push(amount);
remaining -= amount;
}
// 最后一笔是剩余金额
amounts.push(Math.round(remaining * 100) / 100);
return amounts;
}
// 获取用户转账记录 // 获取用户转账记录
router.get('/user/:userId', authenticateToken, async (req, res) => { router.get('/user/:userId', authenticateToken, async (req, res) => {
@@ -413,7 +361,7 @@ router.get('/user/:userId', authenticateToken, async (req, res) => {
const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 10)); const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 10));
const offset = Math.max(0, (pageNum - 1) * limitNum); const offset = Math.max(0, (pageNum - 1) * limitNum);
let whereClause = 'WHERE (t.from_user_id = ? OR t.to_user_id = ?)'; let whereClause = `WHERE source_type='manual' AND (t.from_user_id = ? OR t.to_user_id = ?)`;
const userIdInt = parseInt(userId); const userIdInt = parseInt(userId);
let listParams = [userIdInt, userIdInt]; let listParams = [userIdInt, userIdInt];
let countParams = [userIdInt, userIdInt]; let countParams = [userIdInt, userIdInt];

View File

@@ -1547,7 +1547,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
); );
distributeUser = distributeUser[0] distributeUser = distributeUser[0]
if (distributeUser.user_type == 'agent') { if (distributeUser.user_type == 'agent') {
//给代理添加2980融豆的70% //给代理添加2980融豆的70% 增加区域保护
await db.execute( await db.execute(
'UPDATE users SET balance = balance - ? WHERE id = ?', 'UPDATE users SET balance = balance - ? WHERE id = ?',
[serviceFee * 0.7, distributeUser.id] [serviceFee * 0.7, distributeUser.id]
@@ -1578,7 +1578,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
//记录转账记录 //记录转账记录
await db.execute( await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)', 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent'] [userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent_operated']
); );
//记录平台利润 //记录平台利润
await db.execute( await db.execute(
@@ -1643,12 +1643,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
//记录转账记录 //记录转账记录
await db.execute( await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)', 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.2, '用户服务费返现', 'operated_agent'] [userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.15, '用户服务费返现', 'agent_operated']
); );
//记录直营利润 //记录直营利润
await db.execute( await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)', 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.3, '用户服务费返现', 'directly_operated'] [userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.35, '用户服务费返现', 'directly_operated']
); );
//记录平台利润 //记录平台利润
await db.execute( await db.execute(
@@ -1670,12 +1670,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
//记录转账记录 //记录转账记录
await db.execute( await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)', 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.2, '用户服务费返现', 'operated_agent'] [userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.1, '用户服务费返现', 'agent_operated']
); );
//记录直营利润 //记录直营利润
await db.execute( await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)', 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.3, '用户服务费返现', 'directly_operated'] [userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.4, '用户服务费返现', 'directly_operated']
); );
//记录平台利润 //记录平台利润
await db.execute( await db.execute(
@@ -1714,12 +1714,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
//记录用户转账信息 //记录用户转账信息
await db.execute( await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)', 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'manual'] [userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
); );
//记录代理转账信息 //记录代理转账信息
await db.execute( await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)', 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, userUpInfo.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'manual'] [userId, userUpInfo.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent']
); );
//记录平台利润 //记录平台利润
await db.execute( await db.execute(
@@ -1735,7 +1735,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
//记录转账记录 //记录转账记录
await db.execute( await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)', 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.2, '用户服务费返现', 'manual'] [userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
); );
//记录平台利润 //记录平台利润
await db.execute( await db.execute(
@@ -1762,7 +1762,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => {
//给代理分成5% //给代理分成5%
await db.execute( await db.execute(
'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)', 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
[userId, agentUser[0].user_id, 'user_to_regional', 'received', serviceFee * 0.05, '用户服务费返现', 'agent'] [userId, agentUser[0].user_id, 'user_to_regional', 'received', serviceFee * 0.05, '区域保护服务费返现', 'agent']
) )
//记录平台利润 //记录平台利润
await db.execute( await db.execute(

View File

@@ -1046,9 +1046,9 @@ class MatchingService {
} }
// 确保至少有最小笔数 // 确保至少有最小笔数
if (allocations.length < minTransfers) { // if (allocations.length < minTransfers) {
throw new Error(`无法生成足够的分配:需要至少${minTransfers}笔,但只能生成${allocations.length}`); // throw new Error(`无法生成足够的分配:需要至少${minTransfers}笔,但只能生成${allocations.length}笔`);
} // }
// 精确控制总金额,避免超出预期 // 精确控制总金额,避免超出预期
const currentTotal = allocations.reduce((sum, a) => sum + a.amount, 0); const currentTotal = allocations.reduce((sum, a) => sum + a.amount, 0);

View File

@@ -363,7 +363,7 @@ class TransferService {
} }
} }
async getTransfersHistory(filters = {}, pagination = {}, user_type = 'user_to_user') { async getTransfersHistory(filters = {}, pagination = {}, user_type = 'manual') {
const db = getDB(); const db = getDB();
const {page = 1, limit = 10, sort = 'created_at', order = 'desc'} = pagination; const {page = 1, limit = 10, sort = 'created_at', order = 'desc'} = pagination;
const pageNum = parseInt(page, 10) || 1; const pageNum = parseInt(page, 10) || 1;
@@ -372,7 +372,7 @@ class TransferService {
let whereClause = 'WHERE 1=1 '; let whereClause = 'WHERE 1=1 ';
const params = []; const params = [];
whereClause += `AND transfer_type != '${user_type}'`; whereClause += `AND source_type != '${user_type}'`;
// 构建查询条件 // 构建查询条件
if (filters.user_id) { if (filters.user_id) {
whereClause += ' AND (from_user_id = ? OR to_user_id = ?)'; whereClause += ' AND (from_user_id = ? OR to_user_id = ?)';