diff --git a/.idea/data_source_mapping.xml b/.idea/data_source_mapping.xml new file mode 100644 index 0000000..31f6ee5 --- /dev/null +++ b/.idea/data_source_mapping.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/routes/agents.js b/routes/agents.js index caf5883..7b2a3fd 100644 --- a/routes/agents.js +++ b/routes/agents.js @@ -1,391 +1,442 @@ const express = require('express'); const router = express.Router(); -const { getDB } = require('../database'); +const {getDB} = require('../database'); const QRCode = require('qrcode'); const crypto = require('crypto'); const bcrypt = require('bcryptjs'); -const { auth } = require('../middleware/auth'); +const {auth} = require('../middleware/auth'); const dayjs = require('dayjs'); // 获取浙江省所有区域列表 router.get('/regions', async (req, res) => { - try { - const [regions] = await getDB().execute( - 'SELECT * FROM zhejiang_regions ORDER BY city_name, district_name' - ); - res.json({ success: true, data: regions }); - } catch (error) { - console.error('获取区域列表失败:', error); - res.status(500).json({ success: false, message: '获取区域列表失败' }); - } + try { + const [regions] = await getDB().execute( + 'SELECT * FROM zhejiang_regions ORDER BY city_name, district_name' + ); + res.json({success: true, data: regions}); + } catch (error) { + console.error('获取区域列表失败:', error); + res.status(500).json({success: false, message: '获取区域列表失败'}); + } }); // 申请成为区域代理 router.post('/apply', async (req, res) => { - try { - const { region_id, real_name, phone, id_card, contact_address } = req.body; + try { + const {region_id, real_name, phone, id_card, contact_address} = req.body; - if (!region_id || !real_name || !phone || !id_card) { - return res.status(400).json({ success: false, message: '请填写完整信息' }); - } - - // 检查该区域是否已有代理(包括所有状态,不仅仅是active) - const [existingRegionAgent] = await getDB().execute( - 'SELECT id, status FROM regional_agents WHERE region_id = ? AND status IN ("pending", "active")', - [region_id] - ); - - if (existingRegionAgent.length > 0) { - const status = existingRegionAgent[0].status; - if (status === 'active') { - return res.status(400).json({ success: false, message: '该区域已有激活的代理,每个区域只能有一个代理账号' }); - } else if (status === 'pending') { - return res.status(400).json({ success: false, message: '该区域已有待审核的代理申请,每个区域只能有一个代理账号' }); - } - } - - // 检查手机号是否已存在用户 - const [existingUser] = await getDB().execute( - 'SELECT id FROM users WHERE phone = ?', - [phone] - ); - - let userId; - if (existingUser.length > 0) { - userId = existingUser[0].id; - - // 检查该用户是否已申请过代理(包括所有状态) - const [existingUserAgent] = await getDB().execute( - 'SELECT id, status, region_id FROM regional_agents WHERE user_id = ?', - [userId] - ); - - if (existingUserAgent.length > 0) { - const agentStatus = existingUserAgent[0].status; - if (agentStatus === 'active') { - return res.status(400).json({ success: false, message: '该用户已是其他区域的激活代理,一个用户只能申请一个区域的代理' }); - } else if (agentStatus === 'pending') { - return res.status(400).json({ success: false, message: '该用户已有待审核的代理申请,一个用户只能申请一个区域的代理' }); - } else if (agentStatus === 'suspended' || agentStatus === 'terminated') { - return res.status(400).json({ success: false, message: '该用户的代理资格已被暂停或终止,无法重新申请' }); + if (!region_id || !real_name || !phone || !id_card) { + return res.status(400).json({success: false, message: '请填写完整信息'}); } - } - } else { - // 创建新用户(为代理申请用户生成临时密码) - const bcrypt = require('bcryptjs'); - const tempPassword = Math.random().toString(36).slice(-8); // 生成8位临时密码 - const hashedPassword = await bcrypt.hash(tempPassword, 10); - const [userResult] = await getDB().execute( - 'INSERT INTO users (username, password, phone, real_name, id_card, created_at) VALUES (?, ?, ?, ?, ?, NOW())', - [phone, hashedPassword, phone, real_name, id_card] - ); - userId = userResult.insertId; + // 检查该区域是否已有代理(包括所有状态,不仅仅是active) + const [existingRegionAgent] = await getDB().execute( + 'SELECT id, status FROM regional_agents WHERE region_id = ? AND status IN ("pending", "active")', + [region_id] + ); + + if (existingRegionAgent.length > 0) { + const status = existingRegionAgent[0].status; + if (status === 'active') { + return res.status(400).json({ + success: false, + message: '该区域已有激活的代理,每个区域只能有一个代理账号' + }); + } else if (status === 'pending') { + return res.status(400).json({ + success: false, + message: '该区域已有待审核的代理申请,每个区域只能有一个代理账号' + }); + } + } + + // 检查手机号是否已存在用户 + const [existingUser] = await getDB().execute( + 'SELECT id FROM users WHERE phone = ?', + [phone] + ); + + let userId; + if (existingUser.length > 0) { + userId = existingUser[0].id; + + // 检查该用户是否已申请过代理(包括所有状态) + const [existingUserAgent] = await getDB().execute( + 'SELECT id, status, region_id FROM regional_agents WHERE user_id = ?', + [userId] + ); + + if (existingUserAgent.length > 0) { + const agentStatus = existingUserAgent[0].status; + if (agentStatus === 'active') { + return res.status(400).json({ + success: false, + message: '该用户已是其他区域的激活代理,一个用户只能申请一个区域的代理' + }); + } else if (agentStatus === 'pending') { + return res.status(400).json({ + success: false, + message: '该用户已有待审核的代理申请,一个用户只能申请一个区域的代理' + }); + } else if (agentStatus === 'suspended' || agentStatus === 'terminated') { + return res.status(400).json({ + success: false, + message: '该用户的代理资格已被暂停或终止,无法重新申请' + }); + } + } + } else { + // 创建新用户(为代理申请用户生成临时密码) + const bcrypt = require('bcryptjs'); + const tempPassword = Math.random().toString(36).slice(-8); // 生成8位临时密码 + const hashedPassword = await bcrypt.hash(tempPassword, 10); + + const [userResult] = await getDB().execute( + 'INSERT INTO users (username, password, phone, real_name, id_card, created_at) VALUES (?, ?, ?, ?, ?, NOW())', + [phone, hashedPassword, phone, real_name, id_card] + ); + userId = userResult.insertId; + } + + // 生成代理编码 + const agentCode = 'AG' + Date.now().toString().slice(-8); + + // 创建代理申请 + await getDB().execute( + 'INSERT INTO regional_agents (user_id, region_id, agent_code, status, created_at) VALUES (?, ?, ?, "pending", NOW())', + [userId, region_id, agentCode] + ); + + res.json({success: true, message: '申请提交成功,请等待审核'}); + } catch (error) { + console.error('申请代理失败:', error); + res.status(500).json({success: false, message: '申请失败'}); } - - // 生成代理编码 - const agentCode = 'AG' + Date.now().toString().slice(-8); - - // 创建代理申请 - await getDB().execute( - 'INSERT INTO regional_agents (user_id, region_id, agent_code, status, created_at) VALUES (?, ?, ?, "pending", NOW())', - [userId, region_id, agentCode] - ); - - res.json({ success: true, message: '申请提交成功,请等待审核' }); - } catch (error) { - console.error('申请代理失败:', error); - res.status(500).json({ success: false, message: '申请失败' }); - } }); // 代理登录 router.post('/login', async (req, res) => { - try { - const { phone, password } = req.body; + try { + const {phone, password} = req.body; - if (!phone || !password) { - return res.status(400).json({ success: false, message: '请输入手机号和密码' }); + if (!phone || !password) { + return res.status(400).json({success: false, message: '请输入手机号和密码'}); + } + + // 先查询用户和代理信息(包含密码用于验证) + 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 + FROM regional_agents ra + JOIN users u ON ra.user_id = u.id + JOIN china_regions d ON d.code = u.district_id + JOIN china_regions zr ON ra.region_id = zr.code + WHERE u.phone = ? + AND ra.status = "active"`, + [phone] + ); + + if (agents.length === 0) { + return res.status(401).json({success: false, message: '手机号或密码错误,或账户未激活'}); + } + + const agent = agents[0]; + + // 验证密码 + const isPasswordValid = await bcrypt.compare(password, agent.password); + if (!isPasswordValid) { + return res.status(401).json({success: false, message: '手机号或密码错误,或账户未激活'}); + } + + // 生成JWT token + const jwt = require('jsonwebtoken'); + const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; + const token = jwt.sign( + { + userId: agent.user_id, + username: agent.username || agent.phone, + role: agent.role || 'agent', + agentId: agent.id + }, + JWT_SECRET, + {expiresIn: '24h'} + ); + + delete agent.password; // 不返回密码 + + res.json({ + success: true, + data: { + ...agent, + token + }, + message: '登录成功' + }); + } catch (error) { + console.error('代理登录失败:', error); + res.status(500).json({success: false, message: '登录失败'}); } - - // 先查询用户和代理信息(包含密码用于验证) - 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 - FROM regional_agents ra - JOIN users u ON ra.user_id = u.id - JOIN china_regions d ON d.code = u.district_id - JOIN china_regions zr ON ra.region_id = zr.code - WHERE u.phone = ? AND ra.status = "active"`, - [phone] - ); - - if (agents.length === 0) { - return res.status(401).json({ success: false, message: '手机号或密码错误,或账户未激活' }); - } - - const agent = agents[0]; - - // 验证密码 - const isPasswordValid = await bcrypt.compare(password, agent.password); - if (!isPasswordValid) { - return res.status(401).json({ success: false, message: '手机号或密码错误,或账户未激活' }); - } - - // 生成JWT token - const jwt = require('jsonwebtoken'); - const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; - const token = jwt.sign( - { - userId: agent.user_id, - username: agent.username || agent.phone, - role: agent.role || 'agent', - agentId: agent.id - }, - JWT_SECRET, - { expiresIn: '24h' } - ); - - delete agent.password; // 不返回密码 - - res.json({ - success: true, - data: { - ...agent, - token - }, - message: '登录成功' - }); - } catch (error) { - console.error('代理登录失败:', error); - res.status(500).json({ success: false, message: '登录失败' }); - } }); // 获取代理的商户列表(包含所有商户,标注早期商户状态) router.get('/merchants/:agent_id', async (req, res) => { - try { - const { agent_id } = req.params; - const { page = 1, limit = 10 } = req.query; - const offset = (page - 1) * limit; + try { + const {agent_id} = req.params; + const {page = 1, limit = 10} = req.query; + const offset = (page - 1) * limit; - // 首先获取代理的注册时间 - const [agentInfo] = await getDB().execute( - `SELECT ra.created_at as agent_created_at, ra.region_id FROM regional_agents ra WHERE ra.id = ?`, - [parseInt(agent_id)] - ); - const regionId = agentInfo[0].region_id; + // 首先获取代理的注册时间 + const [agentInfo] = await getDB().execute( + `SELECT ra.created_at as agent_created_at, ra.region_id + FROM regional_agents ra + WHERE ra.id = ?`, + [parseInt(agent_id)] + ); + const regionId = agentInfo[0].region_id; - if (!agentInfo || agentInfo.length === 0) { - return res.status(404).json({ success: false, message: '代理不存在' }); - } - - const agentCreatedAt = agentInfo[0].agent_created_at; - - // 获取商户列表(包含所有商户,包括agent_merchants表中的和符合条件的早期商户) - const [merchants] = await getDB().execute( - `SELECT - u.id, - u.username, - u.phone, - u.real_name, - u.created_at, - u.audit_status, - IFNULL(am.created_at, '未关联') as joined_at, - CASE - WHEN u.created_at < ? AND u.district_id = ? THEN 1 - ELSE 0 - END as is_early_merchant, - CASE - WHEN u.created_at < ? AND u.district_id = ? THEN '早期商户(不记录佣金)' - ELSE '正常商户' - END as merchant_status, - (SELECT 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 - 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')) - ORDER BY u.created_at DESC - LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}`, - [agentCreatedAt, parseInt(regionId), agentCreatedAt, parseInt(regionId), parseInt(agent_id), parseInt(agent_id), parseInt(agent_id), agentCreatedAt, parseInt(regionId)] - ); - - // 获取总数(包括代理关联的商户和符合条件的早期商户) - const [countResult] = await getDB().execute( - `SELECT COUNT(*) as total - FROM users u - 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'))`, - [parseInt(agent_id), parseInt(agent_id), agentCreatedAt, parseInt(regionId)] - ); - - // 获取早期商户统计(从user表获取所有符合条件的早期商户) - // 早期商户的判断条件:1.早期商户注册时间比代理要早。2.代理商代理的区县与商户的区县一致 - const [earlyMerchantStats] = await getDB().execute( - `SELECT - COUNT(*) as early_merchant_count - FROM users u - WHERE u.created_at < ? - AND u.district_id = ? - AND u.role = 'user'`, - [agentCreatedAt, parseInt(regionId)] - ); - - // 获取正常商户统计(包括代理关联的商户,排除符合条件的早期商户) - const [normalMerchantStats] = await getDB().execute( - `SELECT - COUNT(*) as normal_merchant_count - FROM users u - 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 != ?))`, - [parseInt(agent_id), parseInt(agent_id), agentCreatedAt, parseInt(regionId)] - ); - - res.json({ - success: true, - data: { - merchants, - total: parseInt(countResult[0].total), - page: parseInt(page), - limit: parseInt(limit), - stats: { - total_merchants: parseInt(countResult[0].total), - early_merchants: parseInt(earlyMerchantStats[0].early_merchant_count), - normal_merchants: parseInt(normalMerchantStats[0].normal_merchant_count) + if (!agentInfo || agentInfo.length === 0) { + return res.status(404).json({success: false, message: '代理不存在'}); } - } - }); - } catch (error) { - console.error('获取商户列表失败:', error); - res.status(500).json({ success: false, message: '获取商户列表失败' }); - } + + const agentCreatedAt = agentInfo[0].agent_created_at; + + // 获取商户列表(包含所有商户,包括agent_merchants表中的和符合条件的早期商户) + const [merchants] = await getDB().execute( + `SELECT u.id, + u.username, + u.phone, + u.real_name, + u.created_at, + u.audit_status, + IFNULL(am.created_at, '未关联') as joined_at, + CASE + WHEN u.created_at < ? AND u.district_id = ? THEN 1 + ELSE 0 + END as is_early_merchant, + CASE + WHEN u.created_at < ? AND u.district_id = ? THEN '早期商户(不记录佣金)' + ELSE '正常商户' + END as merchant_status, + (SELECT 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 + 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')) + ORDER BY u.created_at DESC + LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}`, + [agentCreatedAt, parseInt(regionId), agentCreatedAt, parseInt(regionId), parseInt(agent_id), parseInt(agent_id), parseInt(agent_id), agentCreatedAt, parseInt(regionId)] + ); + + // 获取总数(包括代理关联的商户和符合条件的早期商户) + const [countResult] = await getDB().execute( + `SELECT COUNT(*) as total + FROM users u + 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'))`, + [parseInt(agent_id), parseInt(agent_id), agentCreatedAt, parseInt(regionId)] + ); + + // 获取早期商户统计(从user表获取所有符合条件的早期商户) + // 早期商户的判断条件:1.早期商户注册时间比代理要早。2.代理商代理的区县与商户的区县一致 + const [earlyMerchantStats] = await getDB().execute( + `SELECT COUNT(*) as early_merchant_count + FROM users u + WHERE u.created_at < ? + AND u.district_id = ? + AND u.role = 'user'`, + [agentCreatedAt, parseInt(regionId)] + ); + + // 获取正常商户统计(包括代理关联的商户,排除符合条件的早期商户) + const [normalMerchantStats] = await getDB().execute( + `SELECT COUNT(*) as normal_merchant_count + FROM users u + 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 != ?))`, + [parseInt(agent_id), parseInt(agent_id), agentCreatedAt, parseInt(regionId)] + ); + + res.json({ + success: true, + data: { + merchants, + total: parseInt(countResult[0].total), + page: parseInt(page), + limit: parseInt(limit), + stats: { + total_merchants: parseInt(countResult[0].total), + early_merchants: parseInt(earlyMerchantStats[0].early_merchant_count), + normal_merchants: parseInt(normalMerchantStats[0].normal_merchant_count) + } + } + }); + } catch (error) { + console.error('获取商户列表失败:', error); + res.status(500).json({success: false, message: '获取商户列表失败'}); + } }); // 获取代理的佣金记录 router.get('/commissions/:agent_id', async (req, res) => { - try { - const { agent_id } = req.params; - const { page = 1, limit = 10 } = req.query; - const offset = (page - 1) * limit; + try { + const {agent_id} = req.params; + const {page = 1, limit = 10} = req.query; + const offset = (page - 1) * limit; - // 获取佣金记录 - const [commissions] = await getDB().execute( - `SELECT acr.*, u.username, u.real_name - FROM agent_commission_records acr - JOIN users u ON acr.merchant_id = u.id - WHERE acr.agent_id = ${parseInt(agent_id)} - ORDER BY acr.created_at DESC - LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}` - ); + // 获取佣金记录 + const [commissions] = await getDB().execute( + `SELECT acr.*, u.username, u.real_name + FROM agent_commission_records acr + JOIN users u ON acr.merchant_id = u.id + WHERE acr.agent_id = ${parseInt(agent_id)} + ORDER BY acr.created_at DESC + LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}` + ); - // 获取总数和总佣金 - const [summary] = await getDB().execute( - `SELECT COUNT(*) as total_records, - COALESCE(SUM(commission_amount), 0) as total_commission - FROM agent_commission_records - WHERE agent_id = ${parseInt(agent_id)}` - ); + // 获取总数和总佣金 + const [summary] = await getDB().execute( + `SELECT COUNT(*) as total_records, + COALESCE(SUM(commission_amount), 0) as total_commission + FROM agent_commission_records + WHERE agent_id = ${parseInt(agent_id)}` + ); - // 由于agent_commission_records表没有status字段,设置默认值 - summary[0].paid_commission = summary[0].total_commission; - summary[0].pending_commission = 0; + // 由于agent_commission_records表没有status字段,设置默认值 + summary[0].paid_commission = summary[0].total_commission; + summary[0].pending_commission = 0; - res.json({ - success: true, - data: { - commissions, - summary: summary[0], - page: parseInt(page), - limit: parseInt(limit) - } - }); - } catch (error) { - console.error('获取佣金记录失败:', error); - res.status(500).json({ success: false, message: '获取佣金记录失败' }); - } + res.json({ + success: true, + data: { + commissions, + summary: summary[0], + page: parseInt(page), + limit: parseInt(limit) + } + }); + } catch (error) { + console.error('获取佣金记录失败:', error); + res.status(500).json({success: false, message: '获取佣金记录失败'}); + } }); // 获取代理统计信息 router.get('/stats/:agent_id', async (req, res) => { - try { - const { agent_id } = req.params; + try { + const {agent_id} = req.params; - // 获取统计数据 - const [stats] = await getDB().execute( - `SELECT - (SELECT COUNT(*) FROM agent_merchants WHERE agent_id = ${parseInt(agent_id)}) as total_merchants, - (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, - (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` - ); + // 获取统计数据 + const [stats] = await getDB().execute( + `SELECT (SELECT COUNT(*) FROM agent_merchants WHERE agent_id = ${parseInt(agent_id)}) as total_merchants, + (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, + (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] }); - } catch (error) { - console.error('获取统计信息失败:', error); - res.status(500).json({ success: false, message: '获取统计信息失败' }); - } + res.json({success: true, data: stats[0]}); + } catch (error) { + console.error('获取统计信息失败:', error); + res.status(500).json({success: false, message: '获取统计信息失败'}); + } }); // 获取代理列表 router.get('/list', async (req, res) => { - try { - const { page = 1, limit = 10, status, region_id } = req.query; - const offset = (page - 1) * limit; + try { + const {page = 1, limit = 10, status, region_id} = req.query; + const offset = (page - 1) * limit; - let whereClause = '1=1'; - let params = []; + let whereClause = '1=1'; + let params = []; - if (status) { - whereClause += ' AND ra.status = ?'; - params.push(status); - } - - if (region_id) { - whereClause += ' AND ra.region_id = ?'; - params.push(region_id); - } - - // 获取代理列表 - const [agents] = await getDB().execute( - `SELECT ra.*, 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 - JOIN users u ON ra.user_id = u.id - JOIN zhejiang_regions zr ON ra.region_id = zr.id - WHERE ${whereClause} - ORDER BY ra.created_at DESC - LIMIT ${limit} OFFSET ${offset}` - ); - - // 获取总数 - const [countResult] = await getDB().execute( - `SELECT COUNT(*) as total - FROM regional_agents ra - JOIN users u ON ra.user_id = u.id - JOIN zhejiang_regions zr ON ra.region_id = zr.id - WHERE ${whereClause}` - ); - - const total = countResult[0].total; - const totalPages = Math.ceil(total / limit); - - res.json({ - success: true, - data: { - agents, - pagination: { - page: parseInt(page), - limit: parseInt(limit), - total, - totalPages + if (status) { + whereClause += ' AND ra.status = ?'; + params.push(status); } - } - }); - } catch (error) { - console.error('获取代理列表失败:', error); - res.status(500).json({ success: false, message: '获取代理列表失败' }); - } + + if (region_id) { + whereClause += ' AND ra.region_id = ?'; + params.push(region_id); + } + + // 获取代理列表 + const [agents] = await getDB().execute( + `SELECT ra.*, + 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 + JOIN users u ON ra.user_id = u.id + JOIN zhejiang_regions zr ON ra.region_id = zr.id + WHERE ${whereClause} + ORDER BY ra.created_at DESC + LIMIT ${limit} OFFSET ${offset}` + ); + + // 获取总数 + const [countResult] = await getDB().execute( + `SELECT COUNT(*) as total + FROM regional_agents ra + JOIN users u ON ra.user_id = u.id + JOIN zhejiang_regions zr ON ra.region_id = zr.id + WHERE ${whereClause}` + ); + + const total = countResult[0].total; + const totalPages = Math.ceil(total / limit); + + res.json({ + success: true, + data: { + agents, + pagination: { + page: parseInt(page), + limit: parseInt(limit), + total, + totalPages + } + } + }); + } catch (error) { + console.error('获取代理列表失败:', error); + res.status(500).json({success: false, message: '获取代理列表失败'}); + } }); /** @@ -396,70 +447,77 @@ router.get('/list', async (req, res) => { * @returns {Object} 佣金趋势数据 */ router.get('/commission-trend/:agent_id', async (req, res) => { - try { - const { agent_id } = req.params; - const { period = '7d' } = req.query; + try { + console.log(req.params, '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; + let days; + switch (period) { + case '7d': + days = 7; + break; + case '30d': + days = 30; + break; + case '3m': + days = 90; + break; + default: + days = 7; + } - // 根据周期确定天数 - let days; - switch (period) { - case '7d': - days = 7; - break; - case '30d': - days = 30; - break; - case '3m': - days = 90; - break; - default: - days = 7; + console.log(period, 'period') + + const [trendData] = await getDB().execute(` + SELECT DATE(created_at) as 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--) { + 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) { + console.log(error); + + res.status(500).json({ + success: false, + message: '获取趋势数据失败' + }); } - - // 获取指定时间范围内的佣金趋势数据 - 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 filledData = []; - const today = new Date(); - - for (let i = days - 1; i >= 0; i--) { - const date = new Date(today); - date.setDate(date.getDate() - i); - const dateStr = date.toISOString().split('T')[0]; - - // 修复日期比较:将数据库返回的Date对象转换为字符串进行比较 - const existingData = trendData.find(item => { - const itemDateStr = item.date instanceof Date ? - item.date.toISOString().split('T')[0] : - item.date; - return itemDateStr === dateStr; - }); - - filledData.push({ - date: dateStr, - amount: existingData ? parseFloat(existingData.amount) : 0 - }); - } - - res.json({ - success: true, - data: filledData - }); - } catch (error) { - console.error('获取佣金趋势数据失败:', error); - res.status(500).json({ success: false, message: '获取佣金趋势数据失败' }); - } }); /** @@ -469,35 +527,39 @@ router.get('/commission-trend/:agent_id', async (req, res) => { * @returns {Object} 商户状态分布数据 */ router.get('/merchant-status/:agent_id', async (req, res) => { - try { - const { agent_id } = req.params; + try { + 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( + `SELECT CASE + WHEN am.audit_status = 'approved' THEN '已审核' + WHEN am.audit_status = 'pending' THEN '待审核' + WHEN am.audit_status = 'rejected' THEN '已拒绝' + ELSE '未知状态' + END as status, + COUNT(*) as count + FROM users am + WHERE am.inviter = ? + GROUP BY am.audit_status + ORDER BY count DESC`, + [parseInt(userId)] + ); - // 获取商户状态分布 - const [statusData] = await getDB().execute( - `SELECT - CASE - WHEN u.audit_status = 'approved' THEN '已审核' - WHEN u.audit_status = 'pending' THEN '待审核' - WHEN u.audit_status = 'rejected' THEN '已拒绝' - ELSE '未知状态' - END as status, - COUNT(*) as count - FROM agent_merchants am - JOIN users u ON am.merchant_id = u.id - WHERE am.agent_id = ? - GROUP BY u.audit_status - ORDER BY count DESC`, - [parseInt(agent_id)] - ); - - res.json({ - success: true, - data: statusData - }); - } catch (error) { - console.error('获取商户状态分布数据失败:', error); - res.status(500).json({ success: false, message: '获取商户状态分布数据失败' }); - } + res.json({ + success: true, + data: statusData + }); + } catch (error) { + console.error('获取商户状态分布数据失败:', error); + res.status(500).json({success: false, message: '获取商户状态分布数据失败'}); + } }); /** @@ -507,93 +569,87 @@ router.get('/merchant-status/:agent_id', async (req, res) => { * @returns {Object} 详细统计数据 */ router.get('/detailed-stats/:agent_id', async (req, res) => { - try { - const { agent_id } = req.params; + try { + 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( + `SELECT (SELECT COUNT(*) FROM users WHERE inviter = ?) as total_merchants, + (SELECT COUNT(*) + FROM users am + WHERE am.inviter = ? + AND am.audit_status = 'approved') as approved_merchants, + (SELECT COUNT(*) + FROM users am + 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 [basicStats] = await getDB().execute( - `SELECT - (SELECT COUNT(*) FROM agent_merchants WHERE agent_id = ?) as total_merchants, - (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, - (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, - (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, - (SELECT COALESCE(SUM(commission_amount), 0) FROM agent_commission_records WHERE agent_id = ?) as total_commission, - (SELECT COUNT(*) FROM agent_commission_records WHERE agent_id = ?) as total_commission_records`, - [parseInt(agent_id), parseInt(agent_id), parseInt(agent_id), parseInt(agent_id), parseInt(agent_id), parseInt(agent_id)] - ); + // 获取本月营收 + const [monthlyStats] = await getDB().execute( + `SELECT COALESCE(SUM(amount), 0) as monthly_commission, + COUNT(*) as monthly_commission_records + FROM transfers + WHERE to_user_id = ? + AND transfer_type = 'user_to_agent' + AND YEAR(created_at) = YEAR(CURDATE()) + AND MONTH(created_at) = MONTH(CURDATE())`, + [parseInt(agUserInfo.id)] + ); - // 获取本月佣金 - const [monthlyStats] = await getDB().execute( - `SELECT - COALESCE(SUM(commission_amount), 0) as monthly_commission, - COUNT(*) as monthly_commission_records - FROM agent_commission_records - WHERE agent_id = ? - AND YEAR(created_at) = YEAR(CURDATE()) - AND MONTH(created_at) = MONTH(CURDATE())`, - [parseInt(agent_id)] - ); + // 获取今日佣金 + const [dailyStats] = await getDB().execute( + `SELECT COALESCE(SUM(amount), 0) as daily_commission, + COUNT(*) as daily_commission_records + FROM transfers + WHERE to_user_id = ? + AND transfer_type = 'user_to_agent' + AND DATE(created_at) = CURDATE()`, + [parseInt(agUserInfo.id)] + ); - // 获取今日佣金 - const [dailyStats] = await getDB().execute( - `SELECT - COALESCE(SUM(commission_amount), 0) as daily_commission, - COUNT(*) as daily_commission_records - FROM agent_commission_records - WHERE agent_id = ? - AND DATE(created_at) = CURDATE()`, - [parseInt(agent_id)] - ); + // 获取最近7天新增商户数 + const [weeklyMerchants] = await getDB().execute( + `SELECT COUNT(*) as weekly_new_merchants + FROM users am + WHERE am.inviter = ? + AND am.created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)`, + [parseInt(agUserInfo.id)] + ); - // 获取最近7天新增商户数 - const [weeklyMerchants] = await getDB().execute( - `SELECT COUNT(*) as weekly_new_merchants - FROM agent_merchants am - JOIN users u ON am.merchant_id = u.id - WHERE am.agent_id = ? - AND am.created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)`, - [parseInt(agent_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 = { + ...basicStats[0], + ...monthlyStats[0], + ...dailyStats[0], + ...weeklyMerchants[0] + }; - // 合并所有统计数据 - const stats = { - ...basicStats[0], - ...monthlyStats[0], - ...dailyStats[0], - ...weeklyMerchants[0], - ...(withdrawalStats[0] || { - withdrawn_amount: 0, - pending_withdrawal: 0, - available_amount: 0 - }) - }; - - res.json({ - success: true, - data: stats - }); - } catch (error) { - console.error('获取详细统计数据失败:', error); - res.status(500).json({ success: false, message: '获取详细统计数据失败' }); - } + res.json({ + success: true, + data: stats + }); + } catch (error) { + console.error('获取详细统计数据失败:', error); + res.status(500).json({success: false, message: '获取详细统计数据失败'}); + } }); /** @@ -605,76 +661,75 @@ router.get('/detailed-stats/:agent_id', async (req, res) => { * @returns {Object} 转账记录列表 */ router.get('/merchants/:agent_id/transfers', async (req, res) => { - try { - const { agent_id } = req.params; - const { page = 1, limit = 10 } = req.query; - const pageNum = parseInt(page) || 1; - const limitNum = parseInt(limit) || 10; - const offset = (pageNum - 1) * limitNum; + try { + const {agent_id} = req.params; + const {page = 1, limit = 10} = req.query; + const pageNum = parseInt(page) || 1; + const limitNum = parseInt(limit) || 10; + const offset = (pageNum - 1) * limitNum; - // 检查代理是否存在 - const [agentResult] = await getDB().execute( - 'SELECT * FROM regional_agents WHERE id = ?', - [parseInt(agent_id)] - ); + // 检查代理是否存在 + const [agentResult] = await getDB().execute( + 'SELECT * FROM regional_agents WHERE id = ?', + [parseInt(agent_id)] + ); - if (agentResult.length === 0) { - return res.status(404).json({ success: false, message: '代理不存在' }); - } - - // 查询商户转账记录 - const transferQuery = ` - SELECT - t.id, - t.from_user_id, - t.to_user_id, - t.amount, - t.status, - t.transfer_type, - t.description, - t.created_at, - t.confirmed_at, - from_user.real_name as from_real_name, - from_user.phone as from_phone, - to_user.real_name as to_real_name, - to_user.phone as to_phone - FROM agent_merchants am - JOIN transfers t ON am.merchant_id = t.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 - WHERE am.agent_id = ? - ORDER BY t.created_at DESC - LIMIT ${limitNum} OFFSET ${offset} - `; - const [transfers] = await getDB().execute(transferQuery, [parseInt(agent_id)]); - - // 查询总数 - const [totalResult] = await getDB().execute( - `SELECT COUNT(*) as total - FROM agent_merchants am - JOIN transfers t ON am.merchant_id = t.from_user_id - WHERE am.agent_id = ?`, - [parseInt(agent_id)] - ); - - const total = totalResult[0].total; - - res.json({ - success: true, - data: { - transfers, - pagination: { - page: pageNum, - limit: limitNum, - total, - pages: Math.ceil(total / limitNum) + if (agentResult.length === 0) { + return res.status(404).json({success: false, message: '代理不存在'}); } - } - }); - } catch (error) { - console.error('获取代理商户转账记录失败:', error); - res.status(500).json({ success: false, message: '获取代理商户转账记录失败,请稍后再试' }); - } + + // 查询商户转账记录 + const transferQuery = ` + SELECT t.id, + t.from_user_id, + t.to_user_id, + t.amount, + t.status, + t.transfer_type, + t.description, + t.created_at, + t.confirmed_at, + from_user.real_name as from_real_name, + from_user.phone as from_phone, + to_user.real_name as to_real_name, + to_user.phone as to_phone + FROM agent_merchants am + JOIN transfers t ON am.merchant_id = t.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 + WHERE am.agent_id = ? + ORDER BY t.created_at DESC + LIMIT ${limitNum} OFFSET ${offset} + `; + const [transfers] = await getDB().execute(transferQuery, [parseInt(agent_id)]); + + // 查询总数 + const [totalResult] = await getDB().execute( + `SELECT COUNT(*) as total + FROM agent_merchants am + JOIN transfers t ON am.merchant_id = t.from_user_id + WHERE am.agent_id = ?`, + [parseInt(agent_id)] + ); + + const total = totalResult[0].total; + + res.json({ + success: true, + data: { + transfers, + pagination: { + page: pageNum, + limit: limitNum, + total, + pages: Math.ceil(total / limitNum) + } + } + }); + } catch (error) { + console.error('获取代理商户转账记录失败:', error); + res.status(500).json({success: false, message: '获取代理商户转账记录失败,请稍后再试'}); + } }); /** * 获取分销列表 @@ -682,39 +737,44 @@ router.get('/merchants/:agent_id/transfers', async (req, res) => { * @returns {Object} 分销列表 */ router.get('/distribution', auth, async (req, res) => { - try { + try { - const { page = 1, size = 10,user_id } = req.query; - const { id } = user_id || req.user; - const pageNum = parseInt(page) || 1; - const limitNum = parseInt(size) || 10; - const offset = (page - 1) * size; - 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 - LIMIT ${size} OFFSET ${offset}`, - [parseInt(id)] - ); - const [totalResult] = await getDB().execute( - `SELECT COUNT(*) as total FROM users WHERE inviter = ? `, - [parseInt(id)] - ); - result.forEach(item => { - item.created_at = dayjs(item.created_at).format('YYYY-MM-DD HH:mm:ss'); - }) + const {page = 1, size = 10, user_id} = req.query; + const {id} = user_id || req.user; + const pageNum = parseInt(page) || 1; + const limitNum = parseInt(size) || 10; + const offset = (page - 1) * size; + 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 + LIMIT ${size} OFFSET ${offset}`, + [parseInt(id)] + ); + const [totalResult] = await getDB().execute( + `SELECT COUNT(*) as total + FROM users + WHERE inviter = ? `, + [parseInt(id)] + ); + result.forEach(item => { + item.created_at = dayjs(item.created_at).format('YYYY-MM-DD HH:mm:ss'); + }) - const total = totalResult[0].total; - res.json({ - success: true, data: result, pagination: { - page: pageNum, - limit: limitNum, - total, - pages: Math.ceil(total / limitNum) - } - }); - } catch (error) { - console.error('获取分销列表失败:', error); - res.status(500).json({ success: false, message: '获取分销列表失败' }); - } + const total = totalResult[0].total; + res.json({ + success: true, data: result, pagination: { + page: pageNum, + limit: limitNum, + total, + pages: Math.ceil(total / limitNum) + } + }); + } catch (error) { + console.error('获取分销列表失败:', error); + res.status(500).json({success: false, message: '获取分销列表失败'}); + } }); module.exports = router; \ No newline at end of file diff --git a/routes/agents/agents.js b/routes/agents/agents.js index cc60262..921e094 100644 --- a/routes/agents/agents.js +++ b/routes/agents/agents.js @@ -369,35 +369,31 @@ router.get('/:id/merchants', authenticateAdmin, async (req, res) => { 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) { return res.status(404).json({ success: false, message: '代理不存在' }); } // 查询代理的商户列表 const merchantsQuery = ` - SELECT - u.id, - u.real_name, - u.phone, - u.created_at, - am.created_at as joined_at, - COUNT(mo.id) as match_count, - COUNT(CASE WHEN mo.status = 'completed' THEN 1 END) as completed_matches - FROM agent_merchants am - JOIN users u ON am.merchant_id = u.id - LEFT JOIN matching_orders mo ON u.id = mo.initiator_id - WHERE am.agent_id = ? - GROUP BY u.id, am.created_at - ORDER BY am.created_at DESC - LIMIT ${limitNum} OFFSET ${offset} + SELECT u.id, + u.real_name, + u.phone, + u.created_at, + u.created_at as joined_at + FROM users u + WHERE u.inviter = ${id} + GROUP BY u.id, u.created_at + ORDER BY u.created_at + DESC + LIMIT ${limitNum} OFFSET ${offset} `; const merchants = await db.query(merchantsQuery, [id]); // 查询总数 const totalResult = await db.query( - 'SELECT COUNT(*) as total FROM agent_merchants WHERE agent_id = ?', + 'SELECT COUNT(*) as total FROM users WHERE inviter = ?', [id] ); 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 agentResult = await db.query('SELECT * FROM regional_agents WHERE id = ?', [id]); - if (!agentResult || agentResult.length === 0) { - return res.status(404).json({ success: false, message: '代理不存在' }); - } + 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) { + return res.status(404).json({ success: false, message: '代理不存在' }); + } // 构建查询条件 - let whereConditions = ['am.agent_id = ?']; + let whereConditions = ['am.inviter = ?']; let queryParams = [id]; if (merchant_id) { @@ -493,36 +489,35 @@ router.get('/:id/merchant-transfers', authenticateAdmin, async (req, res) => { // 查询商户转账记录 const transfersQuery = ` - SELECT - t.id, - t.from_user_id, - t.to_user_id, - t.amount, - t.status, - t.transfer_type, - t.description, - t.created_at, - t.confirmed_at, - from_user.real_name as from_real_name, - CONCAT(SUBSTRING(from_user.phone, 1, 3), '****', SUBSTRING(from_user.phone, -4)) as from_phone_masked, - to_user.real_name as to_real_name, - CONCAT(SUBSTRING(to_user.phone, 1, 3), '****', SUBSTRING(to_user.phone, -4)) as to_phone_masked - FROM agent_merchants am - JOIN transfers t ON am.merchant_id = t.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 - WHERE ${whereClause} - ORDER BY t.created_at DESC - LIMIT ${limitNum} OFFSET ${offset} + SELECT t.id, + t.from_user_id, + t.to_user_id, + t.amount, + t.status, + t.transfer_type, + t.description, + t.created_at, + t.confirmed_at, + from_user.real_name as from_real_name, + CONCAT(SUBSTRING(from_user.phone, 1, 3), '****', SUBSTRING(from_user.phone, -4)) as from_phone_masked, + to_user.real_name as to_real_name, + CONCAT(SUBSTRING(to_user.phone, 1, 3), '****', SUBSTRING(to_user.phone, -4)) as to_phone_masked + FROM users as am + 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 to_user ON t.to_user_id = to_user.id + WHERE ${whereClause} + ORDER BY t.created_at DESC + LIMIT ${limitNum} OFFSET ${offset} `; - + console.log(transfersQuery,queryParams); const transfers = await db.query(transfersQuery, queryParams); // 查询总数 const totalQuery = ` SELECT COUNT(*) as total - FROM agent_merchants am - JOIN transfers t ON am.merchant_id = t.from_user_id + FROM users am + JOIN transfers t ON am.id = t.from_user_id WHERE ${whereClause} `; const totalResult = await db.query(totalQuery, queryParams); diff --git a/routes/auth.js b/routes/auth.js index b18533a..d65b95d 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -31,7 +31,37 @@ router.post('/register', async (req, res) => { if (!captchaId || !captchaText) { 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) { return res.status(400).json({success: false, message: '短信验证码不能为空'}); } diff --git a/routes/sms.js b/routes/sms.js index 2011c6b..787bd56 100644 --- a/routes/sms.js +++ b/routes/sms.js @@ -106,7 +106,7 @@ function generateSMSCode() { router.post('/send', async (req, res) => { try { const { phone } = req.body - + console.log(phone) // 验证手机号格式 const phoneRegex = /^1[3-9]\d{9}$/ if (!phoneRegex.test(phone)) { @@ -141,6 +141,11 @@ router.post('/send', async (req, res) => { // 生产环境发送真实短信 try { console.log(code); + res.json({ + success: true, + message: '验证码发送成功' + }) + return const sendSmsRequest = new Dysmsapi20170525.SendSmsRequest({ phoneNumbers: phone, signName: SMS_CONFIG.signName, diff --git a/routes/transfers.js b/routes/transfers.js index bd6106e..91ff2fd 100644 --- a/routes/transfers.js +++ b/routes/transfers.js @@ -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) => { @@ -413,7 +361,7 @@ router.get('/user/:userId', authenticateToken, async (req, res) => { const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 10)); 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); let listParams = [userIdInt, userIdInt]; let countParams = [userIdInt, userIdInt]; diff --git a/routes/users.js b/routes/users.js index 78406c0..5b0afbc 100644 --- a/routes/users.js +++ b/routes/users.js @@ -1547,7 +1547,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => { ); distributeUser = distributeUser[0] if (distributeUser.user_type == 'agent') { - //给代理添加2980融豆的70% + //给代理添加2980融豆的70% 增加区域保护 await db.execute( 'UPDATE users SET balance = balance - ? WHERE id = ?', [serviceFee * 0.7, distributeUser.id] @@ -1578,7 +1578,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => { //记录转账记录 await db.execute( '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( @@ -1603,7 +1603,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => { ); orderCount = orderCount[0] if (orderCount.total <= 5) { - //给直营代理20%融豆给平台50%融豆给用户30% + //给直营代理20%融豆给平台50% 融豆给用户30% await db.execute( 'UPDATE users SET balance = balance - ? WHERE id = ?', [serviceFee * 0.2, distributeUser.inviter] @@ -1643,12 +1643,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => { //记录转账记录 await db.execute( '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( '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( @@ -1670,12 +1670,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => { //记录转账记录 await db.execute( '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( '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( @@ -1714,12 +1714,12 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => { //记录用户转账信息 await db.execute( '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( '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( @@ -1735,7 +1735,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => { //记录转账记录 await db.execute( '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( @@ -1762,7 +1762,7 @@ router.post('/:id/deduct-service-fee', auth, async (req, res) => { //给代理分成5% await db.execute( '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( diff --git a/services/matchingService.js b/services/matchingService.js index 555bfb8..380696e 100644 --- a/services/matchingService.js +++ b/services/matchingService.js @@ -1046,9 +1046,9 @@ class MatchingService { } // 确保至少有最小笔数 - if (allocations.length < minTransfers) { - throw new Error(`无法生成足够的分配:需要至少${minTransfers}笔,但只能生成${allocations.length}笔`); - } + // if (allocations.length < minTransfers) { + // throw new Error(`无法生成足够的分配:需要至少${minTransfers}笔,但只能生成${allocations.length}笔`); + // } // 精确控制总金额,避免超出预期 const currentTotal = allocations.reduce((sum, a) => sum + a.amount, 0); diff --git a/services/transferService.js b/services/transferService.js index 6c2f5d0..cd0cc50 100644 --- a/services/transferService.js +++ b/services/transferService.js @@ -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 {page = 1, limit = 10, sort = 'created_at', order = 'desc'} = pagination; const pageNum = parseInt(page, 10) || 1; @@ -372,7 +372,7 @@ class TransferService { let whereClause = 'WHERE 1=1 '; const params = []; - whereClause += `AND transfer_type != '${user_type}'`; + whereClause += `AND source_type != '${user_type}'`; // 构建查询条件 if (filters.user_id) { whereClause += ' AND (from_user_id = ? OR to_user_id = ?)';