This commit is contained in:
2025-09-15 17:27:13 +08:00
parent d50290e8fe
commit 14a3c39f9d
27 changed files with 3571 additions and 31198 deletions

View File

@@ -110,10 +110,11 @@ router.post('/login', async (req, res) => {
// 先查询用户和代理信息(包含密码用于验证)
const [agents] = await getDB().execute(
`SELECT ra.*, u.id as user_id, u.username, u.phone, u.real_name, u.password, u.role, zr.city_name, zr.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
JOIN users u ON ra.user_id = u.id
JOIN zhejiang_regions zr ON ra.region_id = zr.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]
);
@@ -682,13 +683,14 @@ router.get('/merchants/:agent_id/transfers', async (req, res) => {
*/
router.get('/distribution', auth, async (req, res) => {
try {
const { id } = req.user;
const { page = 1, size = 10 } = req.query;
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 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}`,
[parseInt(id)]
);
@@ -715,5 +717,4 @@ router.get('/distribution', auth, async (req, res) => {
}
});
module.exports = router;

View File

@@ -20,7 +20,7 @@ const db = {
// 获取代理列表和统计信息
router.get('/', authenticateAdmin, async (req, res) => {
try {
const { page = 1, limit = 20, status, city, search } = req.query;
const { page = 1, limit = 20, status, city, search,district } = req.query;
const pageNum = parseInt(page) || 1;
const limitNum = parseInt(limit) || 20;
const offset = (pageNum - 1) * limitNum;
@@ -34,13 +34,13 @@ router.get('/', authenticateAdmin, async (req, res) => {
queryParams.push(status);
}
if (city) {
whereConditions.push('zr.city_name = ?');
queryParams.push(city);
if (district) {
whereConditions.push('zr.name = ?');
queryParams.push(district);
}
if (search) {
whereConditions.push('(ra.real_name LIKE ? OR ra.phone LIKE ?)');
whereConditions.push('(u.real_name LIKE ? OR u.phone LIKE ?)');
queryParams.push(`%${search}%`, `%${search}%`);
}
@@ -48,37 +48,33 @@ router.get('/', authenticateAdmin, async (req, res) => {
// 查询代理列表
const agentsQuery = `
SELECT
ra.*,
u.real_name,
u.phone,
u.id_card,
zr.city_name,
zr.district_name,
(
SELECT COUNT(DISTINCT merchant_id)
FROM agent_merchants
WHERE agent_id = ra.id
) as merchant_count,
(
SELECT CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2))
FROM agent_commission_records
WHERE agent_id = ra.id
) as total_commission,
0 as paid_commission,
(
SELECT CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2))
FROM agent_commission_records
WHERE agent_id = ra.id
) as pending_commission
FROM regional_agents ra
LEFT JOIN users u ON ra.user_id = u.id
LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id
${whereClause}
ORDER BY ra.created_at DESC
LIMIT ${limitNum} OFFSET ${offset}
`;
SELECT ra.*,
u.real_name,
u.phone,
u.id_card,
province.name AS province_name,
city.name AS city_name,
zr.name AS district_name,
(SELECT COUNT(DISTINCT merchant_id)
FROM agent_merchants
WHERE agent_id = ra.id) as merchant_count,
(SELECT CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10, 2))
FROM agent_commission_records
WHERE agent_id = ra.id) as total_commission,
0 as paid_commission,
(SELECT CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10, 2))
FROM agent_commission_records
WHERE agent_id = ra.id) as pending_commission
FROM regional_agents ra
LEFT JOIN users u ON ra.user_id = u.id
LEFT JOIN china_regions zr ON ra.region_id = zr.code -- 区
LEFT JOIN china_regions city ON zr.parent_code = city.code -- 市
LEFT JOIN china_regions province ON city.parent_code = province.code -- 省
${whereClause}
ORDER BY ra.created_at
DESC
LIMIT ${limitNum} OFFSET ${offset};`;
console.log(agentsQuery,queryParams)
const agents = await db.query(agentsQuery, queryParams);
// 查询总数
@@ -86,7 +82,7 @@ router.get('/', authenticateAdmin, async (req, res) => {
SELECT COUNT(DISTINCT ra.id) as total
FROM regional_agents ra
LEFT JOIN users u ON ra.user_id = u.id
LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id
LEFT JOIN china_regions zr ON ra.region_id = zr.code
${whereClause}
`;
@@ -377,7 +373,6 @@ router.get('/:id/merchants', authenticateAdmin, async (req, res) => {
if (!agentResult || agentResult.length === 0) {
return res.status(404).json({ success: false, message: '代理不存在' });
}
const agent = agentResult[0];
// 查询代理的商户列表
const merchantsQuery = `

View File

@@ -58,11 +58,9 @@ router.get('/', auth, async (req, res) => {
const [announcements] = await db.execute(query, [req.user.id, ...params]);
// 自动将过期公告标记为已读
console.log(announcements);
const expiredUnreadAnnouncements = announcements.filter(a => a.is_expired && !a.is_read);
console.log(expiredUnreadAnnouncements);
if (expiredUnreadAnnouncements.length > 0) {
const expiredIds = expiredUnreadAnnouncements.map(a => a.id);

View File

@@ -1,331 +1,314 @@
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { getDB } = require('../database');
const {getDB} = require('../database');
const router = express.Router();
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
router.post('/register', async (req, res) => {
try {
const db = getDB();
await db.query('START TRANSACTION');
const {
username,
phone,
password,
city,
district_id: district,
province,
inviter = '',
captchaId,
captchaText,
smsCode, // 短信验证码
role = 'user'
} = req.body;
if (!username || !phone || !password || !city || !district || !province) {
return res.status(400).json({ success: false, message: '用户名、手机号、密码、城市和区域不能为空' });
}
if (!captchaId || !captchaText) {
return res.status(400).json({ success: false, message: '图形验证码不能为空' });
}
if (!smsCode) {
return res.status(400).json({ success: false, message: '短信验证码不能为空' });
}
// 验证短信验证码
const smsAPI = require('./sms');
const smsValid = smsAPI.verifySMSCode(phone, smsCode);
if (!smsValid) {
return res.status(400).json({ success: false, message: '短信验证码错误或已过期' });
}
// 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(phone)) {
return res.status(400).json({ success: false, message: '手机号格式不正确' });
}
// 检查用户是否已存在
const [existingUsers] = await db.execute(
'SELECT id, payment_status FROM users WHERE username = ? OR phone = ?',
[username, phone]
);
if (existingUsers.length > 0) {
return res.status(400).json({ success: false, message: '用户名或手机号已存在' });
}
// 加密密码
const hashedPassword = await bcrypt.hash(password, 10);
// 创建用户(初始状态为未支付)
const [result] = await db.execute(
'INSERT INTO users (username, phone, password, role, points, audit_status, city, district_id, payment_status, province, inviter) VALUES (?, ?, ?, ?, ?, ?, ?, ?, "unpaid", ?, ?)',
[username, phone, hashedPassword, role, 0, 'pending', city, district, province, inviter]
);
const userId = result.insertId;
// 根据地区自动关联代理
const [agents] = await db.execute(
'SELECT ra.id FROM users u INNER JOIN regional_agents ra ON u.id = ra.user_id WHERE ra.region_id = ? AND ra.status = "active" ORDER BY ra.created_at ASC LIMIT 1',
[district]
);
if (agents.length > 0) {
await db.execute(
'INSERT INTO agent_merchants (agent_id, merchant_id, created_at) VALUES (?, ?, NOW())',
[agents[0].id, userId]
);
}
await db.query('COMMIT');
// 生成JWT token用于支付流程
const token = jwt.sign(
{ userId: userId, username, role },
JWT_SECRET,
{ expiresIn: '24h' }
);
res.status(201).json({
success: true,
message: '用户信息创建成功,请完成支付以激活账户',
token,
user: {
id: userId,
username,
phone,
role,
points: 0,
audit_status: 'pending',
city,
district,
paymentStatus: 'unpaid'
},
needPayment: true
});
} catch (error) {
try {
// await getDB().query('ROLLBACK');
} catch (rollbackError) {
console.error('回滚错误:', rollbackError);
const db = getDB();
await db.query('START TRANSACTION');
const {
username,
phone,
password,
city,
district_id: district,
province,
inviter = null,
captchaId,
captchaText,
smsCode, // 短信验证码
role = 'user'
} = req.body;
if (!username || !phone || !password || !city || !district || !province) {
return res.status(400).json({success: false, message: '用户名、手机号、密码、城市和区域不能为空'});
}
if (!captchaId || !captchaText) {
return res.status(400).json({success: false, message: '图形验证码不能为空'});
}
if (!smsCode) {
return res.status(400).json({success: false, message: '短信验证码不能为空'});
}
// 验证短信验证码
const smsAPI = require('./sms');
const smsValid = smsAPI.verifySMSCode(phone, smsCode);
if (!smsValid) {
return res.status(400).json({success: false, message: '短信验证码错误或已过期'});
}
// 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(phone)) {
return res.status(400).json({success: false, message: '手机号格式不正确'});
}
// 检查用户是否已存在
const [existingUsers] = await db.execute(
'SELECT id, payment_status FROM users WHERE username = ? OR phone = ?',
[username, phone]
);
if (existingUsers.length > 0) {
return res.status(400).json({success: false, message: '用户名或手机号已存在'});
}
// 加密密码
const hashedPassword = await bcrypt.hash(password, 10);
// 创建用户(初始状态为未支付)
const [result] = await db.execute(
'INSERT INTO users (username, phone, password, role, points, audit_status, city, district_id, payment_status, province, inviter) VALUES (?, ?, ?, ?, ?, ?, ?, ?, "unpaid", ?, ?)',
[username, phone, hashedPassword, role, 0, 'pending', city, district, province, inviter]
);
const userId = result.insertId;
await db.query('COMMIT');
// 生成JWT token用于支付流程
const token = jwt.sign(
{userId: userId, username, role},
JWT_SECRET,
{expiresIn: '24h'}
);
res.status(201).json({
success: true,
message: '用户信息创建成功,请完成支付以激活账户',
token,
user: {
id: userId,
username,
phone,
role,
points: 0,
audit_status: 'pending',
city,
district,
paymentStatus: 'unpaid'
},
needPayment: true
});
} catch (error) {
try {
// await getDB().query('ROLLBACK');
} catch (rollbackError) {
console.error('回滚错误:', rollbackError);
}
console.error('注册错误详情:', error);
console.error('错误堆栈:', error.stack);
res.status(500).json({
success: false,
message: '注册失败',
error: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
console.error('注册错误详情:', error);
console.error('错误堆栈:', error.stack);
res.status(500).json({
success: false,
message: '注册失败',
error: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
});
router.post('/login', async (req, res) => {
try {
const db = getDB();
const { username, password, captchaId, captchaText } = req.body;
try {
const db = getDB();
const {username, password, captchaId, captchaText} = req.body;
if (!username || !password) {
return res.status(400).json({ success: false, message: '用户名和密码不能为空' });
if (!username || !password) {
return res.status(400).json({success: false, message: '用户名和密码不能为空'});
}
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: '验证码错误'
});
}
// 注意:验证码已在前端通过 /captcha/verify 接口验证过,这里不再重复验证
// 查找用户(包含支付状态)
console.log('登录尝试 - 用户名:', username);
const [users] = await db.execute(
'SELECT * FROM users WHERE username = ?',
[username]
);
console.log('查找到的用户数量:', users.length);
if (users.length === 0) {
console.log('用户不存在:', username);
return res.status(401).json({success: false, message: '用户名或密码错误'});
}
const user = users[0];
console.log('找到用户:', user.username, '密码长度:', user.password ? user.password.length : 'null');
// 验证密码
console.log('验证密码 - 输入密码:', password, '数据库密码前10位:', user.password ? user.password.substring(0, 10) : 'null');
const isValidPassword = await bcrypt.compare(password, user.password);
console.log('密码验证结果:', isValidPassword);
if (!isValidPassword) {
console.log('密码验证失败');
return res.status(401).json({success: false, message: '用户名或密码错误'});
}
// 检查支付状态(管理员除外)
if (user.role !== 'admin' && user.payment_status === 'unpaid') {
const token = jwt.sign(
{userId: user.id, username: user.username, role: user.role},
JWT_SECRET,
{expiresIn: '5m'}
);
return res.status(200).json({
success: false,
message: '您的账户尚未激活,请完成支付后再登录',
needPayment: true,
user: user[0],
token
});
}
// 检查用户审核状态(管理员除外,只阻止被拒绝的用户)
if (user.role !== 'admin' && user.audit_status === 'rejected') {
return res.status(403).json({success: false, message: '您的账户审核未通过,请联系管理员'});
}
// 待审核用户可以正常登录使用系统,但匹配功能会有限制
// 生成JWT token
const token = jwt.sign(
{userId: user.id, username: user.username, role: user.role},
JWT_SECRET,
{expiresIn: '24h'}
);
const [is_distribution] = await db.execute(`
SELECT *
FROM distribution
WHERE user_id = ?`, [user.id]);
user.distribution = is_distribution.length > 0 ? true : false;
res.json({
success: true,
message: '登录成功',
token,
user
});
} catch (error) {
console.error('登录错误:', error);
res.status(500).json({success: false, message: '登录失败'});
}
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: '验证码错误'
});
}
// 注意:验证码已在前端通过 /captcha/verify 接口验证过,这里不再重复验证
// 查找用户(包含支付状态)
console.log('登录尝试 - 用户名:', username);
const [users] = await db.execute(
'SELECT * FROM users WHERE username = ?',
[username]
);
console.log('查找到的用户数量:', users.length);
if (users.length === 0) {
console.log('用户不存在:', username);
return res.status(401).json({ success: false, message: '用户名或密码错误' });
}
const user = users[0];
console.log('找到用户:', user.username, '密码长度:', user.password ? user.password.length : 'null');
// 验证密码
console.log('验证密码 - 输入密码:', password, '数据库密码前10位:', user.password ? user.password.substring(0, 10) : 'null');
const isValidPassword = await bcrypt.compare(password, user.password);
console.log('密码验证结果:', isValidPassword);
if (!isValidPassword) {
console.log('密码验证失败');
return res.status(401).json({ success: false, message: '用户名或密码错误' });
}
// 检查支付状态(管理员除外)
if (user.role !== 'admin' && user.payment_status === 'unpaid') {
const token = jwt.sign(
{ userId: user.id, username: user.username, role: user.role },
JWT_SECRET,
{ expiresIn: '5m' }
);
return res.status(200).json({
success: false,
message: '您的账户尚未激活,请完成支付后再登录',
needPayment: true,
user: user[0],
token
});
}
// 检查用户审核状态(管理员除外,只阻止被拒绝的用户)
if (user.role !== 'admin' && user.audit_status === 'rejected') {
return res.status(403).json({ success: false, message: '您的账户审核未通过,请联系管理员' });
}
// 待审核用户可以正常登录使用系统,但匹配功能会有限制
// 生成JWT token
const token = jwt.sign(
{ userId: user.id, username: user.username, role: user.role },
JWT_SECRET,
{ expiresIn: '24h' }
);
res.json({
success: true,
message: '登录成功',
token,
user: {
id: user.id,
username: user.username,
role: user.role,
avatar: user.avatar,
points: user.points,
payment_status: user.payment_status
}
});
} catch (error) {
console.error('登录错误:', error);
res.status(500).json({ success: false, message: '登录失败' });
}
});
// 验证token中间件
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ success: false, message: '访问令牌缺失' });
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ success: false, message: '访问令牌无效' });
if (!token) {
return res.status(401).json({success: false, message: '访问令牌缺失'});
}
req.user = user;
next();
});
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({success: false, message: '访问令牌无效'});
}
req.user = user;
next();
});
};
// 获取当前用户信息
router.get('/me', authenticateToken, async (req, res) => {
try {
const db = getDB();
const [users] = await db.execute(
'SELECT id, username, role, avatar, points, created_at FROM users WHERE id = ?',
[req.user.userId]
);
try {
const db = getDB();
const [users] = await db.execute(
'SELECT id, username, role, avatar, points, created_at FROM users WHERE id = ?',
[req.user.userId]
);
if (users.length === 0) {
return res.status(404).json({ success: false, message: '用户不存在' });
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
res.json({success: true, user: users[0]});
} catch (error) {
console.error('获取用户信息错误:', error);
res.status(500).json({success: false, message: '获取用户信息失败'});
}
res.json({ success: true, user: users[0] });
} catch (error) {
console.error('获取用户信息错误:', error);
res.status(500).json({ success: false, message: '获取用户信息失败' });
}
});
// 修改密码
router.put('/change-password', authenticateToken, async (req, res) => {
try {
const db = getDB();
const { currentPassword, newPassword } = req.body;
try {
const db = getDB();
const {currentPassword, newPassword} = req.body;
if (!currentPassword || !newPassword) {
return res.status(400).json({ success: false, message: '旧密码和新密码不能为空' });
if (!currentPassword || !newPassword) {
return res.status(400).json({success: false, message: '旧密码和新密码不能为空'});
}
// 获取用户当前密码
const [users] = await db.execute(
'SELECT password FROM users WHERE id = ?',
[req.user.userId]
);
if (users.length === 0) {
return res.status(404).json({success: false, message: '用户不存在'});
}
// 验证旧密码
const isValidPassword = await bcrypt.compare(currentPassword, users[0].password);
if (!isValidPassword) {
return res.status(400).json({success: false, message: '旧密码错误'});
}
// 加密新密码
const hashedNewPassword = await bcrypt.hash(newPassword, 10);
// 更新密码
await db.execute(
'UPDATE users SET password = ? WHERE id = ?',
[hashedNewPassword, req.user.userId]
);
res.json({success: true, message: '密码修改成功'});
} catch (error) {
console.error('修改密码错误:', error);
res.status(500).json({success: false, message: '修改密码失败'});
}
// 获取用户当前密码
const [users] = await db.execute(
'SELECT password FROM users WHERE id = ?',
[req.user.userId]
);
if (users.length === 0) {
return res.status(404).json({ success: false, message: '用户不存在' });
}
// 验证旧密码
const isValidPassword = await bcrypt.compare(currentPassword, users[0].password);
if (!isValidPassword) {
return res.status(400).json({ success: false, message: '旧密码错误' });
}
// 加密新密码
const hashedNewPassword = await bcrypt.hash(newPassword, 10);
// 更新密码
await db.execute(
'UPDATE users SET password = ? WHERE id = ?',
[hashedNewPassword, req.user.userId]
);
res.json({ success: true, message: '密码修改成功' });
} catch (error) {
console.error('修改密码错误:', error);
res.status(500).json({ success: false, message: '修改密码失败' });
}
});
module.exports = router;

View File

@@ -119,7 +119,6 @@ router.get('/generate', (req, res) => {
// 生成SVG图片
const svgImage = generateCaptchaSVG(captchaText);
res.json({
success: true,
data: {

View File

@@ -1,6 +1,6 @@
const express = require('express')
const router = express.Router()
const { getDB } = require('../database')
const {getDB} = require('../database')
/**
* @swagger
@@ -70,28 +70,28 @@ const { getDB } = require('../database')
* description: 服务器错误
*/
router.get('/zhejiang', async (req, res) => {
try {
const query = `
SELECT id, city_name, district_name, region_code, is_available
FROM zhejiang_regions
WHERE is_available = 1
ORDER BY city_name, district_name
`
const [rows] = await getDB().execute(query)
res.json({
success: true,
data: rows,
message: '获取地区数据成功'
})
} catch (error) {
console.error('获取浙江省地区数据失败:', error)
res.status(500).json({
success: false,
message: '获取地区数据失败'
})
}
try {
const query = `
SELECT id, city_name, district_name, region_code, is_available
FROM zhejiang_regions
WHERE is_available = 1
ORDER BY city_name, district_name
`
const [rows] = await getDB().execute(query)
res.json({
success: true,
data: rows,
message: '获取地区数据成功'
})
} catch (error) {
console.error('获取浙江省地区数据失败:', error)
res.status(500).json({
success: false,
message: '获取地区数据失败'
})
}
})
/**
@@ -119,56 +119,64 @@ router.get('/zhejiang', async (req, res) => {
* description: 服务器错误
*/
router.get('/provinces', async (req, res) => {
try {
// 一次性获取所有区域数据(省、市、区县)
const [allRegions] = await getDB().execute(
`SELECT code, name as label, level, parent_code FROM china_regions
WHERE level <= 3
ORDER BY level, code`
);
// 按level分组数据
const regionsByLevel = {
1: [], // 省份
2: [], // 城市
3: [] // 区县
};
// 创建code到region的映射便于快速查找
const regionMap = {};
// 分组并建立映射
allRegions.forEach(region => {
region.children = []; // 初始化children数组
regionsByLevel[region.level].push(region);
regionMap[region.code] = region;
});
// 构建层级关系:先处理区县到城市的关系
regionsByLevel[3].forEach(district => {
const parentCity = regionMap[district.parent_code];
if (parentCity) {
parentCity.children.push(district);
}
});
// 再处理城市到省份的关系
regionsByLevel[2].forEach(city => {
const parentProvince = regionMap[city.parent_code];
if (parentProvince) {
parentProvince.children.push(city);
}
});
// 返回省份数据(已包含完整的层级结构)
res.json({
success: true,
data: regionsByLevel[1]
});
} catch (error) {
console.error('获取省份列表错误:', error);
res.status(500).json({ message: '获取省份列表失败' });
}
try {
// 按level分组数据
const regionsByLevel = {
1: [], // 省份
2: [], // 城市
3: [] // 区县
};
if (!global.provinces) {
// 一次性获取所有区域数据(省、市、区县)
const [allRegions] = await getDB().execute(
`SELECT code, name as label, level, parent_code
FROM china_regions
WHERE level <= 3
ORDER BY level, code`
);
// 创建code到region的映射便于快速查找
const regionMap = {};
// 分组并建立映射
allRegions.forEach(region => {
region.children = []; // 初始化children数组
regionsByLevel[region.level].push(region);
regionMap[region.code] = region;
});
// 构建层级关系:先处理区县到城市的关系
regionsByLevel[3].forEach(district => {
const parentCity = regionMap[district.parent_code];
if (parentCity) {
parentCity.children.push(district);
}
});
// 再处理城市到省份的关系
regionsByLevel[2].forEach(city => {
const parentProvince = regionMap[city.parent_code];
if (parentProvince) {
parentProvince.children.push(city);
}
});
global.provinces = regionsByLevel[1];
}else {
console.log('1111')
regionsByLevel[1] = global.provinces;
}
// 返回省份数据(已包含完整的层级结构)
res.json({
success: true,
data: regionsByLevel[1]
});
} catch (error) {
console.error('获取省份列表错误:', error);
res.status(500).json({message: '获取省份列表失败'});
}
});
/**
@@ -203,24 +211,26 @@ router.get('/provinces', async (req, res) => {
* description: 服务器错误
*/
router.get('/cities/:provinceCode', async (req, res) => {
try {
const provinceCode = req.params.provinceCode;
const [cities] = await getDB().execute(
`SELECT code, name FROM china_regions
WHERE level = 2 AND parent_code = ?
ORDER BY code`,
[provinceCode]
);
res.json({
success: true,
data: cities
});
} catch (error) {
console.error('获取城市列表错误:', error);
res.status(500).json({ message: '获取城市列表失败' });
}
try {
const provinceCode = req.params.provinceCode;
const [cities] = await getDB().execute(
`SELECT code, name
FROM china_regions
WHERE level = 2
AND parent_code = ?
ORDER BY code`,
[provinceCode]
);
res.json({
success: true,
data: cities
});
} catch (error) {
console.error('获取城市列表错误:', error);
res.status(500).json({message: '获取城市列表失败'});
}
});
/**
@@ -255,24 +265,26 @@ router.get('/cities/:provinceCode', async (req, res) => {
* description: 服务器错误
*/
router.get('/districts/:cityCode', async (req, res) => {
try {
const cityCode = req.params.cityCode;
const [districts] = await getDB().execute(
`SELECT code, name FROM china_regions
WHERE level = 3 AND parent_code = ?
ORDER BY code`,
[cityCode]
);
res.json({
success: true,
data: districts
});
} catch (error) {
console.error('获取区县列表错误:', error);
res.status(500).json({ message: '获取区县列表失败' });
}
try {
const cityCode = req.params.cityCode;
const [districts] = await getDB().execute(
`SELECT code, name
FROM china_regions
WHERE level = 3
AND parent_code = ?
ORDER BY code`,
[cityCode]
);
res.json({
success: true,
data: districts
});
} catch (error) {
console.error('获取区县列表错误:', error);
res.status(500).json({message: '获取区县列表失败'});
}
});
/**
@@ -314,110 +326,111 @@ router.get('/districts/:cityCode', async (req, res) => {
* description: 服务器错误
*/
router.get('/path/:regionCode', async (req, res) => {
try {
const regionCode = req.params.regionCode;
// 获取当前区域信息
const [currentRegion] = await getDB().execute(
'SELECT code, name, level, parent_code FROM china_regions WHERE code = ?',
[regionCode]
);
if (currentRegion.length === 0) {
return res.status(404).json({ message: '区域不存在' });
try {
const regionCode = req.params.regionCode;
// 获取当前区域信息
const [currentRegion] = await getDB().execute(
'SELECT code, name, level, parent_code FROM china_regions WHERE code = ?',
[regionCode]
);
if (currentRegion.length === 0) {
return res.status(404).json({message: '区域不存在'});
}
const region = currentRegion[0];
const path = [region];
// 递归获取父级区域
let parentCode = region.parent_code;
while (parentCode) {
const [parentRegion] = await getDB().execute(
'SELECT code, name, level, parent_code FROM china_regions WHERE code = ? AND status = "active"',
[parentCode]
);
if (parentRegion.length > 0) {
path.unshift(parentRegion[0]);
parentCode = parentRegion[0].parent_code;
} else {
break;
}
}
res.json({
success: true,
data: {
path,
province: path.find(r => r.level === 1) || null,
city: path.find(r => r.level === 2) || null,
district: path.find(r => r.level === 3) || null
}
});
} catch (error) {
console.error('获取区域路径错误:', error);
res.status(500).json({message: '获取区域路径失败'});
}
const region = currentRegion[0];
const path = [region];
// 递归获取父级区域
let parentCode = region.parent_code;
while (parentCode) {
const [parentRegion] = await getDB().execute(
'SELECT code, name, level, parent_code FROM china_regions WHERE code = ? AND status = "active"',
[parentCode]
);
if (parentRegion.length > 0) {
path.unshift(parentRegion[0]);
parentCode = parentRegion[0].parent_code;
} else {
break;
}
}
res.json({
success: true,
data: {
path,
province: path.find(r => r.level === 1) || null,
city: path.find(r => r.level === 2) || null,
district: path.find(r => r.level === 3) || null
}
});
} catch (error) {
console.error('获取区域路径错误:', error);
res.status(500).json({ message: '获取区域路径失败' });
}
});
// 搜索区域(支持模糊搜索)
router.get('/search', async (req, res) => {
try {
const { keyword, level } = req.query;
if (!keyword || keyword.trim() === '') {
return res.status(400).json({ message: '搜索关键词不能为空' });
}
let sql = `SELECT code, name, level, parent_code FROM china_regions
WHERE name LIKE ?`;
const params = [`%${keyword.trim()}%`];
if (level) {
sql += ' AND level = ?';
params.push(parseInt(level));
}
sql += ' ORDER BY level, code LIMIT 50';
const [regions] = await getDB().execute(sql, params);
// 为每个搜索结果获取完整路径
const results = [];
for (const region of regions) {
const path = [region];
let parentCode = region.parent_code;
while (parentCode) {
const [parentRegion] = await getDB().execute(
'SELECT code, name, level, parent_code FROM china_regions WHERE code = ? AND status = "active"',
[parentCode]
);
if (parentRegion.length > 0) {
path.unshift(parentRegion[0]);
parentCode = parentRegion[0].parent_code;
} else {
break;
try {
const {keyword, level} = req.query;
if (!keyword || keyword.trim() === '') {
return res.status(400).json({message: '搜索关键词不能为空'});
}
}
results.push({
...region,
path,
fullName: path.map(r => r.name).join(' - ')
});
let sql = `SELECT code, name, level, parent_code
FROM china_regions
WHERE name LIKE ?`;
const params = [`%${keyword.trim()}%`];
if (level) {
sql += ' AND level = ?';
params.push(parseInt(level));
}
sql += ' ORDER BY level, code LIMIT 50';
const [regions] = await getDB().execute(sql, params);
// 为每个搜索结果获取完整路径
const results = [];
for (const region of regions) {
const path = [region];
let parentCode = region.parent_code;
while (parentCode) {
const [parentRegion] = await getDB().execute(
'SELECT code, name, level, parent_code FROM china_regions WHERE code = ? AND status = "active"',
[parentCode]
);
if (parentRegion.length > 0) {
path.unshift(parentRegion[0]);
parentCode = parentRegion[0].parent_code;
} else {
break;
}
}
results.push({
...region,
path,
fullName: path.map(r => r.name).join(' - ')
});
}
res.json({
success: true,
data: results
});
} catch (error) {
console.error('搜索区域错误:', error);
res.status(500).json({message: '搜索区域失败'});
}
res.json({
success: true,
data: results
});
} catch (error) {
console.error('搜索区域错误:', error);
res.status(500).json({ message: '搜索区域失败' });
}
});
module.exports = router

View File

@@ -141,12 +141,6 @@ 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,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff