2025-08-26 10:06:23 +08:00
|
|
|
|
const express = require('express')
|
|
|
|
|
|
const router = express.Router()
|
2025-09-15 17:27:13 +08:00
|
|
|
|
const {getDB} = require('../database')
|
2025-08-26 10:06:23 +08:00
|
|
|
|
|
2025-08-28 09:14:56 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @swagger
|
|
|
|
|
|
* tags:
|
|
|
|
|
|
* name: Regions
|
|
|
|
|
|
* description: 地区数据API
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @swagger
|
|
|
|
|
|
* components:
|
|
|
|
|
|
* schemas:
|
|
|
|
|
|
* Region:
|
|
|
|
|
|
* type: object
|
|
|
|
|
|
* properties:
|
|
|
|
|
|
* code:
|
|
|
|
|
|
* type: string
|
|
|
|
|
|
* description: 地区编码
|
|
|
|
|
|
* name:
|
|
|
|
|
|
* type: string
|
|
|
|
|
|
* description: 地区名称
|
|
|
|
|
|
* ZhejiangRegion:
|
|
|
|
|
|
* type: object
|
|
|
|
|
|
* properties:
|
|
|
|
|
|
* id:
|
|
|
|
|
|
* type: integer
|
|
|
|
|
|
* description: 地区ID
|
|
|
|
|
|
* city_name:
|
|
|
|
|
|
* type: string
|
|
|
|
|
|
* description: 城市名称
|
|
|
|
|
|
* district_name:
|
|
|
|
|
|
* type: string
|
|
|
|
|
|
* description: 区县名称
|
|
|
|
|
|
* region_code:
|
|
|
|
|
|
* type: string
|
|
|
|
|
|
* description: 地区编码
|
|
|
|
|
|
* is_available:
|
|
|
|
|
|
* type: integer
|
|
|
|
|
|
* description: 是否可用(1:可用 0:不可用)
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @swagger
|
|
|
|
|
|
* /regions/zhejiang:
|
|
|
|
|
|
* get:
|
|
|
|
|
|
* summary: 获取浙江省所有地区数据
|
|
|
|
|
|
* tags: [Regions]
|
|
|
|
|
|
* responses:
|
|
|
|
|
|
* 200:
|
|
|
|
|
|
* description: 成功获取浙江省地区数据
|
|
|
|
|
|
* content:
|
|
|
|
|
|
* application/json:
|
|
|
|
|
|
* schema:
|
|
|
|
|
|
* type: object
|
|
|
|
|
|
* properties:
|
|
|
|
|
|
* success:
|
|
|
|
|
|
* type: boolean
|
|
|
|
|
|
* example: true
|
|
|
|
|
|
* data:
|
|
|
|
|
|
* type: array
|
|
|
|
|
|
* items:
|
|
|
|
|
|
* $ref: '#/components/schemas/ZhejiangRegion'
|
|
|
|
|
|
* message:
|
|
|
|
|
|
* type: string
|
|
|
|
|
|
* example: 获取地区数据成功
|
|
|
|
|
|
* 500:
|
|
|
|
|
|
* description: 服务器错误
|
|
|
|
|
|
*/
|
2025-08-26 10:06:23 +08:00
|
|
|
|
router.get('/zhejiang', async (req, res) => {
|
2025-09-15 17:27:13 +08:00
|
|
|
|
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: '获取地区数据失败'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-08-26 10:06:23 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2025-08-28 09:14:56 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @swagger
|
|
|
|
|
|
* /regions/provinces:
|
|
|
|
|
|
* get:
|
|
|
|
|
|
* summary: 获取所有省份
|
|
|
|
|
|
* tags: [Regions]
|
|
|
|
|
|
* responses:
|
|
|
|
|
|
* 200:
|
|
|
|
|
|
* description: 成功获取省份列表
|
|
|
|
|
|
* content:
|
|
|
|
|
|
* application/json:
|
|
|
|
|
|
* schema:
|
|
|
|
|
|
* type: object
|
|
|
|
|
|
* properties:
|
|
|
|
|
|
* success:
|
|
|
|
|
|
* type: boolean
|
|
|
|
|
|
* example: true
|
|
|
|
|
|
* data:
|
|
|
|
|
|
* type: array
|
|
|
|
|
|
* items:
|
|
|
|
|
|
* $ref: '#/components/schemas/Region'
|
|
|
|
|
|
* 500:
|
|
|
|
|
|
* description: 服务器错误
|
|
|
|
|
|
*/
|
|
|
|
|
|
router.get('/provinces', async (req, res) => {
|
2025-09-15 17:27:13 +08:00
|
|
|
|
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: '获取省份列表失败'});
|
|
|
|
|
|
}
|
2025-08-28 09:14:56 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @swagger
|
|
|
|
|
|
* /regions/cities/{provinceCode}:
|
|
|
|
|
|
* get:
|
|
|
|
|
|
* summary: 根据省份代码获取城市列表
|
|
|
|
|
|
* tags: [Regions]
|
|
|
|
|
|
* parameters:
|
|
|
|
|
|
* - in: path
|
|
|
|
|
|
* name: provinceCode
|
|
|
|
|
|
* required: true
|
|
|
|
|
|
* schema:
|
|
|
|
|
|
* type: string
|
|
|
|
|
|
* description: 省份代码
|
|
|
|
|
|
* responses:
|
|
|
|
|
|
* 200:
|
|
|
|
|
|
* description: 成功获取城市列表
|
|
|
|
|
|
* content:
|
|
|
|
|
|
* application/json:
|
|
|
|
|
|
* schema:
|
|
|
|
|
|
* type: object
|
|
|
|
|
|
* properties:
|
|
|
|
|
|
* success:
|
|
|
|
|
|
* type: boolean
|
|
|
|
|
|
* example: true
|
|
|
|
|
|
* data:
|
|
|
|
|
|
* type: array
|
|
|
|
|
|
* items:
|
|
|
|
|
|
* $ref: '#/components/schemas/Region'
|
|
|
|
|
|
* 500:
|
|
|
|
|
|
* description: 服务器错误
|
|
|
|
|
|
*/
|
|
|
|
|
|
router.get('/cities/:provinceCode', async (req, res) => {
|
2025-09-15 17:27:13 +08:00
|
|
|
|
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: '获取城市列表失败'});
|
|
|
|
|
|
}
|
2025-08-28 09:14:56 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @swagger
|
|
|
|
|
|
* /regions/districts/{cityCode}:
|
|
|
|
|
|
* get:
|
|
|
|
|
|
* summary: 根据城市代码获取区县列表
|
|
|
|
|
|
* tags: [Regions]
|
|
|
|
|
|
* parameters:
|
|
|
|
|
|
* - in: path
|
|
|
|
|
|
* name: cityCode
|
|
|
|
|
|
* required: true
|
|
|
|
|
|
* schema:
|
|
|
|
|
|
* type: string
|
|
|
|
|
|
* description: 城市代码
|
|
|
|
|
|
* responses:
|
|
|
|
|
|
* 200:
|
|
|
|
|
|
* description: 成功获取区县列表
|
|
|
|
|
|
* content:
|
|
|
|
|
|
* application/json:
|
|
|
|
|
|
* schema:
|
|
|
|
|
|
* type: object
|
|
|
|
|
|
* properties:
|
|
|
|
|
|
* success:
|
|
|
|
|
|
* type: boolean
|
|
|
|
|
|
* example: true
|
|
|
|
|
|
* data:
|
|
|
|
|
|
* type: array
|
|
|
|
|
|
* items:
|
|
|
|
|
|
* $ref: '#/components/schemas/Region'
|
|
|
|
|
|
* 500:
|
|
|
|
|
|
* description: 服务器错误
|
|
|
|
|
|
*/
|
|
|
|
|
|
router.get('/districts/:cityCode', async (req, res) => {
|
2025-09-15 17:27:13 +08:00
|
|
|
|
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: '获取区县列表失败'});
|
|
|
|
|
|
}
|
2025-08-28 09:14:56 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @swagger
|
|
|
|
|
|
* /regions/path/{regionCode}:
|
|
|
|
|
|
* get:
|
|
|
|
|
|
* summary: 根据区域代码获取完整路径(省-市-区)
|
|
|
|
|
|
* tags: [Regions]
|
|
|
|
|
|
* parameters:
|
|
|
|
|
|
* - in: path
|
|
|
|
|
|
* name: regionCode
|
|
|
|
|
|
* required: true
|
|
|
|
|
|
* schema:
|
|
|
|
|
|
* type: string
|
|
|
|
|
|
* description: 区域代码
|
|
|
|
|
|
* responses:
|
|
|
|
|
|
* 200:
|
|
|
|
|
|
* description: 成功获取区域完整路径
|
|
|
|
|
|
* content:
|
|
|
|
|
|
* application/json:
|
|
|
|
|
|
* schema:
|
|
|
|
|
|
* type: object
|
|
|
|
|
|
* properties:
|
|
|
|
|
|
* success:
|
|
|
|
|
|
* type: boolean
|
|
|
|
|
|
* example: true
|
|
|
|
|
|
* data:
|
|
|
|
|
|
* type: object
|
|
|
|
|
|
* properties:
|
|
|
|
|
|
* province:
|
|
|
|
|
|
* $ref: '#/components/schemas/Region'
|
|
|
|
|
|
* city:
|
|
|
|
|
|
* $ref: '#/components/schemas/Region'
|
|
|
|
|
|
* district:
|
|
|
|
|
|
* $ref: '#/components/schemas/Region'
|
|
|
|
|
|
* 404:
|
|
|
|
|
|
* description: 区域不存在
|
|
|
|
|
|
* 500:
|
|
|
|
|
|
* description: 服务器错误
|
|
|
|
|
|
*/
|
|
|
|
|
|
router.get('/path/:regionCode', async (req, res) => {
|
2025-09-15 17:27:13 +08:00
|
|
|
|
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: '获取区域路径失败'});
|
2025-08-28 09:14:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 搜索区域(支持模糊搜索)
|
|
|
|
|
|
router.get('/search', async (req, res) => {
|
2025-09-15 17:27:13 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const {keyword, level} = req.query;
|
|
|
|
|
|
|
|
|
|
|
|
if (!keyword || keyword.trim() === '') {
|
|
|
|
|
|
return res.status(400).json({message: '搜索关键词不能为空'});
|
2025-08-28 09:14:56 +08:00
|
|
|
|
}
|
2025-09-15 17:27:13 +08:00
|
|
|
|
|
|
|
|
|
|
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: '搜索区域失败'});
|
2025-08-28 09:14:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-08-26 10:06:23 +08:00
|
|
|
|
module.exports = router
|