From 49eed40ad04dce3d55057746aef2ddbd6d200de5 Mon Sep 17 00:00:00 2001 From: sunzhuangzhuang <961120009@qq.com> Date: Tue, 2 Sep 2025 09:29:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=95=86=E5=9F=8E=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/vcs.xml | 3 +- .vscode/launch.json | 16 + api-docs/swagger.json | 2155 ++++- config/database-init.js | 119 +- database.js | 2 +- docs/README.md | 79 + docs/apis/orders.js | 236 + docs/apis/products.js | 154 + docs/schemas/cart.js | 93 + docs/schemas/order.js | 102 + docs/schemas/product.js | 53 + docs/schemas/user.js | 104 + package.json | 4 +- routes/addresses.js | 96 +- routes/cart.js | 935 ++ routes/orders.js | 1377 ++- routes/products.js | 718 +- routes/regions.js | 27 +- routes/specifications.js | 1096 +++ routes/transfers.js | 29 +- routes/users.js | 593 +- scripts/fix_sql_syntax.js | 154 - scripts/fix_table_aliases.js | 133 - scripts/import_china_regions.js | 144 + scripts/pca-code.json | 14625 ++++++++++++++++++++++++++++++ scripts/verify_data.js | 37 + server.js | 23 +- services/matchingService.js | 123 +- swagger.js | 2 +- test_maoj.sql | 817 ++ 30 files changed, 22710 insertions(+), 1339 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 docs/README.md create mode 100644 docs/apis/orders.js create mode 100644 docs/apis/products.js create mode 100644 docs/schemas/cart.js create mode 100644 docs/schemas/order.js create mode 100644 docs/schemas/product.js create mode 100644 docs/schemas/user.js create mode 100644 routes/cart.js create mode 100644 routes/specifications.js delete mode 100644 scripts/fix_sql_syntax.js delete mode 100644 scripts/fix_table_aliases.js create mode 100644 scripts/import_china_regions.js create mode 100644 scripts/pca-code.json create mode 100644 scripts/verify_data.js create mode 100644 test_maoj.sql diff --git a/.idea/vcs.xml b/.idea/vcs.xml index bd8b2bb..e105af4 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,7 @@ - + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0d2b3d1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "node-terminal", + "name": "Run Script: dev:server", + "request": "launch", + "command": "npm run dev:server", + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/api-docs/swagger.json b/api-docs/swagger.json index be47090..90b00dc 100644 --- a/api-docs/swagger.json +++ b/api-docs/swagger.json @@ -178,6 +178,70 @@ } } }, + "CartItem": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "购物车项ID" + }, + "user_id": { + "type": "integer", + "description": "用户ID" + }, + "product_id": { + "type": "integer", + "description": "商品ID" + }, + "quantity": { + "type": "integer", + "description": "商品数量" + }, + "spec_combination_id": { + "type": "integer", + "description": "商品规格组合ID" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "创建时间" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "更新时间" + }, + "product": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "price": { + "type": "integer" + }, + "points_price": { + "type": "integer" + }, + "rongdou_price": { + "type": "integer" + }, + "image_url": { + "type": "string" + }, + "stock": { + "type": "integer" + }, + "status": { + "type": "string" + } + } + } + } + }, "MatchingOrder": { "type": "object", "properties": { @@ -428,6 +492,13 @@ "inactive" ] }, + "payment_methods": { + "type": "array", + "items": { + "type": "string" + }, + "description": "支付方式列表" + }, "created_at": { "type": "string", "format": "date-time", @@ -491,6 +562,68 @@ } } }, + "SpecName": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "description": "规格名称(如:颜色、尺寸)" + }, + "display_name": { + "type": "string", + "description": "显示名称" + }, + "sort_order": { + "type": "integer", + "description": "排序" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + } + } + }, + "SpecValue": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "spec_name_id": { + "type": "integer" + }, + "value": { + "type": "string", + "description": "规格值(如:红色、XL)" + }, + "display_value": { + "type": "string" + }, + "color_code": { + "type": "string", + "description": "颜色代码" + }, + "image_url": { + "type": "string" + }, + "sort_order": { + "type": "integer" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + } + } + }, "Transfer": { "type": "object", "properties": { @@ -621,6 +754,10 @@ "type": "boolean", "description": "是否为系统账户" }, + "is_distribute": { + "type": "boolean", + "description": "是否为分发账户" + }, "created_at": { "type": "string", "format": "date-time", @@ -931,7 +1068,7 @@ } }, "delete": { - "summary": "删除收货地址(软删除)", + "summary": "删除收货地址", "tags": [ "Addresses" ], @@ -1295,6 +1432,522 @@ } } }, + "/api/cart": { + "get": { + "summary": "获取购物车列表", + "tags": [ + "Cart" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "获取购物车成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CartItem" + } + }, + "total_count": { + "type": "integer", + "description": "购物车商品总数量" + }, + "total_amount": { + "type": "integer", + "description": "购物车总金额" + }, + "total_points": { + "type": "integer", + "description": "购物车总积分" + }, + "total_rongdou": { + "type": "integer", + "description": "购物车总融豆" + } + } + } + } + } + } + } + }, + "401": { + "description": "未授权" + }, + "500": { + "description": "服务器错误" + } + } + }, + "post": { + "summary": "添加商品到购物车", + "tags": [ + "Cart" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "product_id": { + "type": "integer", + "description": "商品ID" + }, + "quantity": { + "type": "integer", + "description": "商品数量", + "minimum": 1 + }, + "spec_combination_id": { + "type": "integer", + "description": "商品规格组合ID(可选)" + } + }, + "required": [ + "product_id", + "quantity" + ] + } + } + } + }, + "responses": { + "201": { + "description": "添加到购物车成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "cart_item_id": { + "type": "integer" + } + } + } + } + } + } + } + }, + "400": { + "description": "参数错误或库存不足" + }, + "401": { + "description": "未授权" + }, + "404": { + "description": "商品不存在或已下架" + }, + "500": { + "description": "服务器错误" + } + } + } + }, + "/api/cart/{id}": { + "put": { + "summary": "更新购物车商品数量", + "tags": [ + "Cart" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "购物车项ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "quantity": { + "type": "integer", + "description": "新的商品数量", + "minimum": 1 + } + }, + "required": [ + "quantity" + ] + } + } + } + }, + "responses": { + "200": { + "description": "更新购物车成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "400": { + "description": "参数错误或库存不足" + }, + "401": { + "description": "未授权" + }, + "404": { + "description": "购物车项不存在" + }, + "500": { + "description": "服务器错误" + } + } + }, + "delete": { + "summary": "删除购物车商品", + "tags": [ + "Cart" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "购物车项ID" + } + ], + "responses": { + "200": { + "description": "删除购物车商品成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "401": { + "description": "未授权" + }, + "404": { + "description": "购物车项不存在" + }, + "500": { + "description": "服务器错误" + } + } + } + }, + "/api/cart/batch": { + "delete": { + "summary": "批量删除购物车商品", + "tags": [ + "Cart" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "cart_item_ids": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "购物车项ID数组" + } + }, + "required": [ + "cart_item_ids" + ] + } + } + } + }, + "responses": { + "200": { + "description": "批量删除购物车商品成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "deleted_count": { + "type": "integer", + "description": "删除的商品数量" + } + } + } + } + } + } + } + }, + "400": { + "description": "参数错误" + }, + "401": { + "description": "未授权" + }, + "500": { + "description": "服务器错误" + } + } + } + }, + "/api/cart/clear": { + "delete": { + "summary": "清空购物车", + "tags": [ + "Cart" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "清空购物车成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "401": { + "description": "未授权" + }, + "500": { + "description": "服务器错误" + } + } + } + }, + "/api/cart/count": { + "get": { + "summary": "获取购物车商品数量", + "tags": [ + "Cart" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "获取购物车商品数量成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "购物车商品总数量" + } + } + } + } + } + } + } + }, + "401": { + "description": "未授权" + }, + "500": { + "description": "服务器错误" + } + } + } + }, + "/api/cart/checkout": { + "post": { + "summary": "购物车结账", + "tags": [ + "Cart" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "cart_item_ids": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "要结账的购物车项ID数组" + }, + "shipping_address": { + "type": "string", + "description": "收货地址" + } + }, + "required": [ + "cart_item_ids", + "shipping_address" + ] + } + } + } + }, + "responses": { + "201": { + "description": "结账成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "order_id": { + "type": "integer" + }, + "order_no": { + "type": "string" + }, + "total_amount": { + "type": "integer" + }, + "total_points": { + "type": "integer" + }, + "total_rongdou": { + "type": "integer" + } + } + } + } + } + } + } + }, + "400": { + "description": "参数错误或库存不足" + }, + "401": { + "description": "未授权" + }, + "500": { + "description": "服务器错误" + } + } + } + }, "/api/matching/create": { "post": { "summary": "创建匹配订单", @@ -2388,9 +3041,11 @@ "description": "服务器错误" } } - }, + } + }, + "/api/orders/confirm": { "post": { - "summary": "创建订单", + "summary": "确认下单", "tags": [ "Orders" ], @@ -2405,32 +3060,52 @@ "application/json": { "schema": { "type": "object", - "properties": { - "product_id": { - "type": "integer", - "description": "商品ID" - }, - "quantity": { - "type": "integer", - "description": "购买数量" - }, - "shipping_address": { - "type": "string", - "description": "收货地址" - } - }, "required": [ - "product_id", - "quantity", - "shipping_address" - ] + "pre_order_id", + "address" + ], + "properties": { + "pre_order_id": { + "type": "integer", + "description": "预订单ID" + }, + "address": { + "type": "object", + "properties": { + "recipient_name": { + "type": "string", + "description": "收货人姓名" + }, + "phone": { + "type": "string", + "description": "收货人电话" + }, + "province": { + "type": "string", + "description": "省份" + }, + "city": { + "type": "string", + "description": "城市" + }, + "district": { + "type": "string", + "description": "区县" + }, + "detail_address": { + "type": "string", + "description": "详细地址" + } + } + } + } } } } }, "responses": { - "201": { - "description": "订单创建成功", + "200": { + "description": "确认下单成功", "content": { "application/json": { "schema": { @@ -2445,14 +3120,11 @@ "data": { "type": "object", "properties": { - "orderId": { + "order_id": { "type": "integer" }, - "orderNumber": { + "order_no": { "type": "string" - }, - "pointsUsed": { - "type": "integer" } } } @@ -2462,13 +3134,121 @@ } }, "400": { - "description": "参数错误或积分不足" + "description": "请求参数错误" }, "401": { "description": "未授权" }, "404": { - "description": "商品不存在或已下架" + "description": "预订单不存在" + }, + "500": { + "description": "服务器错误" + } + } + } + }, + "/api/orders/pre-order/{id}": { + "get": { + "summary": "获取预订单详情", + "tags": [ + "Orders" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "预订单ID" + } + ], + "responses": { + "200": { + "description": "获取预订单详情成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "order_no": { + "type": "string" + }, + "total_amount": { + "type": "integer" + }, + "total_points": { + "type": "integer" + }, + "total_rongdou": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "product_id": { + "type": "integer" + }, + "product_name": { + "type": "string" + }, + "quantity": { + "type": "integer" + }, + "price": { + "type": "integer" + }, + "points_price": { + "type": "integer" + }, + "rongdou_price": { + "type": "integer" + }, + "spec_info": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + }, + "401": { + "description": "未授权" + }, + "404": { + "description": "预订单不存在" }, "500": { "description": "服务器错误" @@ -2534,6 +3314,100 @@ } } }, + "/api/orders/create-from-cart": { + "post": { + "summary": "创建预订单", + "tags": [ + "Orders" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "cart_item_ids": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "购物车项ID数组" + } + }, + "required": [ + "cart_item_ids" + ] + } + } + } + }, + "responses": { + "201": { + "description": "预订单创建成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "preOrderId": { + "type": "integer" + }, + "orderNumber": { + "type": "string" + }, + "totalAmount": { + "type": "integer" + }, + "totalPoints": { + "type": "integer" + }, + "totalRongdou": { + "type": "integer" + }, + "paymentMethods": { + "type": "array", + "items": { + "type": "string" + }, + "description": "去重后的支付方式列表" + } + } + } + } + } + } + } + }, + "400": { + "description": "参数错误或库存不足" + }, + "401": { + "description": "未授权" + }, + "404": { + "description": "购物车商品不存在" + }, + "500": { + "description": "服务器错误" + } + } + } + }, "/api/orders/{id}/cancel": { "put": { "summary": "用户取消订单", @@ -2730,6 +3604,245 @@ } } }, + "/api/orders/pending-payment/{id}": { + "get": { + "summary": "获取待支付预订单详情", + "tags": [ + "Orders" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "预订单ID" + } + ], + "responses": { + "200": { + "description": "获取预订单详情成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "order_no": { + "type": "string" + }, + "total_amount": { + "type": "integer" + }, + "total_points": { + "type": "integer" + }, + "total_rongdou": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "product_id": { + "type": "integer" + }, + "product_name": { + "type": "string" + }, + "quantity": { + "type": "integer" + }, + "price": { + "type": "integer" + }, + "points_price": { + "type": "integer" + }, + "rongdou_price": { + "type": "integer" + }, + "spec_info": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + }, + "401": { + "description": "未授权" + }, + "404": { + "description": "预订单不存在" + }, + "500": { + "description": "服务器错误" + } + } + } + }, + "/api/orders/confirm-payment": { + "post": { + "summary": "确认支付订单", + "description": "根据商品支付方式确认订单支付:\n- 仅积分支付:按10000积分=1融豆的比例扣除积分\n- 仅融豆支付:直接扣除融豆\n- 组合支付:优先扣除积分(按10000:1转换),不足部分扣除融豆\n", + "tags": [ + "Orders" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "order_id", + "address_id" + ], + "properties": { + "order_id": { + "type": "integer", + "description": "订单ID", + "example": 123 + }, + "address_id": { + "type": "integer", + "description": "收货地址ID", + "example": 456 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "确认支付成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "订单支付成功" + }, + "data": { + "type": "object", + "properties": { + "order_id": { + "type": "integer", + "example": 123 + }, + "order_no": { + "type": "string", + "example": "ORD20240101123456" + } + } + } + } + } + } + } + }, + "400": { + "description": "请求参数错误或余额不足", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": false + }, + "message": { + "type": "string", + "enum": [ + "订单ID和收货地址ID为必填项", + "积分不足", + "融豆不足", + "积分和融豆余额不足", + "商品支付方式配置错误" + ] + } + } + } + } + } + }, + "401": { + "description": "未授权" + }, + "404": { + "description": "订单或地址不存在", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": false + }, + "message": { + "type": "string", + "enum": [ + "订单不存在或已处理", + "收货地址不存在", + "用户不存在" + ] + } + } + } + } + } + }, + "500": { + "description": "服务器错误" + } + } + } + }, "/api/orders/stats": { "get": { "summary": "获取订单统计信息(管理员权限)", @@ -3433,9 +4546,81 @@ } } }, + "/products/hot": { + "get": { + "summary": "获取热销商品", + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "成功获取热销商品", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "object", + "properties": { + "products": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + } + } + } + }, + "/products/flash-sale": { + "get": { + "summary": "获取秒杀商品", + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "成功获取秒杀商品", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "object", + "properties": { + "products": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + } + } + } + }, "/products/{id}": { "get": { - "summary": "获取单个商品详情", + "summary": "获取单个商品详情(包含增强规格信息)", "tags": [ "Products" ], @@ -3452,17 +4637,167 @@ ], "responses": { "200": { - "description": "成功获取商品详情", + "description": "成功获取商品详情,包含完整的规格信息", "content": { "application/json": { "schema": { "type": "object", "properties": { "success": { - "type": "boolean" + "type": "boolean", + "example": true }, "data": { - "$ref": "#/components/schemas/Product" + "type": "object", + "properties": { + "product": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "category": { + "type": "string" + }, + "price": { + "type": "number" + }, + "points_price": { + "type": "number" + }, + "rongdou_price": { + "type": "number" + }, + "stock": { + "type": "integer" + }, + "specifications": { + "type": "array", + "description": "商品规格组合列表(笛卡尔积规格系统)", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "规格组合ID" + }, + "combination_key": { + "type": "string", + "description": "规格组合键(如:1-3-5)" + }, + "spec_display": { + "type": "string", + "description": "规格显示文本(如:颜色:红色 | 尺寸:XL)" + }, + "spec_details": { + "type": "array", + "description": "规格详细信息", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "spec_name": { + "type": "string", + "description": "规格名称" + }, + "spec_display_name": { + "type": "string", + "description": "规格显示名称" + }, + "value": { + "type": "string", + "description": "规格值" + }, + "display_value": { + "type": "string", + "description": "规格显示值" + }, + "color_code": { + "type": "string", + "description": "颜色代码" + }, + "image_url": { + "type": "string", + "description": "规格图片" + } + } + } + }, + "price_adjustment": { + "type": "number", + "description": "价格调整" + }, + "points_adjustment": { + "type": "number", + "description": "积分调整" + }, + "rongdou_adjustment": { + "type": "number", + "description": "融豆调整" + }, + "stock": { + "type": "integer", + "description": "规格库存" + }, + "sku_code": { + "type": "string", + "description": "SKU编码" + }, + "barcode": { + "type": "string", + "description": "条形码" + }, + "weight": { + "type": "number", + "description": "重量" + }, + "volume": { + "type": "number", + "description": "体积" + }, + "actual_price": { + "type": "number", + "description": "实际价格(基础价格+调整)" + }, + "actual_points_price": { + "type": "number", + "description": "实际积分价格" + }, + "actual_rongdou_price": { + "type": "number", + "description": "实际融豆价格" + }, + "is_available": { + "type": "boolean", + "description": "是否有库存" + } + } + } + }, + "specification_count": { + "type": "integer", + "description": "规格总数" + }, + "available_specifications": { + "type": "integer", + "description": "有库存的规格数量" + }, + "attributes": { + "type": "array", + "description": "商品属性" + }, + "isFavorited": { + "type": "boolean", + "description": "是否已收藏" + } + } + } + } } } } @@ -4201,6 +5536,622 @@ } } }, + "/specifications/names": { + "get": { + "summary": "获取所有规格名称", + "tags": [ + "Specifications" + ], + "parameters": [ + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "description": "状态筛选" + } + ], + "responses": { + "200": { + "description": "成功获取规格名称列表", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SpecName" + } + } + } + } + } + } + } + } + }, + "post": { + "summary": "创建规格名称", + "tags": [ + "Specifications" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "name", + "display_name" + ], + "properties": { + "name": { + "type": "string", + "description": "规格名称" + }, + "display_name": { + "type": "string", + "description": "显示名称" + }, + "sort_order": { + "type": "integer", + "default": 0 + } + } + } + } + } + }, + "responses": { + "201": { + "description": "规格名称创建成功" + } + } + } + }, + "/specifications/names/{id}": { + "delete": { + "summary": "删除规格名称", + "tags": [ + "Specifications" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "规格名称ID" + } + ], + "responses": { + "200": { + "description": "删除成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string", + "example": "规格名称删除成功" + } + } + } + } + } + }, + "400": { + "description": "该规格名称下还有规格值,无法删除" + }, + "404": { + "description": "规格名称不存在" + }, + "500": { + "description": "服务器错误" + } + } + } + }, + "/specifications/values": { + "get": { + "summary": "获取规格值列表", + "tags": [ + "Specifications" + ], + "parameters": [ + { + "in": "query", + "name": "spec_name_id", + "schema": { + "type": "integer" + }, + "description": "规格名称ID" + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "description": "状态筛选" + } + ], + "responses": { + "200": { + "description": "成功获取规格值列表" + } + } + }, + "post": { + "summary": "创建规格值", + "tags": [ + "Specifications" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "spec_name_id", + "value", + "display_value" + ], + "properties": { + "spec_name_id": { + "type": "integer" + }, + "value": { + "type": "string" + }, + "display_value": { + "type": "string" + }, + "color_code": { + "type": "string" + }, + "image_url": { + "type": "string" + }, + "sort_order": { + "type": "integer", + "default": 0 + } + } + } + } + } + }, + "responses": { + "201": { + "description": "规格值创建成功" + } + } + } + }, + "/specifications/combinations/{productId}": { + "get": { + "summary": "获取商品的规格组合", + "tags": [ + "Specifications" + ], + "parameters": [ + { + "in": "path", + "name": "productId", + "required": true, + "schema": { + "type": "integer" + }, + "description": "商品ID" + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "description": "状态筛选" + } + ], + "responses": { + "200": { + "description": "成功获取规格组合" + } + } + } + }, + "/specifications/combinations/{id}": { + "get": { + "summary": "获取单个规格组合详情", + "tags": [ + "Specifications" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "规格组合ID" + } + ], + "responses": { + "200": { + "description": "成功获取规格组合详情", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "product_id": { + "type": "integer" + }, + "combination_key": { + "type": "string" + }, + "spec_values": { + "type": "array", + "items": { + "type": "integer" + } + }, + "price_adjustment": { + "type": "integer" + }, + "points_adjustment": { + "type": "integer" + }, + "rongdou_adjustment": { + "type": "integer" + }, + "stock": { + "type": "integer" + }, + "sku_code": { + "type": "string" + }, + "barcode": { + "type": "string" + }, + "weight": { + "type": "number" + }, + "volume": { + "type": "number" + }, + "status": { + "type": "string" + }, + "spec_details": { + "type": "array", + "items": { + "type": "object" + } + }, + "actual_price": { + "type": "number" + }, + "actual_points_price": { + "type": "number" + }, + "actual_rongdou_price": { + "type": "number" + }, + "is_available": { + "type": "boolean" + } + } + } + } + } + } + } + }, + "404": { + "description": "规格组合不存在" + }, + "500": { + "description": "服务器错误" + } + } + }, + "delete": { + "summary": "删除规格组合", + "tags": [ + "Specifications" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "规格组合ID" + } + ], + "responses": { + "200": { + "description": "删除成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string", + "example": "规格组合删除成功" + } + } + } + } + } + }, + "404": { + "description": "规格组合不存在" + }, + "500": { + "description": "服务器错误" + } + } + }, + "put": { + "summary": "更新规格组合", + "tags": [ + "Specifications" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "规格组合ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "price_adjustment": { + "type": "integer" + }, + "points_adjustment": { + "type": "integer" + }, + "rongdou_adjustment": { + "type": "integer" + }, + "stock": { + "type": "integer" + }, + "sku_code": { + "type": "string" + }, + "barcode": { + "type": "string" + }, + "weight": { + "type": "number" + }, + "volume": { + "type": "number" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "规格组合更新成功" + }, + "404": { + "description": "规格组合不存在" + } + } + } + }, + "/specifications/combinations": { + "post": { + "summary": "创建商品规格组合", + "tags": [ + "Specifications" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "product_id", + "spec_values" + ], + "properties": { + "product_id": { + "type": "integer" + }, + "spec_values": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "规格值ID数组" + }, + "price_adjustment": { + "type": "integer", + "default": 0 + }, + "points_adjustment": { + "type": "integer", + "default": 0 + }, + "rongdou_adjustment": { + "type": "integer", + "default": 0 + }, + "stock": { + "type": "integer", + "default": 0 + }, + "sku_code": { + "type": "string" + }, + "barcode": { + "type": "string" + }, + "weight": { + "type": "number" + }, + "volume": { + "type": "number" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "规格组合创建成功" + } + } + } + }, + "/specifications/generate-combinations": { + "post": { + "summary": "为商品生成笛卡尔积规格组合", + "tags": [ + "Specifications" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "product_id", + "spec_name_ids" + ], + "properties": { + "product_id": { + "type": "integer" + }, + "spec_name_ids": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "规格名称ID数组" + }, + "default_stock": { + "type": "integer", + "default": 0, + "description": "默认库存" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "规格组合生成成功" + } + } + } + }, "/transfers": { "get": { "summary": "获取转账列表", @@ -5166,6 +7117,138 @@ } } } + }, + "/api/users/{id}/distribute": { + "put": { + "summary": "设置用户分发状态", + "description": "更新指定用户的分发状态", + "tags": [ + "Users" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "integer" + }, + "description": "用户ID" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "is_distribute" + ], + "properties": { + "is_distribute": { + "type": "boolean", + "description": "分发状态,true为启用分发,false为禁用分发", + "example": true + } + } + } + } + } + }, + "responses": { + "200": { + "description": "分发状态更新成功", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "分发状态更新成功" + }, + "is_distribute": { + "type": "boolean", + "description": "更新后的分发状态", + "example": true + } + } + } + } + } + }, + "400": { + "description": "请求参数错误", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": false + }, + "message": { + "type": "string", + "example": "分发状态无效" + } + } + } + } + } + }, + "404": { + "description": "用户不存在", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": false + }, + "message": { + "type": "string", + "example": "用户不存在" + } + } + } + } + } + }, + "500": { + "description": "服务器内部错误", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": false + }, + "message": { + "type": "string", + "example": "服务器内部错误" + } + } + } + } + } + } + } + } } }, "tags": [ @@ -5177,6 +7260,10 @@ "name": "Captcha", "description": "验证码API" }, + { + "name": "Cart", + "description": "购物车管理相关接口" + }, { "name": "Matching", "description": "匹配订单相关接口" diff --git a/config/database-init.js b/config/database-init.js index ab090cc..119de5f 100644 --- a/config/database-init.js +++ b/config/database-init.js @@ -53,6 +53,7 @@ async function createTables() { role ENUM('user', 'admin') DEFAULT 'user', avatar VARCHAR(255), points INT DEFAULT 0, + rongdou INT DEFAULT 0, balance DECIMAL(10,2) DEFAULT 0.00, real_name VARCHAR(100), id_card VARCHAR(18), @@ -111,7 +112,8 @@ async function createTables() { order_no VARCHAR(50) UNIQUE NOT NULL, total_amount INT NOT NULL, total_points INT NOT NULL, - status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending', + total_rongdou INT NOT NULL DEFAULT 0, + status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled', 'pre_order') DEFAULT 'pending', address JSON, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, @@ -158,12 +160,18 @@ async function createTables() { id INT AUTO_INCREMENT PRIMARY KEY, order_id INT NOT NULL, product_id INT NOT NULL, + spec_combination_id INT NULL COMMENT '规格组合ID', quantity INT NOT NULL, price INT NOT NULL, points INT NOT NULL, + points_price INT NOT NULL DEFAULT 0, + rongdou INT DEFAULT 0 COMMENT '融豆价格', + rongdou_price INT NOT NULL DEFAULT 0, + spec_info JSON COMMENT '规格信息快照', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (order_id) REFERENCES orders(id), - FOREIGN KEY (product_id) REFERENCES products(id) + FOREIGN KEY (product_id) REFERENCES products(id), + FOREIGN KEY (spec_combination_id) REFERENCES product_spec_combinations(id) ON DELETE SET NULL ) `); @@ -182,6 +190,21 @@ async function createTables() { ) `); + // 融豆记录表 + await getDB().execute(` + CREATE TABLE IF NOT EXISTS rongdou_history ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + type ENUM('earn', 'spend') NOT NULL, + amount INT NOT NULL, + description VARCHAR(255), + order_id INT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (order_id) REFERENCES orders(id) + ) + `); + // 管理员操作日志表 await getDB().execute(` CREATE TABLE IF NOT EXISTS admin_operation_logs ( @@ -213,22 +236,75 @@ async function createTables() { ) `); - // 商品规格表 + // 规格名称表(规格维度) await getDB().execute(` - CREATE TABLE IF NOT EXISTS product_specifications ( + CREATE TABLE IF NOT EXISTS spec_names ( id INT AUTO_INCREMENT PRIMARY KEY, - product_id INT NOT NULL, - spec_name VARCHAR(100) NOT NULL, - spec_value VARCHAR(100) NOT NULL, - price_adjustment INT DEFAULT 0, - points_adjustment INT DEFAULT 0, - rongdou_adjustment INT DEFAULT 0, - stock INT DEFAULT 0, - sku_code VARCHAR(100), + name VARCHAR(100) NOT NULL COMMENT '规格名称,如:颜色、尺寸、材质', + display_name VARCHAR(100) NOT NULL COMMENT '显示名称', + sort_order INT DEFAULT 0 COMMENT '排序', status ENUM('active', 'inactive') DEFAULT 'active', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE + UNIQUE KEY unique_name (name) + ) + `); + + // 规格值表 + await getDB().execute(` + CREATE TABLE IF NOT EXISTS spec_values ( + id INT AUTO_INCREMENT PRIMARY KEY, + spec_name_id INT NOT NULL COMMENT '规格名称ID', + value VARCHAR(100) NOT NULL COMMENT '规格值,如:红色、XL、棉质', + display_value VARCHAR(100) NOT NULL COMMENT '显示值', + color_code VARCHAR(20) COMMENT '颜色代码(仅颜色规格使用)', + image_url VARCHAR(500) COMMENT '规格图片', + sort_order INT DEFAULT 0 COMMENT '排序', + status ENUM('active', 'inactive') DEFAULT 'active', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (spec_name_id) REFERENCES spec_names(id) ON DELETE CASCADE, + UNIQUE KEY unique_spec_value (spec_name_id, value) + ) + `); + + // 商品规格组合表(笛卡尔积结果) + await getDB().execute(` + CREATE TABLE IF NOT EXISTS product_spec_combinations ( + id INT AUTO_INCREMENT PRIMARY KEY, + product_id INT NOT NULL COMMENT '商品ID', + combination_key VARCHAR(255) NOT NULL COMMENT '组合键,如:color_1-size_2-material_3', + spec_values JSON NOT NULL COMMENT '规格值组合,存储spec_value_id数组', + price_adjustment INT DEFAULT 0 COMMENT '价格调整', + points_adjustment INT DEFAULT 0 COMMENT '积分调整', + rongdou_adjustment INT DEFAULT 0 COMMENT '融豆调整', + stock INT DEFAULT 0 COMMENT '库存', + sku_code VARCHAR(100) COMMENT 'SKU编码', + barcode VARCHAR(100) COMMENT '条形码', + weight DECIMAL(8,3) COMMENT '重量(kg)', + volume DECIMAL(10,3) COMMENT '体积(cm³)', + status ENUM('active', 'inactive') DEFAULT 'active', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE, + UNIQUE KEY unique_product_combination (product_id, combination_key), + INDEX idx_product_status (product_id, status), + INDEX idx_sku_code (sku_code) + ) + `); + + // 商品规格关联表(定义商品使用哪些规格维度) + await getDB().execute(` + CREATE TABLE IF NOT EXISTS product_spec_names ( + id INT AUTO_INCREMENT PRIMARY KEY, + product_id INT NOT NULL COMMENT '商品ID', + spec_name_id INT NOT NULL COMMENT '规格名称ID', + is_required BOOLEAN DEFAULT TRUE COMMENT '是否必选规格', + sort_order INT DEFAULT 0 COMMENT '排序', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE, + FOREIGN KEY (spec_name_id) REFERENCES spec_names(id) ON DELETE CASCADE, + UNIQUE KEY unique_product_spec_name (product_id, spec_name_id) ) `); @@ -259,6 +335,23 @@ async function createTables() { ) `); + // 购物车表 + await getDB().execute(` + CREATE TABLE IF NOT EXISTS cart_items ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + product_id INT NOT NULL, + quantity INT NOT NULL DEFAULT 1, + spec_combination_id INT NULL COMMENT '规格组合ID', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE, + FOREIGN KEY (spec_combination_id) REFERENCES product_spec_combinations(id) ON DELETE CASCADE, + UNIQUE KEY unique_user_product_spec (user_id, product_id, spec_combination_id) + ) + `); + // 用户收货地址表 await getDB().execute(` CREATE TABLE IF NOT EXISTS user_addresses ( diff --git a/database.js b/database.js index fc6805d..b4005d6 100644 --- a/database.js +++ b/database.js @@ -10,7 +10,7 @@ const dbConfig = { user: 'test_mao', password: 'nK2mPbWriBp25BRd', database: 'test_mao', - // charset: 'utf8mb4', + charset: 'utf8mb4', // 连接池配置 connectionLimit: 20, // 连接池最大连接数 queueLimit: 0, // 排队等待连接的最大数量,0表示无限制 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..6d5cb13 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,79 @@ +# API 文档结构说明 + +本项目已将 Swagger API 文档从路由文件中分离出来,采用模块化的文档管理方式。 + +## 文件夹结构 + +``` +docs/ +├── README.md # 本说明文件 +├── schemas/ # 数据模型定义 +│ ├── product.js # 商品相关数据模型 +│ ├── order.js # 订单相关数据模型 +│ ├── user.js # 用户相关数据模型 +│ └── cart.js # 购物车相关数据模型 +└── apis/ # API 接口定义 + ├── products.js # 商品相关 API + └── orders.js # 订单相关 API +``` + +## 优势 + +1. **模块化管理**: 按功能模块分离文档,便于维护和查找 +2. **代码分离**: 路由文件专注于业务逻辑,文档定义独立管理 +3. **复用性**: Schema 定义可以在多个 API 中复用 +4. **可维护性**: 文档修改不会影响业务代码,降低出错风险 + +## 使用方法 + +### 添加新的 Schema + +在 `schemas/` 文件夹中创建新的 `.js` 文件,使用 `@swagger` 注释定义数据模型: + +```javascript +/** + * @swagger + * components: + * schemas: + * YourModel: + * type: object + * properties: + * id: + * type: integer + * description: ID + */ +``` + +### 添加新的 API 文档 + +在 `apis/` 文件夹中创建新的 `.js` 文件,使用 `@swagger` 注释定义 API 接口: + +```javascript +/** + * @swagger + * tags: + * name: YourModule + * description: 模块描述 + */ + +/** + * @swagger + * /your-endpoint: + * get: + * summary: 接口描述 + * tags: [YourModule] + * responses: + * 200: + * description: 成功响应 + */ +``` + +## 配置 + +Swagger 配置文件 `swagger.js` 已更新扫描路径: + +```javascript +apis: ['./docs/schemas/*.js', './docs/apis/*.js', './routes/*.js', './admin/routes/*.js'] +``` + +这样既保持了对现有路由文件中文档的兼容性,又支持新的模块化文档结构。 \ No newline at end of file diff --git a/docs/apis/orders.js b/docs/apis/orders.js new file mode 100644 index 0000000..c3c3c5f --- /dev/null +++ b/docs/apis/orders.js @@ -0,0 +1,236 @@ +/** + * @swagger + * tags: + * name: Orders + * description: 订单管理API + */ + +/** + * @swagger + * /orders/create-from-cart: + * post: + * summary: 从购物车创建预订单 + * tags: [Orders] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - cartIds + * properties: + * cartIds: + * type: array + * items: + * type: integer + * description: 购物车商品ID数组 + * responses: + * 200: + * description: 成功创建预订单 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * $ref: '#/components/schemas/PreOrder' + * 400: + * description: 请求参数错误 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + */ + +/** + * @swagger + * /orders/pre-order/{id}: + * get: + * summary: 获取预订单详情 + * tags: [Orders] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 预订单ID + * responses: + * 200: + * description: 成功获取预订单详情 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * preOrder: + * $ref: '#/components/schemas/PreOrder' + * items: + * type: array + * items: + * type: object + * properties: + * product_id: + * type: integer + * product_name: + * type: string + * quantity: + * type: integer + * points_price: + * type: integer + * rongdou_price: + * type: number + * image_url: + * type: string + * 404: + * description: 预订单不存在 + */ + +/** + * @swagger + * /orders/confirm: + * post: + * summary: 确认下单 + * tags: [Orders] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - preOrderId + * - shippingAddress + * properties: + * preOrderId: + * type: integer + * description: 预订单ID + * shippingAddress: + * type: string + * description: 收货地址 + * responses: + * 200: + * description: 订单确认成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * orderId: + * type: integer + * orderNumber: + * type: string + * 400: + * description: 请求参数错误或余额不足 + * 404: + * description: 预订单不存在 + */ + +/** + * @swagger + * /orders: + * get: + * summary: 获取用户订单列表 + * tags: [Orders] + * parameters: + * - in: query + * name: page + * schema: + * type: integer + * default: 1 + * description: 页码 + * - in: query + * name: limit + * schema: + * type: integer + * default: 10 + * description: 每页数量 + * - in: query + * name: status + * schema: + * type: string + * enum: [pending, confirmed, shipped, delivered, cancelled] + * description: 订单状态筛选 + * responses: + * 200: + * description: 成功获取订单列表 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * orders: + * type: array + * items: + * $ref: '#/components/schemas/Order' + * pagination: + * type: object + * properties: + * page: + * type: integer + * limit: + * type: integer + * total: + * type: integer + * pages: + * type: integer + */ + +/** + * @swagger + * /orders/{id}: + * get: + * summary: 获取订单详情 + * tags: [Orders] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 订单ID + * responses: + * 200: + * description: 成功获取订单详情 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * order: + * $ref: '#/components/schemas/Order' + * items: + * type: array + * items: + * $ref: '#/components/schemas/OrderItem' + * 404: + * description: 订单不存在 + */ \ No newline at end of file diff --git a/docs/apis/products.js b/docs/apis/products.js new file mode 100644 index 0000000..da8520c --- /dev/null +++ b/docs/apis/products.js @@ -0,0 +1,154 @@ +/** + * @swagger + * tags: + * name: Products + * description: 商品管理API + */ + +/** + * @swagger + * /products: + * get: + * summary: 获取商品列表 + * tags: [Products] + * parameters: + * - in: query + * name: page + * schema: + * type: integer + * default: 1 + * description: 页码 + * - in: query + * name: limit + * schema: + * type: integer + * default: 10 + * description: 每页数量 + * - in: query + * name: search + * schema: + * type: string + * description: 搜索关键词 + * - in: query + * name: category + * schema: + * type: string + * description: 商品分类 + * - in: query + * name: status + * schema: + * type: string + * enum: [active, inactive] + * description: 商品状态 + * responses: + * 200: + * description: 成功获取商品列表 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * products: + * type: array + * items: + * $ref: '#/components/schemas/Product' + * pagination: + * type: object + * properties: + * page: + * type: integer + * limit: + * type: integer + * total: + * type: integer + * pages: + * type: integer + */ + +/** + * @swagger + * /products/categories: + * get: + * summary: 获取商品分类列表 + * tags: [Products] + * responses: + * 200: + * description: 成功获取分类列表 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: array + * items: + * type: string + */ + +/** + * @swagger + * /products/hot: + * get: + * summary: 获取热门商品 + * tags: [Products] + * parameters: + * - in: query + * name: limit + * schema: + * type: integer + * default: 10 + * description: 返回数量 + * responses: + * 200: + * description: 成功获取热门商品 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * products: + * type: array + * items: + * $ref: '#/components/schemas/Product' + */ + +/** + * @swagger + * /products/{id}: + * get: + * summary: 获取商品详情 + * tags: [Products] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 商品ID + * responses: + * 200: + * description: 成功获取商品详情 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * $ref: '#/components/schemas/Product' + * 404: + * description: 商品不存在 + */ \ No newline at end of file diff --git a/docs/schemas/cart.js b/docs/schemas/cart.js new file mode 100644 index 0000000..71ffbe9 --- /dev/null +++ b/docs/schemas/cart.js @@ -0,0 +1,93 @@ +/** + * @swagger + * components: + * schemas: + * CartItem: + * type: object + * required: + * - user_id + * - product_id + * - quantity + * properties: + * id: + * type: integer + * description: 购物车商品ID + * user_id: + * type: integer + * description: 用户ID + * product_id: + * type: integer + * description: 商品ID + * quantity: + * type: integer + * description: 商品数量 + * created_at: + * type: string + * format: date-time + * description: 创建时间 + * updated_at: + * type: string + * format: date-time + * description: 更新时间 + * + * CartItemWithProduct: + * type: object + * properties: + * id: + * type: integer + * description: 购物车商品ID + * product_id: + * type: integer + * description: 商品ID + * product_name: + * type: string + * description: 商品名称 + * quantity: + * type: integer + * description: 商品数量 + * points_price: + * type: integer + * description: 积分价格 + * rongdou_price: + * type: number + * description: 融豆价格 + * image_url: + * type: string + * description: 商品图片URL + * stock: + * type: integer + * description: 库存数量 + * payment_methods: + * type: array + * items: + * type: string + * description: 支付方式列表 + * created_at: + * type: string + * format: date-time + * description: 创建时间 + * + * AddToCartRequest: + * type: object + * required: + * - product_id + * - quantity + * properties: + * product_id: + * type: integer + * description: 商品ID + * quantity: + * type: integer + * minimum: 1 + * description: 商品数量 + * + * UpdateCartRequest: + * type: object + * required: + * - quantity + * properties: + * quantity: + * type: integer + * minimum: 1 + * description: 商品数量 + */ \ No newline at end of file diff --git a/docs/schemas/order.js b/docs/schemas/order.js new file mode 100644 index 0000000..9151207 --- /dev/null +++ b/docs/schemas/order.js @@ -0,0 +1,102 @@ +/** + * @swagger + * components: + * schemas: + * Order: + * type: object + * required: + * - user_id + * - total_amount + * - status + * properties: + * id: + * type: integer + * description: 订单ID + * order_number: + * type: string + * description: 订单号 + * user_id: + * type: integer + * description: 用户ID + * total_amount: + * type: number + * description: 订单总金额 + * total_points: + * type: integer + * description: 订单总积分 + * total_rongdou: + * type: number + * description: 订单总融豆 + * status: + * type: string + * description: 订单状态 + * enum: [pending, confirmed, shipped, delivered, cancelled] + * payment_status: + * type: string + * description: 支付状态 + * enum: [pending, paid, failed, refunded] + * shipping_address: + * type: string + * description: 收货地址 + * created_at: + * type: string + * format: date-time + * description: 创建时间 + * updated_at: + * type: string + * format: date-time + * description: 更新时间 + * + * OrderItem: + * type: object + * properties: + * id: + * type: integer + * description: 订单商品ID + * order_id: + * type: integer + * description: 订单ID + * product_id: + * type: integer + * description: 商品ID + * quantity: + * type: integer + * description: 商品数量 + * price: + * type: number + * description: 商品价格 + * points_price: + * type: integer + * description: 积分价格 + * rongdou_price: + * type: number + * description: 融豆价格 + * created_at: + * type: string + * format: date-time + * description: 创建时间 + * + * PreOrder: + * type: object + * properties: + * preOrderId: + * type: integer + * description: 预订单ID + * orderNumber: + * type: string + * description: 订单号 + * totalAmount: + * type: number + * description: 总金额 + * totalPoints: + * type: integer + * description: 所需积分总数 + * totalRongdou: + * type: number + * description: 所需融豆总数 + * paymentMethods: + * type: array + * items: + * type: string + * description: 去重后的支付方式列表 + */ \ No newline at end of file diff --git a/docs/schemas/product.js b/docs/schemas/product.js new file mode 100644 index 0000000..314372d --- /dev/null +++ b/docs/schemas/product.js @@ -0,0 +1,53 @@ +/** + * @swagger + * components: + * schemas: + * Product: + * type: object + * required: + * - name + * - points_price + * - stock + * properties: + * id: + * type: integer + * description: 商品ID + * name: + * type: string + * description: 商品名称 + * category: + * type: string + * description: 商品分类 + * points_price: + * type: integer + * description: 积分价格 + * rongdou_price: + * type: number + * description: 融豆价格 + * stock: + * type: integer + * description: 库存数量 + * image_url: + * type: string + * description: 商品图片URL + * description: + * type: string + * description: 商品描述 + * status: + * type: string + * description: 商品状态 + * enum: [active, inactive] + * payment_methods: + * type: array + * items: + * type: string + * description: 支付方式列表 + * created_at: + * type: string + * format: date-time + * description: 创建时间 + * updated_at: + * type: string + * format: date-time + * description: 更新时间 + */ \ No newline at end of file diff --git a/docs/schemas/user.js b/docs/schemas/user.js new file mode 100644 index 0000000..08288a1 --- /dev/null +++ b/docs/schemas/user.js @@ -0,0 +1,104 @@ +/** + * @swagger + * components: + * schemas: + * User: + * type: object + * required: + * - username + * - email + * properties: + * id: + * type: integer + * description: 用户ID + * username: + * type: string + * description: 用户名 + * email: + * type: string + * format: email + * description: 邮箱地址 + * phone: + * type: string + * description: 手机号码 + * points: + * type: integer + * description: 积分余额 + * rongdou: + * type: number + * description: 融豆余额 + * avatar: + * type: string + * description: 头像URL + * status: + * type: string + * description: 用户状态 + * enum: [active, inactive, banned] + * created_at: + * type: string + * format: date-time + * description: 创建时间 + * updated_at: + * type: string + * format: date-time + * description: 更新时间 + * + * UserProfile: + * type: object + * properties: + * id: + * type: integer + * description: 用户ID + * username: + * type: string + * description: 用户名 + * email: + * type: string + * description: 邮箱地址 + * phone: + * type: string + * description: 手机号码 + * points: + * type: integer + * description: 积分余额 + * rongdou: + * type: number + * description: 融豆余额 + * avatar: + * type: string + * description: 头像URL + * + * LoginRequest: + * type: object + * required: + * - username + * - password + * properties: + * username: + * type: string + * description: 用户名或邮箱 + * password: + * type: string + * description: 密码 + * + * RegisterRequest: + * type: object + * required: + * - username + * - email + * - password + * properties: + * username: + * type: string + * description: 用户名 + * email: + * type: string + * format: email + * description: 邮箱地址 + * password: + * type: string + * description: 密码 + * phone: + * type: string + * description: 手机号码 + */ \ No newline at end of file diff --git a/package.json b/package.json index 50b873f..b68d7df 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,11 @@ "description": "Vue3 + Node.js 集成系统", "main": "server.js", "scripts": { - "dev": "concurrently \"npm run dev:frontend\" \"npm run dev:admin\" \"npm run dev:server\"", + "dev": "concurrently \"npm run dev:admin\" \"npm run dev:server\"", "dev:frontend": "cd frontend && npm run dev", "dev:admin": "cd admin && npm run dev", "dev:server": "nodemon server.js", - "build": "npm run build:frontend && npm run build:admin", + "build": " npm run build:admin", "build:frontend": "cd frontend && npm run build", "build:admin": "cd admin && npm run build", "start": "node server.js" diff --git a/routes/addresses.js b/routes/addresses.js index 41dd2f5..101bf80 100644 --- a/routes/addresses.js +++ b/routes/addresses.js @@ -100,16 +100,20 @@ const { auth } = require('../middleware/auth'); router.get('/', auth, async (req, res) => { try { const userId = req.user.id; - + const [addresses] = await getDB().execute( - `SELECT ua.*, al.name as label_name, al.color as label_color + `SELECT ua.*, al.name as label_name, al.color as label_color, + p.name as province_name, c.name as city_name, d.name as district_name FROM user_addresses ua - LEFT JOIN address_labels al ON ua.label_id = al.id - WHERE ua.user_id = ? AND ua.deleted_at IS NULL + LEFT JOIN address_labels al ON ua.label = al.id + LEFT JOIN china_regions p ON ua.province = p.code + LEFT JOIN china_regions c ON ua.city = c.code + LEFT JOIN china_regions d ON ua.district = d.code + WHERE ua.user_id = ? ORDER BY ua.is_default DESC, ua.created_at DESC`, [userId] ); - + res.json({ success: true, data: addresses @@ -158,7 +162,7 @@ router.get('/:id', auth, async (req, res) => { try { const addressId = req.params.id; const userId = req.user.id; - + const [addresses] = await getDB().execute( `SELECT ua.*, al.name as label_name, al.color as label_color FROM user_addresses ua @@ -166,11 +170,11 @@ router.get('/:id', auth, async (req, res) => { WHERE ua.id = ? AND ua.user_id = ? AND ua.deleted_at IS NULL`, [addressId, userId] ); - + if (addresses.length === 0) { return res.status(404).json({ message: '收货地址不存在' }); } - + res.json({ success: true, data: addresses[0] @@ -259,41 +263,36 @@ router.post('/', auth, async (req, res) => { recipient_name, phone, province_code, - province_name, city_code, - city_name, district_code, - district_name, detailed_address, - postal_code, - label_id, is_default = false } = req.body; - + // 验证必填字段 if (!recipient_name || !phone || !province_code || !city_code || !district_code || !detailed_address) { return res.status(400).json({ message: '收件人姓名、电话、省市区和详细地址不能为空' }); } - + // 如果设置为默认地址,先取消其他默认地址 if (is_default) { await getDB().execute( - 'UPDATE user_addresses SET is_default = false WHERE user_id = ? AND deleted_at IS NULL', + 'UPDATE user_addresses SET is_default = false WHERE user_id = ? ', [userId] ); } - + const [result] = await getDB().execute( `INSERT INTO user_addresses ( - user_id, recipient_name, phone, province_code, province_name, city_code, city_name, - district_code, district_name, detailed_address, postal_code, label_id, is_default, created_at, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`, + user_id, receiver_name, receiver_phone, province, city, + district, detailed_address, is_default, created_at, updated_at + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`, [ - userId, recipient_name, phone, province_code, province_name, city_code, city_name, - district_code, district_name, detailed_address, postal_code, label_id, is_default + userId, recipient_name, phone, province_code, city_code, + district_code, detailed_address, is_default ] ); - + res.status(201).json({ success: true, message: '收货地址创建成功', @@ -383,48 +382,45 @@ router.put('/:id', auth, async (req, res) => { recipient_name, phone, province_code, - province_name, city_code, - city_name, district_code, - district_name, detailed_address, - postal_code, - label_id, is_default } = req.body; - + if (!recipient_name || !phone || !province_code || !city_code || !district_code || !detailed_address) { + return res.status(400).json({ message: '收件人姓名、电话、省市区和详细地址不能为空' }); + } + // 检查地址是否存在且属于当前用户 const [existing] = await getDB().execute( - 'SELECT id FROM user_addresses WHERE id = ? AND user_id = ? AND deleted_at IS NULL', + 'SELECT id FROM user_addresses WHERE id = ? AND user_id = ? ', [addressId, userId] ); - + if (existing.length === 0) { return res.status(404).json({ message: '收货地址不存在' }); } - + // 如果设置为默认地址,先取消其他默认地址 if (is_default) { await getDB().execute( - 'UPDATE user_addresses SET is_default = false WHERE user_id = ? AND id != ? AND deleted_at IS NULL', + 'UPDATE user_addresses SET is_default = false WHERE user_id = ? AND id != ? ', [userId, addressId] ); } - + const [result] = await getDB().execute( `UPDATE user_addresses SET - recipient_name = ?, phone = ?, province_code = ?, province_name = ?, - city_code = ?, city_name = ?, district_code = ?, district_name = ?, - detailed_address = ?, postal_code = ?, label_id = ?, is_default = ?, updated_at = NOW() + receiver_name = ?, receiver_phone = ?, province = ?, city = ?, + district = ?, detailed_address = ?, is_default = ?, updated_at = NOW() WHERE id = ? AND user_id = ?`, [ - recipient_name, phone, province_code, province_name, city_code, city_name, - district_code, district_name, detailed_address, postal_code, label_id, is_default, + recipient_name, phone, province_code, city_code, + district_code, detailed_address, is_default, addressId, userId ] ); - + res.json({ success: true, message: '收货地址更新成功' @@ -439,7 +435,7 @@ router.put('/:id', auth, async (req, res) => { * @swagger * /addresses/{id}: * delete: - * summary: 删除收货地址(软删除) + * summary: 删除收货地址 * tags: [Addresses] * security: * - bearerAuth: [] @@ -475,16 +471,16 @@ router.delete('/:id', auth, async (req, res) => { try { const addressId = req.params.id; const userId = req.user.id; - + const [result] = await getDB().execute( - 'UPDATE user_addresses SET deleted_at = NOW() WHERE id = ? AND user_id = ? AND deleted_at IS NULL', + 'DELETE FROM user_addresses WHERE id = ? AND user_id = ?', [addressId, userId] ); - + if (result.affectedRows === 0) { return res.status(404).json({ message: '收货地址不存在' }); } - + res.json({ success: true, message: '收货地址删除成功' @@ -535,29 +531,29 @@ router.put('/:id/default', auth, async (req, res) => { try { const addressId = req.params.id; const userId = req.user.id; - + // 检查地址是否存在且属于当前用户 const [existing] = await getDB().execute( 'SELECT id FROM user_addresses WHERE id = ? AND user_id = ? AND deleted_at IS NULL', [addressId, userId] ); - + if (existing.length === 0) { return res.status(404).json({ message: '收货地址不存在' }); } - + // 取消其他默认地址 await getDB().execute( 'UPDATE user_addresses SET is_default = false WHERE user_id = ? AND deleted_at IS NULL', [userId] ); - + // 设置当前地址为默认 await getDB().execute( 'UPDATE user_addresses SET is_default = true WHERE id = ? AND user_id = ?', [addressId, userId] ); - + res.json({ success: true, message: '默认地址设置成功' diff --git a/routes/cart.js b/routes/cart.js new file mode 100644 index 0000000..6cb109f --- /dev/null +++ b/routes/cart.js @@ -0,0 +1,935 @@ +const express = require('express'); +const { getDB } = require('../database'); +const { auth } = require('../middleware/auth'); + +const router = express.Router(); + +/** + * @swagger + * tags: + * name: Cart + * description: 购物车管理相关接口 + */ + +/** + * @swagger + * components: + * schemas: + * CartItem: + * type: object + * properties: + * id: + * type: integer + * description: 购物车项ID + * user_id: + * type: integer + * description: 用户ID + * product_id: + * type: integer + * description: 商品ID + * quantity: + * type: integer + * description: 商品数量 + * spec_combination_id: + * type: integer + * description: 商品规格组合ID + * created_at: + * type: string + * format: date-time + * description: 创建时间 + * updated_at: + * type: string + * format: date-time + * description: 更新时间 + * product: + * type: object + * properties: + * id: + * type: integer + * name: + * type: string + * price: + * type: integer + * points_price: + * type: integer + * rongdou_price: + * type: integer + * image_url: + * type: string + * stock: + * type: integer + * status: + * type: string + */ + +/** + * @swagger + * /api/cart: + * get: + * summary: 获取购物车列表 + * tags: [Cart] + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: 获取购物车成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * items: + * type: array + * items: + * $ref: '#/components/schemas/CartItem' + * total_count: + * type: integer + * description: 购物车商品总数量 + * total_amount: + * type: integer + * description: 购物车总金额 + * total_points: + * type: integer + * description: 购物车总积分 + * total_rongdou: + * type: integer + * description: 购物车总融豆 + * 401: + * description: 未授权 + * 500: + * description: 服务器错误 + */ +router.get('/', auth, async (req, res) => { + try { + const userId = req.user.id; + + // 获取购物车商品列表 + const query = ` + SELECT + c.id, c.user_id, c.product_id, c.quantity, c.specification_id, + c.created_at, c.updated_at, + p.name, p.price, p.points_price, p.rongdou_price, p.image_url, + p.stock, p.status, p.shop_name, p.shop_avatar, + psc.combination_key, psc.price_adjustment, + psc.points_adjustment, psc.rongdou_adjustment, psc.stock as spec_stock, + GROUP_CONCAT(CONCAT(sn.display_name, ':', sv.display_value) ORDER BY sn.sort_order SEPARATOR ' | ') as spec_display + FROM cart_items c + LEFT JOIN products p ON c.product_id = p.id + LEFT JOIN product_spec_combinations psc ON c.specification_id = psc.id + LEFT JOIN JSON_TABLE(psc.spec_values, '$[*]' COLUMNS (spec_value_id INT PATH '$')) jt ON psc.id IS NOT NULL + LEFT JOIN spec_values sv ON jt.spec_value_id = sv.id + LEFT JOIN spec_names sn ON sv.spec_name_id = sn.id + WHERE c.user_id = ? AND p.status = 'active' + GROUP BY c.id + ORDER BY c.created_at DESC + `; + + const [cartItems] = await getDB().execute(query, [userId]); + + // 计算总计信息 + let totalCount = 0; + let totalAmount = 0; + let totalPoints = 0; + let totalRongdou = 0; + + const items = cartItems.map(item => { + const finalPrice = item.price + (item.price_adjustment || 0); + const finalPointsPrice = item.points_price + (item.points_adjustment || 0); + const finalRongdouPrice = item.rongdou_price + (item.rongdou_adjustment || 0); + + totalCount += item.quantity; + totalAmount += finalPrice * item.quantity; + totalPoints += finalPointsPrice * item.quantity; + totalRongdou += finalRongdouPrice * item.quantity; + + return { + id: item.id, + user_id: item.user_id, + product_id: item.product_id, + quantity: item.quantity, + spec_combination_id: item.spec_combination_id, + created_at: item.created_at, + updated_at: item.updated_at, + product: { + id: item.product_id, + name: item.name, + price: finalPrice, + points_price: finalPointsPrice, + rongdou_price: finalRongdouPrice, + image_url: item.image_url, + stock: item.spec_combination_id ? item.spec_stock : item.stock, + status: item.status, + shop_name: item.shop_name, + shop_avatar: item.shop_avatar + }, + specification: item.spec_combination_id ? { + id: item.spec_combination_id, + combination_key: item.combination_key, + spec_display: item.spec_display, + price_adjustment: item.price_adjustment, + points_adjustment: item.points_adjustment, + rongdou_adjustment: item.rongdou_adjustment + } : null + }; + }); + + res.json({ + success: true, + data: { + items, + total_count: totalCount, + total_amount: totalAmount, + total_points: totalPoints, + total_rongdou: totalRongdou + } + }); + } catch (error) { + console.error('获取购物车失败:', error); + res.status(500).json({ success: false, message: '获取购物车失败' }); + } +}); + +/** + * @swagger + * /api/cart: + * post: + * summary: 添加商品到购物车 + * tags: [Cart] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * product_id: + * type: integer + * description: 商品ID + * quantity: + * type: integer + * description: 商品数量 + * minimum: 1 + * spec_combination_id: + * type: integer + * description: 商品规格组合ID(可选) + * required: + * - product_id + * - quantity + * responses: + * 201: + * description: 添加到购物车成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * data: + * type: object + * properties: + * cart_item_id: + * type: integer + * 400: + * description: 参数错误或库存不足 + * 401: + * description: 未授权 + * 404: + * description: 商品不存在或已下架 + * 500: + * description: 服务器错误 + */ +router.post('/add', auth, async (req, res) => { + const db = getDB(); + await db.query('START TRANSACTION'); + + try { + const { productId, quantity, specificationId } = req.body; + const userId = req.user.id; + + + // 验证必填字段 + if (!productId || !quantity || quantity < 1) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: '请填写正确的商品信息和数量' }); + } + + // 检查商品是否存在且有效 + const [products] = await db.execute( + 'SELECT id, name, stock, status FROM products WHERE id = ?', + [productId] + ); + + if (products.length === 0 || products[0].status !== 'active') { + await db.query('ROLLBACK'); + return res.status(404).json({ success: false, message: '商品不存在或已下架' }); + } + + const product = products[0]; + let availableStock = product.stock; + + // 如果指定了规格组合,检查规格组合库存 + if (specificationId) { + const [specs] = await db.execute( + 'SELECT id, stock, status FROM product_spec_combinations WHERE id = ? AND product_id = ?', + [specificationId, productId] + ); + + if (specs.length === 0 || specs[0].status !== 'active') { + await db.query('ROLLBACK'); + return res.status(404).json({ success: false, message: '商品规格组合不存在或已下架' }); + } + + availableStock = specs[0].stock; + } + + // 检查购物车中是否已存在相同商品和规格组合 + const [existingItems] = await db.execute( + 'SELECT id, quantity FROM cart_items WHERE user_id = ? AND product_id = ? AND (specification_id = ? OR (specification_id IS NULL AND ? IS NULL))', + [userId, productId, specificationId, specificationId] + ); + + let finalQuantity = quantity; + if (existingItems.length > 0) { + finalQuantity += existingItems[0].quantity; + } + + // 检查库存是否足够 + if (availableStock < finalQuantity) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: '库存不足' }); + } + + let cartItemId; + + if (existingItems.length > 0) { + // 更新现有购物车项的数量 + await db.execute( + 'UPDATE cart_items SET quantity = ?, updated_at = NOW() WHERE id = ?', + [finalQuantity, existingItems[0].id] + ); + cartItemId = existingItems[0].id; + } else { + // 添加新的购物车项 + const [result] = await db.execute( + 'INSERT INTO cart_items (user_id, product_id, quantity, specification_id, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())', + [userId, productId, quantity, specificationId] + ); + cartItemId = result.insertId; + } + + await db.query('COMMIT'); + + res.status(201).json({ + success: true, + message: '添加到购物车成功', + data: { cart_item_id: cartItemId } + }); + } catch (error) { + await db.query('ROLLBACK'); + console.error('添加到购物车失败:', error); + res.status(500).json({ success: false, message: '添加到购物车失败' }); + } +}); + +/** + * @swagger + * /api/cart/{id}: + * put: + * summary: 更新购物车商品数量 + * tags: [Cart] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 购物车项ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * quantity: + * type: integer + * description: 新的商品数量 + * minimum: 1 + * required: + * - quantity + * responses: + * 200: + * description: 更新购物车成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * 400: + * description: 参数错误或库存不足 + * 401: + * description: 未授权 + * 404: + * description: 购物车项不存在 + * 500: + * description: 服务器错误 + */ +router.put('/:id', auth, async (req, res) => { + const db = getDB(); + await db.query('START TRANSACTION'); + + try { + const cartItemId = req.params.id; + const { quantity } = req.body; + const userId = req.user.id; + + // 验证数量 + if (!quantity || quantity < 1) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: '商品数量必须大于0' }); + } + + // 检查购物车项是否存在且属于当前用户 + const [cartItems] = await db.execute( + 'SELECT id, product_id, specification_id FROM cart_items WHERE id = ? AND user_id = ?', + [cartItemId, userId] + ); + console.log(cartItems,'cartItems'); + + + if (cartItems.length === 0) { + await db.query('ROLLBACK'); + return res.status(404).json({ success: false, message: '购物车项不存在' }); + } + + const cartItem = cartItems[0]; + + // 检查商品库存 + const [products] = await db.execute( + 'SELECT stock, status FROM products WHERE id = ?', + [cartItem.product_id] + ); + + if (products.length === 0 || products[0].status !== 'active') { + await db.query('ROLLBACK'); + return res.status(404).json({ success: false, message: '商品不存在或已下架' }); + } + + let availableStock = products[0].stock; + + // 如果有规格,检查规格库存 + if (cartItem.specification_id) { + const [specs] = await db.execute( + 'SELECT stock, status FROM product_spec_combinations WHERE id = ?', + [cartItem.specification_id] + ); + + if (specs.length === 0 || specs[0].status !== 'active') { + await db.query('ROLLBACK'); + return res.status(404).json({ success: false, message: '商品规格不存在或已下架' }); + } + + availableStock = specs[0].stock; + } + + // 检查库存是否足够 + if (availableStock < quantity) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: '库存不足' }); + } + + // 更新购物车项数量 + await db.execute( + 'UPDATE cart_items SET quantity = ?, updated_at = NOW() WHERE id = ?', + [quantity, cartItemId] + ); + + await db.query('COMMIT'); + + res.json({ + success: true, + message: '更新购物车成功' + }); + } catch (error) { + await db.query('ROLLBACK'); + console.error('更新购物车失败:', error); + res.status(500).json({ success: false, message: '更新购物车失败' }); + } +}); + +/** + * @swagger + * /api/cart/{id}: + * delete: + * summary: 删除购物车商品 + * tags: [Cart] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 购物车项ID + * responses: + * 200: + * description: 删除购物车商品成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * 401: + * description: 未授权 + * 404: + * description: 购物车项不存在 + * 500: + * description: 服务器错误 + */ +router.delete('/:id', auth, async (req, res) => { + try { + const cartItemId = req.params.id; + const userId = req.user.id; + + // 检查购物车项是否存在且属于当前用户 + const [cartItems] = await getDB().execute( + 'SELECT id FROM cart_items WHERE id = ? AND user_id = ?', + [cartItemId, userId] + ); + + if (cartItems.length === 0) { + return res.status(404).json({ success: false, message: '购物车项不存在' }); + } + + // 删除购物车项 + await getDB().execute( + 'DELETE FROM cart_items WHERE id = ?', + [cartItemId] + ); + + res.json({ + success: true, + message: '删除购物车商品成功' + }); + } catch (error) { + console.error('删除购物车商品失败:', error); + res.status(500).json({ success: false, message: '删除购物车商品失败' }); + } +}); + +/** + * @swagger + * /api/cart/batch: + * delete: + * summary: 批量删除购物车商品 + * tags: [Cart] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * cart_item_ids: + * type: array + * items: + * type: integer + * description: 购物车项ID数组 + * required: + * - cart_item_ids + * responses: + * 200: + * description: 批量删除购物车商品成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * data: + * type: object + * properties: + * deleted_count: + * type: integer + * description: 删除的商品数量 + * 400: + * description: 参数错误 + * 401: + * description: 未授权 + * 500: + * description: 服务器错误 + */ +router.delete('/batch', auth, async (req, res) => { + try { + const { cart_item_ids } = req.body; + const userId = req.user.id; + + // 验证参数 + if (!cart_item_ids || !Array.isArray(cart_item_ids) || cart_item_ids.length === 0) { + return res.status(400).json({ success: false, message: '请选择要删除的商品' }); + } + + // 构建删除条件 + const placeholders = cart_item_ids.map(() => '?').join(','); + const query = `DELETE FROM cart_items WHERE id IN (${placeholders}) AND user_id = ?`; + const params = [...cart_item_ids, userId]; + + const [result] = await getDB().execute(query, params); + + res.json({ + success: true, + message: '批量删除购物车商品成功', + data: { + deleted_count: result.affectedRows + } + }); + } catch (error) { + console.error('批量删除购物车商品失败:', error); + res.status(500).json({ success: false, message: '批量删除购物车商品失败' }); + } +}); + +/** + * @swagger + * /api/cart/clear: + * delete: + * summary: 清空购物车 + * tags: [Cart] + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: 清空购物车成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * 401: + * description: 未授权 + * 500: + * description: 服务器错误 + */ +router.delete('/clear', auth, async (req, res) => { + try { + const userId = req.user.id; + + // 清空用户购物车 + await getDB().execute( + 'DELETE FROM cart_items WHERE user_id = ?', + [userId] + ); + + res.json({ + success: true, + message: '清空购物车成功' + }); + } catch (error) { + console.error('清空购物车失败:', error); + res.status(500).json({ success: false, message: '清空购物车失败' }); + } +}); + +/** + * @swagger + * /api/cart/count: + * get: + * summary: 获取购物车商品数量 + * tags: [Cart] + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: 获取购物车商品数量成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * count: + * type: integer + * description: 购物车商品总数量 + * 401: + * description: 未授权 + * 500: + * description: 服务器错误 + */ +router.get('/count', auth, async (req, res) => { + try { + const userId = req.user.id; + + // 获取购物车商品总数量 + const [result] = await getDB().execute( + 'SELECT SUM(quantity) as count FROM cart_items WHERE user_id = ?', + [userId] + ); + + const count = result[0].count || 0; + + res.json({ + success: true, + data: { count } + }); + } catch (error) { + console.error('获取购物车商品数量失败:', error); + res.status(500).json({ success: false, message: '获取购物车商品数量失败' }); + } +}); + +/** + * @swagger + * /api/cart/checkout: + * post: + * summary: 购物车结账 + * tags: [Cart] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * cart_item_ids: + * type: array + * items: + * type: integer + * description: 要结账的购物车项ID数组 + * shipping_address: + * type: string + * description: 收货地址 + * required: + * - cart_item_ids + * - shipping_address + * responses: + * 201: + * description: 结账成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * data: + * type: object + * properties: + * order_id: + * type: integer + * order_no: + * type: string + * total_amount: + * type: integer + * total_points: + * type: integer + * total_rongdou: + * type: integer + * 400: + * description: 参数错误或库存不足 + * 401: + * description: 未授权 + * 500: + * description: 服务器错误 + */ +router.post('/checkout', auth, async (req, res) => { + const db = getDB(); + await db.query('START TRANSACTION'); + + try { + const { cart_item_ids, shipping_address } = req.body; + const userId = req.user.id; + + // 验证参数 + if (!cart_item_ids || !Array.isArray(cart_item_ids) || cart_item_ids.length === 0) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: '请选择要结账的商品' }); + } + + if (!shipping_address) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: '请填写收货地址' }); + } + + // 获取购物车商品信息 + const placeholders = cart_item_ids.map(() => '?').join(','); + const cartQuery = ` + SELECT + c.id, c.product_id, c.quantity, c.spec_combination_id, + p.name, p.price, p.points_price, p.rongdou_price, p.stock, p.status, + psc.price_adjustment, psc.points_adjustment, psc.rongdou_adjustment, psc.stock as spec_stock + FROM cart_items c + LEFT JOIN products p ON c.product_id = p.id + LEFT JOIN product_spec_combinations psc ON c.spec_combination_id = psc.id + WHERE c.id IN (${placeholders}) AND c.user_id = ? + `; + + const [cartItems] = await db.execute(cartQuery, [...cart_item_ids, userId]); + + if (cartItems.length === 0) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: '购物车商品不存在' }); + } + + // 验证商品状态和库存 + let totalAmount = 0; + let totalPoints = 0; + let totalRongdou = 0; + + for (const item of cartItems) { + if (item.status !== 'active') { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: `商品 ${item.name} 已下架` }); + } + + const availableStock = item.spec_combination_id ? item.spec_stock : item.stock; + if (availableStock < item.quantity) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: `商品 ${item.name} 库存不足` }); + } + + const finalPrice = item.price + (item.price_adjustment || 0); + const finalPointsPrice = item.points_price + (item.points_adjustment || 0); + const finalRongdouPrice = item.rongdou_price + (item.rongdou_adjustment || 0); + + totalAmount += finalPrice * item.quantity; + totalPoints += finalPointsPrice * item.quantity; + totalRongdou += finalRongdouPrice * item.quantity; + } + + // 检查用户积分和融豆是否足够 + const [users] = await db.execute( + 'SELECT points, rongdou FROM users WHERE id = ?', + [userId] + ); + + if (users.length === 0) { + await db.query('ROLLBACK'); + return res.status(404).json({ success: false, message: '用户不存在' }); + } + + const user = users[0]; + + if (user.points < totalPoints) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: '积分不足' }); + } + + if (user.rongdou < totalRongdou) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: '融豆不足' }); + } + + // 生成订单号 + const orderNo = 'ORD' + Date.now() + Math.random().toString(36).substr(2, 5).toUpperCase(); + + // 创建订单 + const [orderResult] = await db.execute( + `INSERT INTO orders (order_no, user_id, total_amount, total_points, total_rongdou, + status, shipping_address, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, 'pending', ?, NOW(), NOW())`, + [orderNo, userId, totalAmount, totalPoints, totalRongdou, shipping_address] + ); + + const orderId = orderResult.insertId; + + // 创建订单项 + for (const item of cartItems) { + const finalPrice = item.price + (item.price_adjustment || 0); + const finalPointsPrice = item.points_price + (item.points_adjustment || 0); + const finalRongdouPrice = item.rongdou_price + (item.rongdou_adjustment || 0); + + await db.execute( + `INSERT INTO order_items (order_id, product_id, spec_combination_id, quantity, + price, points_price, rongdou_price, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, NOW())`, + [orderId, item.product_id, item.spec_combination_id, item.quantity, + finalPrice, finalPointsPrice, finalRongdouPrice] + ); + + // 更新库存 + if (item.spec_combination_id) { + await db.execute( + 'UPDATE product_spec_combinations SET stock = stock - ? WHERE id = ?', + [item.quantity, item.spec_combination_id] + ); + } else { + await db.execute( + 'UPDATE products SET stock = stock - ? WHERE id = ?', + [item.quantity, item.product_id] + ); + } + } + + // 扣除用户积分和融豆 + await db.execute( + 'UPDATE users SET points = points - ?, rongdou = rongdou - ? WHERE id = ?', + [totalPoints, totalRongdou, userId] + ); + + // 删除已结账的购物车项 + const deletePlaceholders = cart_item_ids.map(() => '?').join(','); + await db.execute( + `DELETE FROM cart_items WHERE id IN (${deletePlaceholders}) AND user_id = ?`, + [...cart_item_ids, userId] + ); + + await db.query('COMMIT'); + + res.status(201).json({ + success: true, + message: '结账成功', + data: { + order_id: orderId, + order_no: orderNo, + total_amount: totalAmount, + total_points: totalPoints, + total_rongdou: totalRongdou + } + }); + + } catch (error) { + await db.query('ROLLBACK'); + console.error('购物车结账失败:', error); + res.status(500).json({ success: false, message: '结账失败' }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/orders.js b/routes/orders.js index e969574..a5c24a1 100644 --- a/routes/orders.js +++ b/routes/orders.js @@ -4,200 +4,61 @@ const { auth, adminAuth } = require('../middleware/auth'); const router = express.Router(); -/** - * @swagger - * tags: - * name: Orders - * description: 订单管理相关接口 - */ +// 订单管理路由 -/** - * @swagger - * components: - * schemas: - * Order: - * type: object - * properties: - * id: - * type: integer - * description: 订单ID - * order_no: - * type: string - * description: 订单编号 - * user_id: - * type: integer - * description: 用户ID - * total_amount: - * type: number - * description: 订单总金额 - * total_points: - * type: number - * description: 订单总积分 - * status: - * type: string - * enum: [pending, shipped, completed, cancelled] - * description: 订单状态 - * address: - * type: string - * description: 收货地址 - * created_at: - * type: string - * format: date-time - * description: 创建时间 - * updated_at: - * type: string - * format: date-time - * description: 更新时间 - * username: - * type: string - * description: 用户名 - */ -// 生成订单号 -function generateOrderNo() { - const timestamp = Date.now().toString(); - const random = Math.random().toString(36).substr(2, 5); - return `ORD${timestamp}${random}`.toUpperCase(); -} - -/** - * @swagger - * /api/orders: - * get: - * summary: 获取订单列表 - * tags: [Orders] - * security: - * - bearerAuth: [] - * parameters: - * - in: query - * name: page - * schema: - * type: integer - * default: 1 - * description: 页码 - * - in: query - * name: limit - * schema: - * type: integer - * default: 10 - * description: 每页数量 - * - in: query - * name: search - * schema: - * type: string - * description: 搜索关键词(订单号或用户名) - * - in: query - * name: orderNumber - * schema: - * type: string - * description: 订单号 - * - in: query - * name: username - * schema: - * type: string - * description: 用户名 - * - in: query - * name: status - * schema: - * type: string - * enum: [pending, shipped, completed, cancelled] - * description: 订单状态 - * - in: query - * name: startDate - * schema: - * type: string - * format: date - * description: 开始日期 - * - in: query - * name: endDate - * schema: - * type: string - * format: date - * description: 结束日期 - * responses: - * 200: - * description: 成功获取订单列表 - * content: - * application/json: - * schema: - * type: object - * properties: - * success: - * type: boolean - * data: - * type: object - * properties: - * orders: - * type: array - * items: - * $ref: '#/components/schemas/Order' - * pagination: - * type: object - * properties: - * page: - * type: integer - * limit: - * type: integer - * total: - * type: integer - * pages: - * type: integer - * 401: - * description: 未授权 - * 500: - * description: 服务器错误 - */ +// 获取订单列表 router.get('/', auth, async (req, res) => { try { const { page = 1, limit = 10, search = '', orderNumber = '', username = '', status = '', startDate = '', endDate = '' } = req.query; - - + + // 确保参数为有效数字 const pageNum = parseInt(page) || 1; const limitNum = parseInt(limit) || 10; const offset = (pageNum - 1) * limitNum; const isAdmin = req.user.role === 'admin'; - + let whereClause = 'WHERE 1=1'; const params = []; - + // 非管理员只能查看自己的订单 if (!isAdmin) { whereClause += ' AND o.user_id = ?'; params.push(req.user.id); } - + if (search) { whereClause += ' AND (o.order_no LIKE ? OR u.username LIKE ?)'; params.push(`%${search}%`, `%${search}%`); } - + if (orderNumber) { whereClause += ' AND o.order_no LIKE ?'; params.push(`%${orderNumber}%`); } - - if (username ) { + + if (username) { whereClause += ' AND u.username LIKE ?'; params.push(`%${username}%`); } - + if (status && status.trim()) { whereClause += ' AND o.status = ?'; params.push(status); } - + if (startDate && startDate.trim()) { whereClause += ' AND DATE(o.created_at) >= ?'; params.push(startDate); } - + if (endDate && endDate.trim()) { whereClause += ' AND DATE(o.created_at) <= ?'; params.push(endDate); } - + // 获取总数 const countQuery = ` SELECT COUNT(*) as total @@ -205,17 +66,17 @@ router.get('/', auth, async (req, res) => { LEFT JOIN users u ON o.user_id = u.id ${whereClause} `; - console.log(countQuery,params); - + console.log(countQuery, params); + const [countResult] = await getDB().execute(countQuery, params); const total = countResult[0].total; - console.log(total,'数量'); - + console.log(total, '数量'); + // 获取订单列表 const query = ` SELECT o.id, o.order_no, o.user_id, o.total_amount, o.total_points, - o.status, o.address, o.created_at, o.updated_at, + o.status, o.address, o.created_at, o.updated_at,o.total_rongdou, u.username FROM orders o LEFT JOIN users u ON o.user_id = u.id @@ -223,9 +84,47 @@ router.get('/', auth, async (req, res) => { ORDER BY o.created_at DESC LIMIT ${limitNum} OFFSET ${offset} `; - + const [orders] = await getDB().execute(query, [...params]); - + + // 为每个订单获取商品详情 + for (const order of orders) { + const [orderItems] = await getDB().execute( + `SELECT + oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price, + oi.spec_combination_id, + p.name as product_name, p.image_url, p.description, + psc.spec_values as spec_info + FROM order_items oi + LEFT JOIN products p ON oi.product_id = p.id + LEFT JOIN product_spec_combinations psc ON oi.spec_combination_id = psc.id + WHERE oi.order_id = ?`, + [order.id] + ); + + // 处理规格信息 + for (const item of orderItems) { + if (item.spec_info) { + try { + item.spec_info = JSON.parse(item.spec_info); + } catch (e) { + item.spec_info = null; + } + } + } + + // 处理地址信息 + if (order.address) { + try { + order.address = JSON.parse(order.address); + } catch (e) { + order.address = null; + } + } + + order.items = orderItems; + } + res.json({ success: true, data: { @@ -238,6 +137,325 @@ router.get('/', auth, async (req, res) => { } } }); + + /** + * @swagger + * /api/orders/confirm: + * post: + * summary: 确认下单 + * tags: [Orders] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - pre_order_id + * - address + * properties: + * pre_order_id: + * type: integer + * description: 预订单ID + * address: + * type: object + * properties: + * recipient_name: + * type: string + * description: 收货人姓名 + * phone: + * type: string + * description: 收货人电话 + * province: + * type: string + * description: 省份 + * city: + * type: string + * description: 城市 + * district: + * type: string + * description: 区县 + * detail_address: + * type: string + * description: 详细地址 + * responses: + * 200: + * description: 确认下单成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * data: + * type: object + * properties: + * order_id: + * type: integer + * order_no: + * type: string + * 400: + * description: 请求参数错误 + * 401: + * description: 未授权 + * 404: + * description: 预订单不存在 + * 500: + * description: 服务器错误 + */ + router.post('/confirm', auth, async (req, res) => { + const connection = await getDB().getConnection(); + + try { + await connection.beginTransaction(); + + const { pre_order_id, address } = req.body; + const userId = req.user.id; + + // 验证必填字段 + if (!pre_order_id || !address) { + return res.status(400).json({ success: false, message: '预订单ID和收货地址为必填项' }); + } + + const { recipient_name, phone, province, city, district, detail_address } = address; + if (!recipient_name || !phone || !province || !city || !district || !detail_address) { + return res.status(400).json({ success: false, message: '收货地址信息不完整' }); + } + + // 获取预订单信息 + const [preOrders] = await connection.execute( + `SELECT id, order_no, user_id, total_amount, total_points, total_rongdou, status + FROM orders WHERE id = ? AND user_id = ? AND status = 'pre_order'`, + [pre_order_id, userId] + ); + + if (preOrders.length === 0) { + await connection.rollback(); + return res.status(404).json({ success: false, message: '预订单不存在或已处理' }); + } + + const preOrder = preOrders[0]; + + // 获取用户当前积分和融豆 + const [users] = await connection.execute( + 'SELECT points, rongdou FROM users WHERE id = ?', + [userId] + ); + + if (users.length === 0) { + await connection.rollback(); + return res.status(404).json({ success: false, message: '用户不存在' }); + } + + const user = users[0]; + + // 检查积分和融豆是否足够 + if (preOrder.total_points > 0 && user.points < preOrder.total_points) { + await connection.rollback(); + return res.status(400).json({ success: false, message: '积分不足' }); + } + + if (preOrder.total_rongdou > 0 && user.rongdou < preOrder.total_rongdou) { + await connection.rollback(); + return res.status(400).json({ success: false, message: '融豆不足' }); + } + + // 扣除积分 + if (preOrder.total_points > 0) { + await connection.execute( + 'UPDATE users SET points = points - ? WHERE id = ?', + [preOrder.total_points, userId] + ); + + // 记录积分变动历史 + await connection.execute( + `INSERT INTO points_history (user_id, type, amount, description, order_id) + VALUES (?, 'spend', ?, ?, ?)`, + [userId, preOrder.total_points, `订单消费 - ${preOrder.order_no}`, pre_order_id] + ); + } + + // 扣除融豆 + if (preOrder.total_rongdou > 0) { + await connection.execute( + 'UPDATE users SET rongdou = rongdou - ? WHERE id = ?', + [preOrder.total_rongdou, userId] + ); + + // 记录融豆变动历史 + await connection.execute( + `INSERT INTO rongdou_history (user_id, type, amount, description, order_id) + VALUES (?, 'spend', ?, ?, ?)`, + [userId, preOrder.total_rongdou, `订单消费 - ${preOrder.order_no}`, pre_order_id] + ); + } + + // 更新订单状态和收货地址 + const addressStr = JSON.stringify({ + recipient_name, + phone, + province, + city, + district, + detail_address + }); + + await connection.execute( + `UPDATE orders SET status = 'pending', address = ?, updated_at = NOW() + WHERE id = ?`, + [addressStr, pre_order_id] + ); + + await connection.commit(); + + res.json({ + success: true, + message: '订单确认成功', + data: { + order_id: pre_order_id, + order_no: preOrder.order_no + } + }); + + } catch (error) { + await connection.rollback(); + console.error('确认下单失败:', error); + res.status(500).json({ success: false, message: '确认下单失败' }); + } finally { + connection.release(); + } + }); + + /** + * @swagger + * /api/orders/pre-order/{id}: + * get: + * summary: 获取预订单详情 + * tags: [Orders] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 预订单ID + * responses: + * 200: + * description: 获取预订单详情成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * id: + * type: integer + * order_no: + * type: string + * total_amount: + * type: integer + * total_points: + * type: integer + * total_rongdou: + * type: integer + * status: + * type: string + * created_at: + * type: string + * items: + * type: array + * items: + * type: object + * properties: + * id: + * type: integer + * product_id: + * type: integer + * product_name: + * type: string + * quantity: + * type: integer + * price: + * type: integer + * points_price: + * type: integer + * rongdou_price: + * type: integer + * spec_info: + * type: object + * 401: + * description: 未授权 + * 404: + * description: 预订单不存在 + * 500: + * description: 服务器错误 + */ + router.get('/pre-order/:id', auth, async (req, res) => { + try { + const preOrderId = req.params.id; + const userId = req.user.id; + + // 获取预订单基本信息 + const [orders] = await getDB().execute( + `SELECT id, order_no, user_id, total_amount, total_points, total_rongdou, + status, created_at FROM orders WHERE id = ? AND user_id = ? AND status = 'pre_order'`, + [preOrderId, userId] + ); + + if (orders.length === 0) { + return res.status(404).json({ success: false, message: '预订单不存在' }); + } + + const order = orders[0]; + + // 获取预订单商品详情 + const [orderItems] = await getDB().execute( + `SELECT + oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price, + oi.spec_combination_id, + p.name as product_name, p.image_url, p.description, + psc.spec_values as spec_info + FROM order_items oi + LEFT JOIN products p ON oi.product_id = p.id + LEFT JOIN product_spec_combinations psc ON oi.spec_combination_id = psc.id + WHERE oi.order_id = ?`, + [preOrderId] + ); + + // 处理规格信息 + for (const item of orderItems) { + if (item.spec_info) { + try { + item.spec_info = JSON.parse(item.spec_info); + } catch (e) { + item.spec_info = null; + } + } + } + + res.json({ + success: true, + data: { + ...order, + items: orderItems + } + }); + } catch (error) { + console.error('获取预订单详情失败:', error); + res.status(500).json({ success: false, message: '获取预订单详情失败' }); + } + }); } catch (error) { console.error('获取订单列表失败:', error); res.status(500).json({ success: false, message: '获取订单列表失败' }); @@ -285,16 +503,16 @@ router.get('/:id', auth, async (req, res) => { try { const { id } = req.params; const isAdmin = req.user.role === 'admin'; - + let whereClause = 'WHERE o.id = ?'; const params = [id]; - + // 非管理员只能查看自己的订单 if (!isAdmin) { whereClause += ' AND o.user_id = ?'; params.push(req.user.id); } - + const query = ` SELECT o.id, o.order_no, o.user_id, o.total_amount, o.total_points, @@ -304,16 +522,54 @@ router.get('/:id', auth, async (req, res) => { LEFT JOIN users u ON o.user_id = u.id ${whereClause} `; - + const [orders] = await getDB().execute(query, params); - + if (orders.length === 0) { return res.status(404).json({ success: false, message: '订单不存在' }); } - + + const order = orders[0]; + + // 获取订单商品详情 + const [orderItems] = await getDB().execute( + `SELECT + oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price, + oi.spec_combination_id, + p.name as product_name, p.image_url, p.description, + psc.spec_values as spec_info + FROM order_items oi + LEFT JOIN products p ON oi.product_id = p.id + LEFT JOIN product_spec_combinations psc ON oi.spec_combination_id = psc.id + WHERE oi.order_id = ?`, + [order.id] + ); + + // 处理规格信息 + for (const item of orderItems) { + if (item.spec_info) { + try { + item.spec_info = JSON.parse(item.spec_info); + } catch (e) { + item.spec_info = null; + } + } + } + + // 处理地址信息 + if (order.address) { + try { + order.address = JSON.parse(order.address); + } catch (e) { + order.address = null; + } + } + + order.items = orderItems; + res.json({ success: true, - data: { order: orders[0] } + data: { order } }); } catch (error) { console.error('获取订单详情失败:', error); @@ -321,163 +577,150 @@ router.get('/:id', auth, async (req, res) => { } }); -/** - * @swagger - * /api/orders: - * post: - * summary: 创建订单 - * tags: [Orders] - * security: - * - bearerAuth: [] - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * product_id: - * type: integer - * description: 商品ID - * quantity: - * type: integer - * description: 购买数量 - * shipping_address: - * type: string - * description: 收货地址 - * required: - * - product_id - * - quantity - * - shipping_address - * responses: - * 201: - * description: 订单创建成功 - * content: - * application/json: - * schema: - * type: object - * properties: - * success: - * type: boolean - * message: - * type: string - * data: - * type: object - * properties: - * orderId: - * type: integer - * orderNumber: - * type: string - * pointsUsed: - * type: integer - * 400: - * description: 参数错误或积分不足 - * 401: - * description: 未授权 - * 404: - * description: 商品不存在或已下架 - * 500: - * description: 服务器错误 - */ -router.post('/', auth, async (req, res) => { +// 创建预订单 +router.post('/create-from-cart', auth, async (req, res) => { const db = getDB(); await db.query('START TRANSACTION'); - - try { - - const { product_id, quantity, shipping_address } = req.body; + try { + const { cart_item_ids } = req.body; const user_id = req.user.id; - + // 验证必填字段 - if (!product_id || !quantity || !shipping_address) { - return res.status(400).json({ success: false, message: '请填写所有必填字段' }); - } - - // 检查商品是否存在且有效 - const [products] = await db.execute( - 'SELECT id, name, points_price, stock, status FROM products WHERE id = ?', - [product_id] - ); - - if (products.length === 0 || products[0].status !== 'active') { + if (!cart_item_ids || !Array.isArray(cart_item_ids) || cart_item_ids.length === 0) { await db.query('ROLLBACK'); - return res.status(404).json({ success: false, message: '商品不存在或已下架' }); + return res.status(400).json({ success: false, message: '请选择要购买的商品' }); } - - const product = products[0]; - - // 检查库存 - if (product.stock < quantity) { + + // 获取购物车商品信息和支付方式 + const placeholders = cart_item_ids.map(() => '?').join(','); + const cartQuery = ` + SELECT + c.id, c.product_id, c.quantity, c.specification_id, + p.name, p.price, p.points_price, p.rongdou_price, p.stock, p.status, p.payment_methods, + psc.price_adjustment, psc.points_adjustment, psc.rongdou_adjustment, psc.stock as spec_stock + FROM cart_items c + LEFT JOIN products p ON c.product_id = p.id + LEFT JOIN product_spec_combinations psc ON c.specification_id = psc.id + WHERE c.id IN (${placeholders}) AND c.user_id = ? + `; + + const [cartItems] = await db.execute(cartQuery, [...cart_item_ids, user_id]); + + if (cartItems.length === 0) { await db.query('ROLLBACK'); - return res.status(400).json({ success: false, message: '库存不足' }); + return res.status(400).json({ success: false, message: '购物车商品不存在' }); + } + + // 验证商品状态和库存,计算总价和支付方式 + let totalAmount = 0; + let totalPoints = 0; + let totalRongdou = 0; + let allPaymentMethods = []; + + for (const item of cartItems) { + if (item.status !== 'active') { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: `商品 ${item.name} 已下架` }); + } + + const availableStock = item.specification_id ? item.spec_stock : item.stock; + if (availableStock < item.quantity) { + await db.query('ROLLBACK'); + return res.status(400).json({ success: false, message: `商品 ${item.name} 库存不足` }); + } + + // 解析商品支付方式 + let productPaymentMethods = ['rongdou']; // 默认支付方式 + if (item.payment_methods) { + try { + productPaymentMethods = JSON.parse(item.payment_methods); + } catch (e) { + console.error('解析商品支付方式失败:', e); + } + } + console.log(productPaymentMethods,'productPaymentMethods'); + + allPaymentMethods = allPaymentMethods.concat(productPaymentMethods); + + const finalPrice = item.price + (item.price_adjustment || 0); + const finalRongdouPrice = item.rongdou_price + (item.rongdou_adjustment || 0); + + totalAmount += finalPrice * item.quantity; + + // 根据支付方式计算积分和融豆需求 + const hasPoints = productPaymentMethods.includes('points') || productPaymentMethods.includes('points_rongdou'); + const hasRongdou = productPaymentMethods.includes('rongdou') || productPaymentMethods.includes('points_rongdou'); + + if (hasPoints && !hasRongdou) { + // 仅积分支付:按10000积分=1融豆计算 + totalPoints += finalRongdouPrice * item.quantity * 10000; + totalRongdou += finalRongdouPrice * item.quantity; + } else if (!hasPoints && hasRongdou) { + // 仅融豆支付 + totalRongdou += finalRongdouPrice * item.quantity; + } else { + // 组合支付或默认:记录融豆价格,前端可选择支付方式 + totalRongdou += finalRongdouPrice * item.quantity; + } } - // 计算总积分 - const totalPoints = product.points_price * quantity; - - // 检查用户积分是否足够 - const [users] = await db.execute( - 'SELECT id, username, points FROM users WHERE id = ?', - [user_id] - ); - - if (users.length === 0) { - await db.query('ROLLBACK'); - return res.status(404).json({ success: false, message: '用户不存在' }); - } - - const user = users[0]; - - if (user.points < totalPoints) { - await db.query('ROLLBACK'); - return res.status(400).json({ success: false, message: '积分不足' }); - } - - // 生成订单号 - const orderNumber = 'ORD' + Date.now() + Math.random().toString(36).substr(2, 4).toUpperCase(); - - // 创建订单 + // 去重支付方式 + const uniquePaymentMethods = [...new Set(allPaymentMethods)]; + console.log('订单支付方式:', uniquePaymentMethods); + + // 生成预订单号 + const orderNumber = 'PRE' + Date.now() + Math.random().toString(36).substr(2, 5).toUpperCase(); + + // 创建预订单(状态为pre_order) const [orderResult] = await db.execute( - `INSERT INTO orders (order_no, user_id, total_amount, total_points, status, address, created_at, updated_at) - VALUES (?, ?, ?, ?, 'pending', ?, NOW(), NOW())`, - [orderNumber, user_id, 0, totalPoints, shipping_address] + `INSERT INTO orders (order_no, user_id, total_amount, total_points, total_rongdou, + status, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, 'pre_order', NOW(), NOW())`, + [orderNumber, user_id, totalAmount, totalPoints, totalRongdou] ); - - // 扣除用户积分 + + const preOrderId = orderResult.insertId; + + // 创建预订单项 + for (const item of cartItems) { + const finalPrice = item.price + (item.price_adjustment || 0); + const finalPointsPrice = item.points_price + (item.points_adjustment || 0); + const finalRongdouPrice = item.rongdou_price + (item.rongdou_adjustment || 0); + + await db.execute( + `INSERT INTO order_items (order_id, product_id, spec_combination_id, quantity, + price, points, points_price, rongdou, rongdou_price, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`, + [preOrderId, item.product_id, item.specification_id, item.quantity, + finalPrice, finalPointsPrice * item.quantity, finalPointsPrice, finalRongdouPrice * item.quantity, finalRongdouPrice] + ); + } + + // 删除购物车中的商品 await db.execute( - 'UPDATE users SET points = points - ? WHERE id = ?', - [totalPoints, user_id] + `DELETE FROM cart_items WHERE id IN (${placeholders})`, + cart_item_ids ); - - // 减少商品库存 - await db.execute( - 'UPDATE products SET stock = stock - ? WHERE id = ?', - [quantity, product_id] - ); - - // 记录积分历史 - await db.execute( - `INSERT INTO points_history (user_id, amount, type, description, created_at) - VALUES (?, ?, 'spend', ?, NOW())`, - [user_id, -totalPoints, `购买商品:${product.name}`] - ); - + await db.query('COMMIT'); - + res.status(201).json({ success: true, - message: '订单创建成功', + message: '预订单创建成功', data: { - orderId: orderResult.insertId, + preOrderId, orderNumber, - pointsUsed: totalPoints - } - }); + totalAmount, + totalPoints, + totalRongdou, + paymentMethods: uniquePaymentMethods + } + }); } catch (error) { await db.query('ROLLBACK'); - console.error('创建订单失败:', error); - res.status(500).json({ success: false, message: '创建订单失败' }); + console.error('创建预订单失败:', error); + res.status(500).json({ success: false, message: '创建预订单失败' }); } }); @@ -520,51 +763,51 @@ router.post('/', auth, async (req, res) => { router.put('/:id/cancel', auth, async (req, res) => { const db = getDB(); await db.query('START TRANSACTION'); - + try { - + const orderId = req.params.id; const userId = req.user.id; - + // 检查订单是否存在且属于当前用户 const [orders] = await db.execute( 'SELECT id, user_id, total_points, status FROM orders WHERE id = ? AND user_id = ?', [orderId, userId] ); - + if (orders.length === 0) { await db.query('ROLLBACK'); return res.status(404).json({ success: false, message: '订单不存在' }); } - + const order = orders[0]; - + if (order.status !== 'pending') { await db.query('ROLLBACK'); return res.status(400).json({ success: false, message: '只能取消待处理的订单' }); } - + // 退还用户积分 await db.execute( 'UPDATE users SET points = points + ? WHERE id = ?', [order.total_points, userId] ); - + // 记录积分历史 await db.execute( `INSERT INTO points_history (user_id, amount, type, description, created_at) VALUES (?, ?, 'refund', '订单取消退还积分', NOW())`, [userId, order.total_points] ); - + // 更新订单状态 await db.execute( 'UPDATE orders SET status = "cancelled", updated_at = NOW() WHERE id = ?', [orderId] ); - + await db.query('COMMIT'); - + res.json({ success: true, message: '订单已取消' }); } catch (error) { await db.query('ROLLBACK'); @@ -613,29 +856,29 @@ router.put('/:id/confirm', auth, async (req, res) => { try { const orderId = req.params.id; const userId = req.user.id; - + // 检查订单是否存在且属于当前用户 const [orders] = await getDB().execute( 'SELECT id, status FROM orders WHERE id = ? AND user_id = ?', [orderId, userId] ); - + if (orders.length === 0) { return res.status(404).json({ success: false, message: '订单不存在' }); } - + const order = orders[0]; - + if (order.status !== 'shipped') { return res.status(400).json({ success: false, message: '只能确认已发货的订单' }); } - + // 更新订单状态 await getDB().execute( 'UPDATE orders SET status = "completed", updated_at = NOW() WHERE id = ?', [orderId] ); - + res.json({ success: true, message: '确认收货成功' }); } catch (error) { console.error('确认收货失败:', error); @@ -697,55 +940,72 @@ router.put('/:id/confirm', auth, async (req, res) => { router.put('/:id/status', auth, adminAuth, async (req, res) => { const db = getDB(); await db.query('START TRANSACTION'); - + try { - + const orderId = req.params.id; const { status } = req.body; - + const validStatuses = ['pending', 'shipped', 'completed', 'cancelled']; if (!validStatuses.includes(status)) { await db.query('ROLLBACK'); return res.status(400).json({ success: false, message: '无效的订单状态' }); } - + // 检查订单是否存在 const [orders] = await db.execute( - 'SELECT id, user_id, total_points, status FROM orders WHERE id = ?', + 'SELECT id, user_id, total_points, total_rongdou, status FROM orders WHERE id = ?', [orderId] ); - + if (orders.length === 0) { await db.query('ROLLBACK'); return res.status(404).json({ success: false, message: '订单不存在' }); } - + const order = orders[0]; - - // 如果是取消订单,需要退还积分 - if (status === 'cancelled' && order.status !== 'cancelled') { + + // 如果是取消订单,需要退还积分和融豆 + if (status === 'cancelled' && order.status !== 'cancelled' && order.status !== 'pre_order') { // 退还用户积分 - await db.execute( - 'UPDATE users SET points = points + ? WHERE id = ?', - [order.total_points, order.user_id] - ); - - // 记录积分历史 - await db.execute( - `INSERT INTO points_history (user_id, amount, type, description, created_at) - VALUES (?, ?, 'earn', '订单取消退还积分', NOW())`, - [order.user_id, order.points_cost] - ); + if (order.total_points > 0) { + await db.execute( + 'UPDATE users SET points = points + ? WHERE id = ?', + [order.total_points, order.user_id] + ); + + // 记录积分历史 + await db.execute( + `INSERT INTO points_history (user_id, amount, type, description, created_at) + VALUES (?, ?, 'earn', '订单取消退还积分', NOW())`, + [order.user_id, order.total_points] + ); + } + + // 退还用户融豆 + if (order.total_rongdou > 0) { + await db.execute( + 'UPDATE users SET balance = balance - ? WHERE id = ?', + [order.total_rongdou, order.user_id] + ); + + // 记录融豆历史 + await db.execute( + `INSERT INTO rongdou_history (user_id, amount, type, description, created_at) + VALUES (?, ?, 'earn', '订单取消退还融豆', NOW())`, + [order.user_id, order.total_rongdou] + ); + } } - + // 更新订单状态 await db.execute( 'UPDATE orders SET status = ?, updated_at = NOW() WHERE id = ?', [status, orderId] ); - + await db.query('COMMIT'); - + res.json({ success: true, message: '订单状态已更新' }); } catch (error) { await db.query('ROLLBACK'); @@ -754,6 +1014,420 @@ router.put('/:id/status', auth, adminAuth, async (req, res) => { } }); +/** + * @swagger + * /api/orders/pending-payment/{id}: + * get: + * summary: 获取待支付预订单详情 + * tags: [Orders] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 预订单ID + * responses: + * 200: + * description: 获取预订单详情成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * id: + * type: integer + * order_no: + * type: string + * total_amount: + * type: integer + * total_points: + * type: integer + * total_rongdou: + * type: integer + * status: + * type: string + * created_at: + * type: string + * items: + * type: array + * items: + * type: object + * properties: + * id: + * type: integer + * product_id: + * type: integer + * product_name: + * type: string + * quantity: + * type: integer + * price: + * type: integer + * points_price: + * type: integer + * rongdou_price: + * type: integer + * spec_info: + * type: object + * 401: + * description: 未授权 + * 404: + * description: 预订单不存在 + * 500: + * description: 服务器错误 + */ +router.get('/pending-payment/:id', auth, async (req, res) => { + try { + const preOrderId = req.params.id; + const userId = req.user.id; + + // 获取预订单基本信息 + const [orders] = await getDB().execute( + `SELECT id, order_no, user_id, total_amount, total_points, total_rongdou, + status, created_at FROM orders WHERE id = ? AND user_id = ? AND status = 'pre_order'`, + [preOrderId, userId] + ); + + if (orders.length === 0) { + return res.status(404).json({ success: false, message: '预订单不存在' }); + } + + const order = orders[0]; + + // 获取预订单商品详情 + const [orderItems] = await getDB().execute( + `SELECT + oi.id, oi.product_id, oi.quantity, oi.price, oi.points_price, oi.rongdou_price, + oi.spec_combination_id, + p.name as product_name, p.image_url, p.description, + psc.spec_values as spec_info + FROM order_items oi + LEFT JOIN products p ON oi.product_id = p.id + LEFT JOIN product_spec_combinations psc ON oi.spec_combination_id = psc.id + WHERE oi.order_id = ?`, + [preOrderId] + ); + + // 处理规格信息 + for (const item of orderItems) { + if (item.spec_info) { + try { + item.spec_info = JSON.parse(item.spec_info); + } catch (e) { + item.spec_info = null; + } + } + } + + res.json({ + success: true, + data: { + ...order, + items: orderItems + } + }); + } catch (error) { + console.error('获取预订单详情失败:', error); + res.status(500).json({ success: false, message: '获取预订单详情失败' }); + } +}); + +/** + * @swagger + * /api/orders/confirm-payment: + * post: + * summary: 确认支付订单 + * description: | + * 根据商品支付方式确认订单支付: + * - 仅积分支付:按10000积分=1融豆的比例扣除积分 + * - 仅融豆支付:直接扣除融豆 + * - 组合支付:优先扣除积分(按10000:1转换),不足部分扣除融豆 + * tags: [Orders] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - order_id + * - address_id + * properties: + * order_id: + * type: integer + * description: 订单ID + * example: 123 + * address_id: + * type: integer + * description: 收货地址ID + * example: 456 + * responses: + * 200: + * description: 确认支付成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * example: true + * message: + * type: string + * example: "订单支付成功" + * data: + * type: object + * properties: + * order_id: + * type: integer + * example: 123 + * order_no: + * type: string + * example: "ORD20240101123456" + * 400: + * description: 请求参数错误或余额不足 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * example: false + * message: + * type: string + * enum: ["订单ID和收货地址ID为必填项", "积分不足", "融豆不足", "积分和融豆余额不足", "商品支付方式配置错误"] + * 401: + * description: 未授权 + * 404: + * description: 订单或地址不存在 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * example: false + * message: + * type: string + * enum: ["订单不存在或已处理", "收货地址不存在", "用户不存在"] + * 500: + * description: 服务器错误 + */ +router.post('/confirm-payment', auth, async (req, res) => { + const connection = await getDB().getConnection(); + + try { + await connection.beginTransaction(); + + const { orderId: order_id, addressId: address_id } = req.body; + const userId = req.user.id; + + // 验证必填字段 + if (!order_id || !address_id) { + return res.status(400).json({ success: false, message: '订单ID和收货地址ID为必填项' }); + } + + // 获取订单信息和商品支付方式 + const [orders] = await connection.execute( + `SELECT o.id, o.order_no, o.user_id, o.total_amount, o.total_points, o.total_rongdou, o.status, + GROUP_CONCAT(DISTINCT p.payment_methods) as payment_methods_list + FROM orders o + JOIN order_items oi ON o.id = oi.order_id + JOIN products p ON oi.product_id = p.id + WHERE o.id = ? AND o.user_id = ? AND o.status = 'pre_order' + GROUP BY o.id`, + [order_id, userId] + ); + + if (orders.length === 0) { + await connection.rollback(); + return res.status(404).json({ success: false, message: '订单不存在或已处理' }); + } + + const order = orders[0]; + + // 解析支付方式 + let allPaymentMethods = []; + console.log(typeof order.payment_methods_list); + + if (order.payment_methods_list) { + try { + // 数据库中存储的是序列化的JSON字符串,直接解析 + + allPaymentMethods = JSON.parse(JSON.parse(order.payment_methods_list)); + } catch (e) { + console.error('解析支付方式失败:', e, 'raw data:', order.payment_methods_list); + allPaymentMethods = []; + } + } + + // 去重支付方式 + allPaymentMethods = [...new Set(allPaymentMethods)]; + + // 判断支付方式类型 + const hasPoints = allPaymentMethods.includes('points') || allPaymentMethods.includes('points_rongdou'); + const hasRongdou = allPaymentMethods.includes('rongdou') || allPaymentMethods.includes('points_rongdou'); + const isComboPayment = allPaymentMethods.includes('points_rongdou'); + + console.log('订单支付方式:', allPaymentMethods, { hasPoints, hasRongdou, isComboPayment }); + + // 获取收货地址信息 + const [addresses] = await connection.execute( + 'SELECT id, receiver_name, receiver_phone, province, city, district, detailed_address as detail_address FROM user_addresses WHERE id = ? AND user_id = ?', + [address_id, userId] + ); + + if (addresses.length === 0) { + await connection.rollback(); + return res.status(404).json({ success: false, message: '收货地址不存在' }); + } + + const address = addresses[0]; + + // 获取用户当前积分和融豆 + const [users] = await connection.execute( + 'SELECT points, balance FROM users WHERE id = ?', + [userId] + ); + + if (users.length === 0) { + await connection.rollback(); + return res.status(404).json({ success: false, message: '用户不存在' }); + } + + const user = users[0]; + if (user.balance > 0) { + return res.status(400).json({ success: false, message: '融豆不足' }); + } + user.balance = Math.abs(user.balance); + + // 根据支付方式处理扣费逻辑 + let totalRongdouNeeded = order.total_rongdou; // 需要的融豆总数 + let pointsToDeduct = 0; // 需要扣除的积分 + let rongdouToDeduct = 0; // 需要扣除的融豆 + + if (!hasRongdou && !hasPoints) { + await connection.rollback(); + return res.status(400).json({ success: false, message: '商品支付方式配置错误' }); + } + + if (hasPoints && !hasRongdou) { + // 只支持积分支付,按10000积分=1融豆转换 + const pointsNeeded = totalRongdouNeeded * 10000; + if (user.points < pointsNeeded) { + await connection.rollback(); + return res.status(400).json({ success: false, message: '积分不足' }); + } + pointsToDeduct = pointsNeeded; + rongdouToDeduct = 0; + } else if (!hasPoints && hasRongdou) { + // 只支持融豆支付 + if (user.balance < totalRongdouNeeded) { + await connection.rollback(); + return res.status(400).json({ success: false, message: '融豆不足' }); + } + pointsToDeduct = 0; + rongdouToDeduct = totalRongdouNeeded; + } else if (isComboPayment) { + // 组合支付:先扣积分,不足部分用融豆 + const availablePointsInRongdou = Math.floor(user.points / 10000); // 积分可转换的融豆数 + + if (availablePointsInRongdou >= totalRongdouNeeded) { + // 积分足够支付全部 + pointsToDeduct = totalRongdouNeeded * 10000; + rongdouToDeduct = 0; + } else { + // 积分不够,需要组合支付 + pointsToDeduct = availablePointsInRongdou * 10000; + rongdouToDeduct = totalRongdouNeeded - availablePointsInRongdou; + + if (user.balance < rongdouToDeduct) { + await connection.rollback(); + return res.status(400).json({ success: false, message: '积分和融豆余额不足' }); + } + } + } + + console.log('扣费计算:', { totalRongdouNeeded, pointsToDeduct, rongdouToDeduct, userPoints: user.points, userBalance: user.balance }); + + // 扣除积分 + if (pointsToDeduct > 0) { + await connection.execute( + 'UPDATE users SET points = points - ? WHERE id = ?', + [pointsToDeduct, userId] + ); + + // 记录积分变动历史 + await connection.execute( + `INSERT INTO points_history (user_id, type, amount, description, order_id) + VALUES (?, 'spend', ?, ?, ?)`, + [userId, pointsToDeduct, `订单支付 - ${order.order_no}`, order_id] + ); + } + + // 扣除融豆 + if (rongdouToDeduct > 0) { + await connection.execute( + 'UPDATE users SET balance = balance + ? WHERE id = ?', + [rongdouToDeduct, userId] + ); + + // 记录融豆变动历史 + await connection.execute( + `INSERT INTO rongdou_history (user_id, type, amount, description, order_id) + VALUES (?, 'spend', ?, ?, ?)`, + [userId, rongdouToDeduct, `订单支付 - ${order.order_no}`, order_id] + ); + } + + // 更新订单状态和收货地址 + const addressStr = JSON.stringify({ + recipient_name: address.receiver_name, + phone: address.receiver_phone, + province: address.province, + city: address.city, + district: address.district, + detail_address: address.detail_address + }); + + await connection.execute( + `UPDATE orders SET status = 'pending', address = ?, updated_at = NOW() + WHERE id = ?`, + [addressStr, order_id] + ); + + await connection.commit(); + + res.json({ + success: true, + message: '订单支付成功', + data: { + order_id: order_id, + order_no: order.order_no + } + }); + + } catch (error) { + await connection.rollback(); + console.error('确认支付失败:', error); + res.status(500).json({ success: false, message: '确认支付失败' }); + } finally { + connection.release(); + } +}); + /** * @swagger * /api/orders/stats: @@ -804,23 +1478,23 @@ router.get('/stats', auth, adminAuth, async (req, res) => { try { // 总订单数 const [totalOrders] = await getDB().execute('SELECT COUNT(*) as count FROM orders'); - + // 待发货订单数 const [pendingOrders] = await getDB().execute('SELECT COUNT(*) as count FROM orders WHERE status = "pending"'); - + // 已完成订单数 const [completedOrders] = await getDB().execute('SELECT COUNT(*) as count FROM orders WHERE status = "completed"'); - + // 本月新增订单 const [monthOrders] = await getDB().execute( 'SELECT COUNT(*) as count FROM orders WHERE YEAR(created_at) = YEAR(NOW()) AND MONTH(created_at) = MONTH(NOW())' ); - + // 上月订单数(用于计算增长率) const [lastMonthOrders] = await getDB().execute( 'SELECT COUNT(*) as count FROM orders WHERE YEAR(created_at) = YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND MONTH(created_at) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH))' ); - + // 计算月增长率 const lastMonthCount = lastMonthOrders[0].count; const currentMonthCount = monthOrders[0].count; @@ -828,10 +1502,10 @@ router.get('/stats', auth, adminAuth, async (req, res) => { if (lastMonthCount > 0) { monthGrowthRate = ((currentMonthCount - lastMonthCount) / lastMonthCount * 100).toFixed(1); } - + // 总积分消费 const [totalPointsConsumed] = await getDB().execute('SELECT SUM(points_cost) as total FROM orders WHERE status != "cancelled"'); - + res.json({ success: true, data: { @@ -849,4 +1523,5 @@ router.get('/stats', auth, adminAuth, async (req, res) => { } }); + module.exports = router; \ No newline at end of file diff --git a/routes/products.js b/routes/products.js index d63aa5b..e83202f 100644 --- a/routes/products.js +++ b/routes/products.js @@ -4,123 +4,7 @@ const { auth, adminAuth } = require('../middleware/auth'); const router = express.Router(); -/** - * @swagger - * tags: - * name: Products - * description: 商品管理API - */ - -/** - * @swagger - * components: - * schemas: - * Product: - * type: object - * required: - * - name - * - points_price - * - stock - * properties: - * id: - * type: integer - * description: 商品ID - * name: - * type: string - * description: 商品名称 - * category: - * type: string - * description: 商品分类 - * points_price: - * type: integer - * description: 积分价格 - * stock: - * type: integer - * description: 库存数量 - * image_url: - * type: string - * description: 商品图片URL - * description: - * type: string - * description: 商品描述 - * status: - * type: string - * description: 商品状态 - * enum: [active, inactive] - * created_at: - * type: string - * format: date-time - * description: 创建时间 - * updated_at: - * type: string - * format: date-time - * description: 更新时间 - */ - -/** - * @swagger - * /products: - * get: - * summary: 获取商品列表 - * tags: [Products] - * parameters: - * - in: query - * name: page - * schema: - * type: integer - * default: 1 - * description: 页码 - * - in: query - * name: limit - * schema: - * type: integer - * default: 10 - * description: 每页数量 - * - in: query - * name: search - * schema: - * type: string - * description: 搜索关键词 - * - in: query - * name: category - * schema: - * type: string - * description: 商品分类 - * - in: query - * name: status - * schema: - * type: string - * enum: [active, inactive] - * description: 商品状态 - * responses: - * 200: - * description: 成功获取商品列表 - * content: - * application/json: - * schema: - * type: object - * properties: - * success: - * type: boolean - * data: - * type: object - * properties: - * products: - * type: array - * items: - * $ref: '#/components/schemas/Product' - * pagination: - * type: object - * properties: - * page: - * type: integer - * limit: - * type: integer - * total: - * type: integer - * pages: - * type: integer - */ +// 商品管理路由 router.get('/', async (req, res) => { try { const { page = 1, limit = 10, search = '', category = '', status = '' } = req.query; @@ -159,7 +43,7 @@ router.get('/', async (req, res) => { // 获取商品列表 const query = ` - SELECT id, name, category, points_price as points, stock, image_url as image, description, status, created_at, updated_at + SELECT id, name, rongdou_price, category, points_price as points, stock, image_url as image, description, status, payment_methods, created_at, updated_at FROM products ${whereClause} ORDER BY created_at DESC @@ -170,7 +54,10 @@ router.get('/', async (req, res) => { const queryParams = [...params]; console.log('Query params:', queryParams, 'Query:', query); const [products] = await getDB().execute(query, queryParams); - + products.forEach(item=>{ + item.payment_methods = JSON.parse(item.payment_methods) + }) + console.log('查询结果:', products); res.json({ success: true, data: { @@ -189,30 +76,7 @@ router.get('/', async (req, res) => { } }); -/** - * @swagger - * /products/categories: - * get: - * summary: 获取商品分类列表 - * tags: [Products] - * responses: - * 200: - * description: 成功获取商品分类列表 - * content: - * application/json: - * schema: - * type: object - * properties: - * success: - * type: boolean - * data: - * type: object - * properties: - * categories: - * type: array - * items: - * type: string - */ +// 获取商品分类列表 router.get('/categories', async (req, res) => { try { const [categories] = await getDB().execute( @@ -231,11 +95,111 @@ router.get('/categories', async (req, res) => { } }); +// 获取热销商品 +router.get('/hot', async (req, res) => { + try { + // 从活跃商品中随机获取2个商品 + const [products] = await getDB().execute( + `SELECT id, name, category, price, points_price, rongdou_price, stock, + image_url, images, description, shop_name, shop_avatar, + payment_methods, sales, rating, status, created_at, updated_at + FROM products + WHERE status = 'active' AND stock > 0 + ORDER BY RAND() + LIMIT 2` + ); + + // 格式化商品数据 + const formattedProducts = products.map(product => ({ + ...product, + images: product.images ? JSON.parse(product.images) : (product.image_url ? [product.image_url] : []), + payment_methods: product.payment_methods ? JSON.parse(product.payment_methods) : ['points'], + // 保持向后兼容 + points: product.points_price, + image: product.image_url + })); + + res.json({ + success: true, + data: { + products: formattedProducts + } + }); + } catch (error) { + console.error('获取热销商品失败:', error); + res.status(500).json({ success: false, message: '获取热销商品失败' }); + } +}); + +/** + * @swagger + * /products/flash-sale: + * get: + * summary: 获取秒杀商品 + * tags: [Products] + * responses: + * 200: + * description: 成功获取秒杀商品 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * products: + * type: array + * items: + * $ref: '#/components/schemas/Product' + */ +router.get('/cheap', async (req, res) => { + try { + // 从活跃商品中随机获取2个商品作为秒杀商品 + const [products] = await getDB().execute( + `SELECT id, name, category, price, points_price, rongdou_price, stock, + image_url, images, description, shop_name, shop_avatar, + payment_methods, sales, rating, status, created_at, updated_at + FROM products + WHERE status = 'active' AND stock > 0 + ORDER BY RAND() + LIMIT 2` + ); + + // 格式化商品数据,为秒杀商品添加特殊标识 + const formattedProducts = products.map(product => ({ + ...product, + images: product.images ? JSON.parse(product.images) : (product.image_url ? [product.image_url] : []), + payment_methods: product.payment_methods ? JSON.parse(product.payment_methods) : ['points'], + // 秒杀商品特殊处理:价格打8折 + flash_sale_price: Math.floor(product.price * 0.8), + flash_sale_points: Math.floor(product.points_price * 0.8), + flash_sale_rongdou: Math.floor(product.rongdou_price * 0.8), + is_flash_sale: true, + // 保持向后兼容 + points: product.points_price, + image: product.image_url + })); + + res.json({ + success: true, + data: { + products: formattedProducts + } + }); + } catch (error) { + console.error('获取秒杀商品失败:', error); + res.status(500).json({ success: false, message: '获取秒杀商品失败' }); + } +}); + /** * @swagger * /products/{id}: * get: - * summary: 获取单个商品详情 + * summary: 获取单个商品详情(包含增强规格信息) * tags: [Products] * parameters: * - in: path @@ -246,7 +210,7 @@ router.get('/categories', async (req, res) => { * description: 商品ID * responses: * 200: - * description: 成功获取商品详情 + * description: 成功获取商品详情,包含完整的规格信息 * content: * application/json: * schema: @@ -254,8 +218,116 @@ router.get('/categories', async (req, res) => { * properties: * success: * type: boolean + * example: true * data: - * $ref: '#/components/schemas/Product' + * type: object + * properties: + * product: + * type: object + * properties: + * id: + * type: integer + * name: + * type: string + * category: + * type: string + * price: + * type: number + * points_price: + * type: number + * rongdou_price: + * type: number + * stock: + * type: integer + * specifications: + * type: array + * description: 商品规格组合列表(笛卡尔积规格系统) + * items: + * type: object + * properties: + * id: + * type: integer + * description: 规格组合ID + * combination_key: + * type: string + * description: 规格组合键(如:1-3-5) + * spec_display: + * type: string + * description: 规格显示文本(如:颜色:红色 | 尺寸:XL) + * spec_details: + * type: array + * description: 规格详细信息 + * items: + * type: object + * properties: + * id: + * type: integer + * spec_name: + * type: string + * description: 规格名称 + * spec_display_name: + * type: string + * description: 规格显示名称 + * value: + * type: string + * description: 规格值 + * display_value: + * type: string + * description: 规格显示值 + * color_code: + * type: string + * description: 颜色代码 + * image_url: + * type: string + * description: 规格图片 + * price_adjustment: + * type: number + * description: 价格调整 + * points_adjustment: + * type: number + * description: 积分调整 + * rongdou_adjustment: + * type: number + * description: 融豆调整 + * stock: + * type: integer + * description: 规格库存 + * sku_code: + * type: string + * description: SKU编码 + * barcode: + * type: string + * description: 条形码 + * weight: + * type: number + * description: 重量 + * volume: + * type: number + * description: 体积 + * actual_price: + * type: number + * description: 实际价格(基础价格+调整) + * actual_points_price: + * type: number + * description: 实际积分价格 + * actual_rongdou_price: + * type: number + * description: 实际融豆价格 + * is_available: + * type: boolean + * description: 是否有库存 + * specification_count: + * type: integer + * description: 规格总数 + * available_specifications: + * type: integer + * description: 有库存的规格数量 + * attributes: + * type: array + * description: 商品属性 + * isFavorited: + * type: boolean + * description: 是否已收藏 * 404: * description: 商品不存在 */ @@ -280,12 +352,92 @@ router.get('/:id', async (req, res) => { const product = products[0]; - // 获取商品规格 - const [specifications] = await getDB().execute( - 'SELECT * FROM product_specifications WHERE product_id = ? ORDER BY id', + // 获取商品的规格组合(新的笛卡尔积规格系统) + const [specCombinations] = await getDB().execute( + `SELECT psc.*, + GROUP_CONCAT(CONCAT(sn.display_name, ':', sv.display_value) ORDER BY sn.sort_order SEPARATOR ' | ') as spec_display + FROM product_spec_combinations psc + LEFT JOIN JSON_TABLE(psc.spec_values, '$[*]' COLUMNS (spec_value_id INT PATH '$')) jt ON TRUE + LEFT JOIN spec_values sv ON jt.spec_value_id = sv.id + LEFT JOIN spec_names sn ON sv.spec_name_id = sn.id + WHERE psc.product_id = ? AND psc.status = 'active' + GROUP BY psc.id + ORDER BY psc.combination_key`, [id] ); + // 为每个规格组合获取详细的规格值信息 + const enhancedSpecifications = []; + for (const combination of specCombinations) { + // 智能解析 spec_values 字段,兼容多种数据格式 + let specValueIds = []; + try { + if (combination.spec_values) { + // 如果是 Buffer 对象,先转换为字符串 + let specValuesStr = combination.spec_values; + if (Buffer.isBuffer(specValuesStr)) { + specValuesStr = specValuesStr.toString('utf8'); + } + + // 尝试 JSON 解析 + if (typeof specValuesStr === 'string') { + specValuesStr = specValuesStr.trim(); + if (specValuesStr.startsWith('[') && specValuesStr.endsWith(']')) { + // JSON 数组格式 + specValueIds = JSON.parse(specValuesStr); + } else if (specValuesStr.includes(',')) { + // 逗号分隔的字符串格式 + specValueIds = specValuesStr.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id)); + } else if (specValuesStr && !isNaN(parseInt(specValuesStr))) { + // 单个数字 + specValueIds = [parseInt(specValuesStr)]; + } + } else if (Array.isArray(specValuesStr)) { + // 已经是数组 + specValueIds = specValuesStr; + } + } + } catch (parseError) { + console.warn(`解析规格值失败 (combination_id: ${combination.id}):`, parseError.message); + specValueIds = []; + } + + // 获取规格值详情 + if (specValueIds && specValueIds.length > 0) { + const placeholders = specValueIds.map(() => '?').join(','); + const [specDetails] = await getDB().execute( + `SELECT sv.*, sn.name as spec_name, sn.display_name as spec_display_name + FROM spec_values sv + LEFT JOIN spec_names sn ON sv.spec_name_id = sn.id + WHERE sv.id IN (${placeholders}) + ORDER BY sn.sort_order, sv.sort_order`, + specValueIds + ); + + enhancedSpecifications.push({ + id: combination.id, + combination_key: combination.combination_key, + spec_display: combination.spec_display, + spec_details: specDetails, + price_adjustment: combination.price_adjustment || 0, + points_adjustment: combination.points_adjustment || 0, + rongdou_adjustment: combination.rongdou_adjustment || 0, + stock: combination.stock, + sku_code: combination.sku_code, + barcode: combination.barcode, + weight: combination.weight, + volume: combination.volume, + actual_price: product.price + (combination.price_adjustment || 0), + actual_points_price: product.points_price + (combination.points_adjustment || 0), + actual_rongdou_price: product.rongdou_price + (combination.rongdou_adjustment || 0), + is_available: combination.stock > 0, + status: combination.status, + created_at: combination.created_at, + updated_at: combination.updated_at + }); + } + } + // 获取商品属性 const [attributes] = await getDB().execute( 'SELECT * FROM product_attributes WHERE product_id = ? ORDER BY sort_order, id', @@ -305,12 +457,72 @@ router.get('/:id', async (req, res) => { // 构建增强的商品数据 const enhancedProduct = { ...product, - images: product.images ? JSON.parse(product.images) : (product.image_url ? [product.image_url] : []), - videos: product.videos ? JSON.parse(product.videos) : [], - payment_methods: product.payment_methods ? JSON.parse(product.payment_methods) : ['points'], - specifications, + images: (() => { + try { + if (product.images) { + let imagesStr = product.images; + if (Buffer.isBuffer(imagesStr)) { + imagesStr = imagesStr.toString('utf8'); + } + if (typeof imagesStr === 'string') { + imagesStr = imagesStr.trim(); + if (imagesStr.startsWith('[') && imagesStr.endsWith(']')) { + return JSON.parse(imagesStr); + } + } + } + return product.image_url ? [product.image_url] : []; + } catch (e) { + console.warn('解析商品图片失败:', e.message); + return product.image_url ? [product.image_url] : []; + } + })(), + videos: (() => { + try { + if (product.videos) { + let videosStr = product.videos; + if (Buffer.isBuffer(videosStr)) { + videosStr = videosStr.toString('utf8'); + } + if (typeof videosStr === 'string') { + videosStr = videosStr.trim(); + if (videosStr.startsWith('[') && videosStr.endsWith(']')) { + return JSON.parse(videosStr); + } + } + } + return []; + } catch (e) { + console.warn('解析商品视频失败:', e.message); + return []; + } + })(), + payment_methods: (() => { + try { + if (product.payment_methods) { + let methodsStr = product.payment_methods; + if (Buffer.isBuffer(methodsStr)) { + methodsStr = methodsStr.toString('utf8'); + } + if (typeof methodsStr === 'string') { + methodsStr = methodsStr.trim(); + if (methodsStr.startsWith('[') && methodsStr.endsWith(']')) { + return JSON.parse(methodsStr); + } + } + } + return ['points']; + } catch (e) { + console.warn('解析支付方式失败:', e.message); + return ['points']; + } + })(), + specifications: enhancedSpecifications, attributes, isFavorited, + // 规格统计信息 + specification_count: enhancedSpecifications.length, + available_specifications: enhancedSpecifications.filter(spec => spec.is_available).length, // 保持向后兼容 points: product.points_price, image: product.image_url, @@ -352,19 +564,7 @@ router.post('/', auth, adminAuth, async (req, res) => { const productId = result.insertId; - // 添加商品规格 - if (specifications && specifications.length > 0) { - for (const spec of specifications) { - await getDB().execute( - `INSERT INTO product_specifications (product_id, spec_name, spec_value, price_adjustment, - points_adjustment, rongdou_adjustment, stock, sku_code) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [productId, spec.name, spec.value, spec.price_adjustment || 0, - spec.points_adjustment || 0, spec.rongdou_adjustment || 0, - spec.stock || 0, spec.sku_code || null] - ); - } - } + // 添加商品属性 if (attributes && attributes.length > 0) { @@ -499,25 +699,7 @@ router.put('/:id', auth, adminAuth, async (req, res) => { updateValues ); - // 更新商品规格 - if (specifications !== undefined) { - // 删除原有规格 - await getDB().execute('DELETE FROM product_specifications WHERE product_id = ?', [productId]); - - // 添加新规格 - if (specifications && specifications.length > 0) { - for (const spec of specifications) { - await getDB().execute( - `INSERT INTO product_specifications (product_id, spec_name, spec_value, price_adjustment, - points_adjustment, rongdou_adjustment, stock, sku_code) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [productId, spec.name, spec.value, spec.price_adjustment || 0, - spec.points_adjustment || 0, spec.rongdou_adjustment || 0, - spec.stock || 0, spec.sku_code || null] - ); - } - } - } + // 更新商品属性 if (attributes !== undefined) { @@ -655,8 +837,8 @@ router.get('/:id/reviews', async (req, res) => { JOIN users u ON pr.user_id = u.id WHERE pr.product_id = ? ORDER BY pr.created_at DESC - LIMIT ? OFFSET ?`, - [id, limit, offset] + LIMIT ${limit} OFFSET ${offset}`, + [id] ); // 获取评论总数 @@ -832,8 +1014,8 @@ router.get('/favorites', auth, async (req, res) => { JOIN products p ON pf.product_id = p.id WHERE pf.user_id = ? AND p.status = 'active' ORDER BY pf.created_at DESC - LIMIT ? OFFSET ?`, - [userId, limit, offset] + LIMIT ${limit} OFFSET ${offset}`, + [userId] ); const [countResult] = await getDB().execute( @@ -867,163 +1049,13 @@ router.get('/favorites', auth, async (req, res) => { } }); -// 获取商品规格 -router.get('/:id/specifications', async (req, res) => { - try { - const productId = req.params.id; - - const [specifications] = await getDB().execute( - 'SELECT id, spec_name as name, spec_value as value, price_adjustment, points_adjustment, rongdou_adjustment, stock, sku_code, created_at, updated_at FROM product_specifications WHERE product_id = ? ORDER BY id', - [productId] - ); - - res.json({ - success: true, - data: specifications - }); - } catch (error) { - console.error('获取商品规格错误:', error); - res.status(500).json({ message: '获取商品规格失败' }); - } -}); -// 创建商品规格(管理员权限) -router.post('/:id/specifications', auth, adminAuth, async (req, res) => { - try { - const productId = req.params.id; - const { name, value, price_adjustment = 0, points_adjustment = 0, rongdou_adjustment = 0, stock = 0, sku_code } = req.body; - - if (!name || !value) { - return res.status(400).json({ message: '规格名称和规格值不能为空' }); - } - - // 检查商品是否存在 - const [products] = await getDB().execute('SELECT id FROM products WHERE id = ?', [productId]); - if (products.length === 0) { - return res.status(404).json({ message: '商品不存在' }); - } - - const [result] = await getDB().execute( - `INSERT INTO product_specifications (product_id, spec_name, spec_value, price_adjustment, - points_adjustment, rongdou_adjustment, stock, sku_code, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`, - [productId, name, value, price_adjustment, points_adjustment, rongdou_adjustment, stock, sku_code || null] - ); - - res.status(201).json({ - success: true, - message: '规格创建成功', - data: { id: result.insertId } - }); - } catch (error) { - console.error('创建商品规格错误:', error); - res.status(500).json({ message: '创建商品规格失败' }); - } -}); -// 更新商品规格(管理员权限) -router.put('/:id/specifications/:specId', auth, adminAuth, async (req, res) => { - try { - const { id: productId, specId } = req.params; - const { name, value, price_adjustment, points_adjustment, rongdou_adjustment, stock, sku_code } = req.body; - - // 检查规格是否存在 - const [specs] = await getDB().execute( - 'SELECT id FROM product_specifications WHERE id = ? AND product_id = ?', - [specId, productId] - ); - - if (specs.length === 0) { - return res.status(404).json({ message: '规格不存在' }); - } - - // 构建更新字段 - const updateFields = []; - const updateValues = []; - - if (name !== undefined) { - updateFields.push('spec_name = ?'); - updateValues.push(name); - } - - if (value !== undefined) { - updateFields.push('spec_value = ?'); - updateValues.push(value); - } - - if (price_adjustment !== undefined) { - updateFields.push('price_adjustment = ?'); - updateValues.push(price_adjustment); - } - - if (points_adjustment !== undefined) { - updateFields.push('points_adjustment = ?'); - updateValues.push(points_adjustment); - } - - if (rongdou_adjustment !== undefined) { - updateFields.push('rongdou_adjustment = ?'); - updateValues.push(rongdou_adjustment); - } - - if (stock !== undefined) { - updateFields.push('stock = ?'); - updateValues.push(stock); - } - - if (sku_code !== undefined) { - updateFields.push('sku_code = ?'); - updateValues.push(sku_code); - } - - if (updateFields.length === 0) { - return res.status(400).json({ message: '没有提供要更新的字段' }); - } - - updateFields.push('updated_at = NOW()'); - updateValues.push(specId); - - await getDB().execute( - `UPDATE product_specifications SET ${updateFields.join(', ')} WHERE id = ?`, - updateValues - ); - - res.json({ - success: true, - message: '规格更新成功' - }); - } catch (error) { - console.error('更新商品规格错误:', error); - res.status(500).json({ message: '更新商品规格失败' }); - } -}); -// 删除商品规格(管理员权限) -router.delete('/:id/specifications/:specId', auth, adminAuth, async (req, res) => { - try { - const { id: productId, specId } = req.params; - - // 检查规格是否存在 - const [specs] = await getDB().execute( - 'SELECT id FROM product_specifications WHERE id = ? AND product_id = ?', - [specId, productId] - ); - - if (specs.length === 0) { - return res.status(404).json({ message: '规格不存在' }); - } - - await getDB().execute('DELETE FROM product_specifications WHERE id = ?', [specId]); - - res.json({ - success: true, - message: '规格删除成功' - }); - } catch (error) { - console.error('删除商品规格错误:', error); - res.status(500).json({ message: '删除商品规格失败' }); - } -}); + + + + // 获取商品属性 router.get('/:id/attributes', async (req, res) => { diff --git a/routes/regions.js b/routes/regions.js index bd6d132..112f8d3 100644 --- a/routes/regions.js +++ b/routes/regions.js @@ -120,12 +120,37 @@ router.get('/zhejiang', async (req, res) => { */ router.get('/provinces', async (req, res) => { try { + // 递归获取子区域的函数 + async function getChildrenRecursively(parentCode, level) { + const [children] = await getDB().execute( + `SELECT code, name as label, level FROM china_regions + WHERE parent_code = ? AND level = ? + ORDER BY code`, + [parentCode, level] + ); + + // 为每个子区域递归获取其子区域 + for (let child of children) { + if (level < 3) { // 最多到区县级别(level 3) + child.children = await getChildrenRecursively(child.code, level + 1); + } + } + + return children; + } + + // 获取所有省份 const [provinces] = await getDB().execute( - `SELECT code, name FROM china_regions + `SELECT code, name as label, level FROM china_regions WHERE level = 1 ORDER BY code` ); + // 为每个省份递归获取城市和区县 + for (let province of provinces) { + province.children = await getChildrenRecursively(province.code, 2); + } + res.json({ success: true, data: provinces diff --git a/routes/specifications.js b/routes/specifications.js new file mode 100644 index 0000000..3563d10 --- /dev/null +++ b/routes/specifications.js @@ -0,0 +1,1096 @@ +const express = require('express'); +const router = express.Router(); +const { getDB } = require('../database'); +const { auth, adminAuth } = require('../middleware/auth'); + +/** + * @swagger + * components: + * schemas: + * SpecName: + * type: object + * properties: + * id: + * type: integer + * name: + * type: string + * description: 规格名称(如:颜色、尺寸) + * display_name: + * type: string + * description: 显示名称 + * sort_order: + * type: integer + * description: 排序 + * status: + * type: string + * enum: [active, inactive] + * SpecValue: + * type: object + * properties: + * id: + * type: integer + * spec_name_id: + * type: integer + * value: + * type: string + * description: 规格值(如:红色、XL) + * display_value: + * type: string + * color_code: + * type: string + * description: 颜色代码 + * image_url: + * type: string + * sort_order: + * type: integer + * status: + * type: string + * enum: [active, inactive] + */ + +/** + * @swagger + * /specifications/names: + * get: + * summary: 获取所有规格名称 + * tags: [Specifications] + * parameters: + * - in: query + * name: status + * schema: + * type: string + * enum: [active, inactive] + * description: 状态筛选 + * responses: + * 200: + * description: 成功获取规格名称列表 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: array + * items: + * $ref: '#/components/schemas/SpecName' + */ +router.get('/names', async (req, res) => { + try { + const { status = 'active' } = req.query; + + let query = 'SELECT * FROM spec_names'; + const params = []; + + if (status) { + query += ' WHERE status = ?'; + params.push(status); + } + + query += ' ORDER BY sort_order, id'; + + const [specNames] = await getDB().execute(query, params); + + res.json({ + success: true, + data: specNames + }); + } catch (error) { + console.error('获取规格名称失败:', error); + res.status(500).json({ success: false, message: '获取规格名称失败' }); + } +}); + +/** + * @swagger + * /specifications/names: + * post: + * summary: 创建规格名称 + * tags: [Specifications] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - name + * - display_name + * properties: + * name: + * type: string + * description: 规格名称 + * display_name: + * type: string + * description: 显示名称 + * sort_order: + * type: integer + * default: 0 + * responses: + * 201: + * description: 规格名称创建成功 + */ +router.post('/names', auth, adminAuth, async (req, res) => { + try { + const { name, display_name, sort_order = 0 } = req.body; + + if (!name || !display_name) { + return res.status(400).json({ success: false, message: '规格名称和显示名称不能为空' }); + } + + const [result] = await getDB().execute( + 'INSERT INTO spec_names (name, display_name, sort_order) VALUES (?, ?, ?)', + [name, display_name, sort_order] + ); + + res.status(201).json({ + success: true, + message: '规格名称创建成功', + data: { id: result.insertId } + }); + } catch (error) { + if (error.code === 'ER_DUP_ENTRY') { + return res.status(400).json({ success: false, message: '规格名称已存在' }); + } + console.error('创建规格名称失败:', error); + res.status(500).json({ success: false, message: '创建规格名称失败' }); + } +}); + +/** + * @swagger + * /specifications/names/{id}: + * delete: + * summary: 删除规格名称 + * tags: [Specifications] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 规格名称ID + * responses: + * 200: + * description: 删除成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * example: 规格名称删除成功 + * 400: + * description: 该规格名称下还有规格值,无法删除 + * 404: + * description: 规格名称不存在 + * 500: + * description: 服务器错误 + */ +router.delete('/names/:id', auth, adminAuth, async (req, res) => { + try { + const { id } = req.params; + + // 检查规格名称是否存在 + const [existingName] = await getDB().execute( + 'SELECT id FROM spec_names WHERE id = ?', + [id] + ); + + if (existingName.length === 0) { + return res.status(404).json({ success: false, message: '规格名称不存在' }); + } + + // 检查该规格名称下是否还有规格值 + const [specValues] = await getDB().execute( + 'SELECT COUNT(*) as count FROM spec_values WHERE spec_name_id = ?', + [id] + ); + + if (specValues[0].count > 0) { + return res.status(400).json({ success: false, message: '该规格名称下还有规格值,请先删除所有规格值' }); + } + + // 删除规格名称 + await getDB().execute( + 'DELETE FROM spec_names WHERE id = ?', + [id] + ); + + res.json({ + success: true, + message: '规格名称删除成功' + }); + } catch (error) { + console.error('删除规格名称失败:', error); + res.status(500).json({ success: false, message: '删除规格名称失败' }); + } +}); + +/** + * @swagger + * /specifications/values: + * get: + * summary: 获取规格值列表 + * tags: [Specifications] + * parameters: + * - in: query + * name: spec_name_id + * schema: + * type: integer + * description: 规格名称ID + * - in: query + * name: status + * schema: + * type: string + * enum: [active, inactive] + * description: 状态筛选 + * responses: + * 200: + * description: 成功获取规格值列表 + */ +router.get('/values', async (req, res) => { + try { + const { spec_name_id, status = 'active' } = req.query; + + let query = ` + SELECT sv.*, sn.name as spec_name, sn.display_name as spec_display_name + FROM spec_values sv + LEFT JOIN spec_names sn ON sv.spec_name_id = sn.id + WHERE 1=1 + `; + const params = []; + + if (spec_name_id) { + query += ' AND sv.spec_name_id = ?'; + params.push(spec_name_id); + } + + if (status) { + query += ' AND sv.status = ?'; + params.push(status); + } + + query += ' ORDER BY sv.spec_name_id, sv.sort_order, sv.id'; + + const [specValues] = await getDB().execute(query, params); + + res.json({ + success: true, + data: specValues + }); + } catch (error) { + console.error('获取规格值失败:', error); + res.status(500).json({ success: false, message: '获取规格值失败' }); + } +}); + +/** + * @swagger + * /specifications/values: + * post: + * summary: 创建规格值 + * tags: [Specifications] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - spec_name_id + * - value + * - display_value + * properties: + * spec_name_id: + * type: integer + * value: + * type: string + * display_value: + * type: string + * color_code: + * type: string + * image_url: + * type: string + * sort_order: + * type: integer + * default: 0 + * responses: + * 201: + * description: 规格值创建成功 + */ +router.post('/values', auth, adminAuth, async (req, res) => { + try { + const { spec_name_id, value, display_value, color_code, image_url, sort_order = 0 } = req.body; + + if (!spec_name_id || !value || !display_value) { + return res.status(400).json({ success: false, message: '规格名称ID、规格值和显示值不能为空' }); + } + + const [result] = await getDB().execute( + `INSERT INTO spec_values (spec_name_id, value, display_value, color_code, image_url, sort_order) + VALUES (?, ?, ?, ?, ?, ?)`, + [spec_name_id, value, display_value, color_code || null, image_url || null, sort_order] + ); + + res.status(201).json({ + success: true, + message: '规格值创建成功', + data: { id: result.insertId } + }); + } catch (error) { + if (error.code === 'ER_DUP_ENTRY') { + return res.status(400).json({ success: false, message: '该规格名称下的规格值已存在' }); + } + console.error('创建规格值失败:', error); + res.status(500).json({ success: false, message: '创建规格值失败' }); + } +}); + +/** + * @swagger + * /specifications/combinations/{productId}: + * get: + * summary: 获取商品的规格组合 + * tags: [Specifications] + * parameters: + * - in: path + * name: productId + * required: true + * schema: + * type: integer + * description: 商品ID + * - in: query + * name: status + * schema: + * type: string + * enum: [active, inactive] + * description: 状态筛选 + * responses: + * 200: + * description: 成功获取规格组合 + */ +router.get('/combinations/:productId', async (req, res) => { + try { + const { productId } = req.params; + const { status = 'active' } = req.query; + + // 获取商品的规格组合 + let query = ` + SELECT psc.*, p.name as product_name, p.price as base_price, + p.points_price as base_points_price, p.rongdou_price as base_rongdou_price + FROM product_spec_combinations psc + LEFT JOIN products p ON psc.product_id = p.id + WHERE psc.product_id = ? + `; + const params = [productId]; + + if (status) { + query += ' AND psc.status = ?'; + params.push(status); + } + + query += ' ORDER BY psc.combination_key'; + + const [combinations] = await getDB().execute(query, params); + + // 为每个组合获取详细的规格值信息 + for (let combination of combinations) { + let specValueIds; + try { + // 处理不同的数据格式 + if (!combination.spec_values) { + specValueIds = []; + } else if (typeof combination.spec_values === 'string') { + // 如果是字符串,尝试JSON解析,失败则按逗号分隔处理 + try { + specValueIds = JSON.parse(combination.spec_values); + } catch { + // 按逗号分隔的字符串处理 + specValueIds = combination.spec_values.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id)); + } + } else if (Buffer.isBuffer(combination.spec_values)) { + // 如果是Buffer,转换为字符串后处理 + const strValue = combination.spec_values.toString(); + try { + specValueIds = JSON.parse(strValue); + } catch { + specValueIds = strValue.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id)); + } + } else { + // 其他情况,尝试直接使用 + specValueIds = Array.isArray(combination.spec_values) ? combination.spec_values : []; + } + } catch (error) { + console.error('解析规格值失败:', combination.spec_values, error); + specValueIds = []; + } + + if (specValueIds && specValueIds.length > 0) { + const placeholders = specValueIds.map(() => '?').join(','); + const [specDetails] = await getDB().execute( + `SELECT sv.*, sn.name as spec_name, sn.display_name as spec_display_name + FROM spec_values sv + LEFT JOIN spec_names sn ON sv.spec_name_id = sn.id + WHERE sv.id IN (${placeholders}) + ORDER BY sn.sort_order, sv.sort_order`, + specValueIds + ); + + combination.spec_details = specDetails; + } else { + combination.spec_details = []; + } + + // 计算实际价格 + combination.actual_price = combination.base_price + (combination.price_adjustment || 0); + combination.actual_points_price = combination.base_points_price + (combination.points_adjustment || 0); + combination.actual_rongdou_price = combination.base_rongdou_price + (combination.rongdou_adjustment || 0); + combination.is_available = combination.stock > 0; + } + + res.json({ + success: true, + data: combinations + }); + } catch (error) { + console.error('获取规格组合失败:', error); + res.status(500).json({ success: false, message: '获取规格组合失败' }); + } +}); + +/** + * @swagger + * /specifications/combinations/{id}: + * get: + * summary: 获取单个规格组合详情 + * tags: [Specifications] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 规格组合ID + * responses: + * 200: + * description: 成功获取规格组合详情 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * data: + * type: object + * properties: + * id: + * type: integer + * product_id: + * type: integer + * combination_key: + * type: string + * spec_values: + * type: array + * items: + * type: integer + * price_adjustment: + * type: integer + * points_adjustment: + * type: integer + * rongdou_adjustment: + * type: integer + * stock: + * type: integer + * sku_code: + * type: string + * barcode: + * type: string + * weight: + * type: number + * volume: + * type: number + * status: + * type: string + * spec_details: + * type: array + * items: + * type: object + * actual_price: + * type: number + * actual_points_price: + * type: number + * actual_rongdou_price: + * type: number + * is_available: + * type: boolean + * 404: + * description: 规格组合不存在 + * 500: + * description: 服务器错误 + * delete: + * summary: 删除规格组合 + * tags: [Specifications] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 规格组合ID + * responses: + * 200: + * description: 删除成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * example: 规格组合删除成功 + * 404: + * description: 规格组合不存在 + * 500: + * description: 服务器错误 + */ +router.get('/combinations/:id', async (req, res) => { + try { + const { id } = req.params; + + // 获取规格组合详情 + const [combinations] = await getDB().execute( + `SELECT psc.*, p.name as product_name, p.price as base_price, + p.points_price as base_points_price, p.rongdou_price as base_rongdou_price + FROM product_spec_combinations psc + LEFT JOIN products p ON psc.product_id = p.id + WHERE psc.id = ?`, + [id] + ); + + if (combinations.length === 0) { + return res.status(404).json({ success: false, message: '规格组合不存在' }); + } + + const combination = combinations[0]; + + // 解析规格值并获取详细信息 + let specValueIds; + try { + if (!combination.spec_values) { + specValueIds = []; + } else if (typeof combination.spec_values === 'string') { + try { + specValueIds = JSON.parse(combination.spec_values); + } catch { + specValueIds = combination.spec_values.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id)); + } + } else if (Buffer.isBuffer(combination.spec_values)) { + const strValue = combination.spec_values.toString(); + try { + specValueIds = JSON.parse(strValue); + } catch { + specValueIds = strValue.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id)); + } + } else { + specValueIds = Array.isArray(combination.spec_values) ? combination.spec_values : []; + } + } catch (error) { + console.error('解析规格值失败:', combination.spec_values, error); + specValueIds = []; + } + + if (specValueIds && specValueIds.length > 0) { + const placeholders = specValueIds.map(() => '?').join(','); + const [specDetails] = await getDB().execute( + `SELECT sv.*, sn.name as spec_name, sn.display_name as spec_display_name + FROM spec_values sv + LEFT JOIN spec_names sn ON sv.spec_name_id = sn.id + WHERE sv.id IN (${placeholders}) + ORDER BY sn.sort_order, sv.sort_order`, + specValueIds + ); + + combination.spec_details = specDetails; + } else { + combination.spec_details = []; + } + + // 计算实际价格 + combination.actual_price = combination.base_price + (combination.price_adjustment || 0); + combination.actual_points_price = combination.base_points_price + (combination.points_adjustment || 0); + combination.actual_rongdou_price = combination.base_rongdou_price + (combination.rongdou_adjustment || 0); + combination.is_available = combination.stock > 0; + + res.json({ + success: true, + data: combination + }); + } catch (error) { + console.error('获取规格组合详情失败:', error); + res.status(500).json({ success: false, message: '获取规格组合详情失败' }); + } +}); + +/** + * @swagger + * /specifications/combinations/{id}: + * delete: + * summary: 删除规格组合 + * tags: [Specifications] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 规格组合ID + * responses: + * 200: + * description: 删除成功 + * 404: + * description: 规格组合不存在 + */ +router.delete('/combinations/:id', auth, adminAuth, async (req, res) => { + try { + const { id } = req.params; + + // 检查规格组合是否存在 + const [existingCombination] = await getDB().execute( + 'SELECT id FROM product_spec_combinations WHERE id = ?', + [id] + ); + + if (existingCombination.length === 0) { + return res.status(404).json({ success: false, message: '规格组合不存在' }); + } + + // 删除规格组合 + await getDB().execute( + 'DELETE FROM product_spec_combinations WHERE id = ?', + [id] + ); + + res.json({ + success: true, + message: '规格组合删除成功' + }); + } catch (error) { + console.error('删除规格组合失败:', error); + res.status(500).json({ success: false, message: '删除规格组合失败' }); + } +}); + +/** + * @swagger + * /specifications/combinations/{id}: + * put: + * summary: 更新规格组合 + * tags: [Specifications] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 规格组合ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * price_adjustment: + * type: integer + * points_adjustment: + * type: integer + * rongdou_adjustment: + * type: integer + * stock: + * type: integer + * sku_code: + * type: string + * barcode: + * type: string + * weight: + * type: number + * volume: + * type: number + * status: + * type: string + * enum: [active, inactive] + * responses: + * 200: + * description: 规格组合更新成功 + * 404: + * description: 规格组合不存在 + */ +router.put('/combinations/:id', auth, adminAuth, async (req, res) => { + try { + const { id } = req.params; + const { + price_adjustment, + points_adjustment, + rongdou_adjustment, + stock, + sku_code, + barcode, + weight, + volume, + status + } = req.body; + + // 检查规格组合是否存在 + const [existing] = await getDB().execute( + 'SELECT id FROM product_spec_combinations WHERE id = ?', + [id] + ); + + if (existing.length === 0) { + return res.status(404).json({ success: false, message: '规格组合不存在' }); + } + + // 构建更新字段 + const updateFields = []; + const updateValues = []; + + if (price_adjustment !== undefined) { + updateFields.push('price_adjustment = ?'); + updateValues.push(price_adjustment); + } + + if (points_adjustment !== undefined) { + updateFields.push('points_adjustment = ?'); + updateValues.push(points_adjustment); + } + + if (rongdou_adjustment !== undefined) { + updateFields.push('rongdou_adjustment = ?'); + updateValues.push(rongdou_adjustment); + } + + if (stock !== undefined) { + updateFields.push('stock = ?'); + updateValues.push(stock); + } + + if (sku_code !== undefined) { + updateFields.push('sku_code = ?'); + updateValues.push(sku_code); + } + + if (barcode !== undefined) { + updateFields.push('barcode = ?'); + updateValues.push(barcode); + } + + if (weight !== undefined) { + updateFields.push('weight = ?'); + updateValues.push(weight); + } + + if (volume !== undefined) { + updateFields.push('volume = ?'); + updateValues.push(volume); + } + + if (status !== undefined) { + updateFields.push('status = ?'); + updateValues.push(status); + } + + if (updateFields.length === 0) { + return res.status(400).json({ success: false, message: '没有提供要更新的字段' }); + } + + updateFields.push('updated_at = NOW()'); + updateValues.push(id); + + const updateQuery = `UPDATE product_spec_combinations SET ${updateFields.join(', ')} WHERE id = ?`; + + await getDB().execute(updateQuery, updateValues); + + res.json({ + success: true, + message: '规格组合更新成功' + }); + } catch (error) { + console.error('更新规格组合失败:', error); + res.status(500).json({ success: false, message: '更新规格组合失败' }); + } +}); + +/** + * @swagger + * /specifications/combinations: + * post: + * summary: 创建商品规格组合 + * tags: [Specifications] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - product_id + * - spec_values + * properties: + * product_id: + * type: integer + * spec_values: + * type: array + * items: + * type: integer + * description: 规格值ID数组 + * price_adjustment: + * type: integer + * default: 0 + * points_adjustment: + * type: integer + * default: 0 + * rongdou_adjustment: + * type: integer + * default: 0 + * stock: + * type: integer + * default: 0 + * sku_code: + * type: string + * barcode: + * type: string + * weight: + * type: number + * volume: + * type: number + * responses: + * 201: + * description: 规格组合创建成功 + */ +router.post('/combinations', auth, adminAuth, async (req, res) => { + try { + const { + product_id, + spec_values, + price_adjustment = 0, + points_adjustment = 0, + rongdou_adjustment = 0, + stock = 0, + sku_code, + barcode, + weight, + volume + } = req.body; + + if (!product_id || !spec_values || !Array.isArray(spec_values) || spec_values.length === 0) { + return res.status(400).json({ success: false, message: '商品ID和规格值数组不能为空' }); + } + + // 生成组合键 + const sortedSpecValues = [...spec_values].sort((a, b) => a - b); + const combinationKey = sortedSpecValues.join('-'); + + const [result] = await getDB().execute( + `INSERT INTO product_spec_combinations + (product_id, combination_key, spec_values, price_adjustment, points_adjustment, + rongdou_adjustment, stock, sku_code, barcode, weight, volume) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [ + product_id, + combinationKey, + JSON.stringify(sortedSpecValues), + price_adjustment, + points_adjustment, + rongdou_adjustment, + stock, + sku_code, + barcode, + weight, + volume + ] + ); + + res.status(201).json({ + success: true, + message: '规格组合创建成功', + data: { id: result.insertId, combination_key: combinationKey } + }); + } catch (error) { + if (error.code === 'ER_DUP_ENTRY') { + return res.status(400).json({ success: false, message: '该规格组合已存在' }); + } + console.error('创建规格组合失败:', error); + res.status(500).json({ success: false, message: '创建规格组合失败' }); + } +}); + +/** + * @swagger + * /specifications/generate-combinations: + * post: + * summary: 为商品生成笛卡尔积规格组合 + * tags: [Specifications] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - product_id + * - spec_name_ids + * properties: + * product_id: + * type: integer + * spec_name_ids: + * type: array + * items: + * type: integer + * description: 规格名称ID数组 + * default_stock: + * type: integer + * default: 0 + * description: 默认库存 + * responses: + * 201: + * description: 规格组合生成成功 + */ +router.post('/generate-combinations', auth, adminAuth, async (req, res) => { + try { + const { product_id, spec_name_ids, default_stock = 0 } = req.body; + + if (!product_id || !spec_name_ids || !Array.isArray(spec_name_ids) || spec_name_ids.length === 0) { + return res.status(400).json({ success: false, message: '商品ID和规格名称ID数组不能为空' }); + } + + // 获取每个规格名称下的所有活跃规格值 + const specValueGroups = []; + + for (const specNameId of spec_name_ids) { + const [specValues] = await getDB().execute( + 'SELECT id FROM spec_values WHERE spec_name_id = ? AND status = "active" ORDER BY sort_order, id', + [specNameId] + ); + + if (specValues.length === 0) { + return res.status(400).json({ + success: false, + message: `规格名称ID ${specNameId} 下没有活跃的规格值` + }); + } + + specValueGroups.push(specValues.map(sv => sv.id)); + } + + // 生成笛卡尔积 + function cartesianProduct(arrays) { + return arrays.reduce((acc, curr) => { + const result = []; + acc.forEach(a => { + curr.forEach(c => { + result.push([...a, c]); + }); + }); + return result; + }, [[]]); + } + + const combinations = cartesianProduct(specValueGroups); + + // 生成所有组合键 + const combinationData = combinations.map(combination => { + const sortedCombination = [...combination].sort((a, b) => a - b); + const combinationKey = sortedCombination.join('-'); + return { + combination: sortedCombination, + key: combinationKey + }; + }); + + // 批量检查已存在的组合 + const existingKeys = new Set(); + if (combinationData.length > 0) { + const keys = combinationData.map(item => item.key); + const placeholders = keys.map(() => '?').join(','); + + const [existingCombinations] = await getDB().execute( + `SELECT combination_key FROM product_spec_combinations + WHERE product_id = ? AND combination_key IN (${placeholders})`, + [product_id, ...keys] + ); + + existingCombinations.forEach(row => { + existingKeys.add(row.combination_key); + }); + } + + // 过滤出需要插入的新组合 + const newCombinations = combinationData.filter(item => !existingKeys.has(item.key)); + + // 批量插入新的规格组合 + let createdCount = 0; + const skippedCount = combinationData.length - newCombinations.length; + + if (newCombinations.length > 0) { + // 使用批量插入提高性能 + const values = []; + const placeholders = []; + + newCombinations.forEach(item => { + values.push( + product_id, + item.key, + JSON.stringify(item.combination), + default_stock + ); + placeholders.push('(?, ?, ?, ?)'); + }); + + const sql = `INSERT INTO product_spec_combinations + (product_id, combination_key, spec_values, stock) + VALUES ${placeholders.join(', ')}`; + + const [result] = await getDB().execute(sql, values); + createdCount = result.affectedRows; + } + + res.status(201).json({ + success: true, + message: '规格组合生成完成', + data: { + total_combinations: combinations.length, + created: createdCount, + skipped: skippedCount + } + }); + } 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/transfers.js b/routes/transfers.js index d28ce94..b4f71d0 100644 --- a/routes/transfers.js +++ b/routes/transfers.js @@ -821,8 +821,8 @@ router.get('/user/:userId', authenticateToken, async (req, res) => { LEFT JOIN users to_user ON t.to_user_id = to_user.id ${whereClause} ORDER BY t.created_at DESC - LIMIT ? OFFSET ? - `, listParams); + LIMIT ${limitNum} OFFSET ${offset} + `, countParams); const [countResult] = await db.execute(` SELECT COUNT(*) as total FROM transfers t ${whereClause} @@ -1556,10 +1556,11 @@ router.get('/daily-stats', u.balance, COALESCE(yesterday_out.amount, 0) as yesterday_out_amount, COALESCE(today_in.amount, 0) as today_in_amount, + COALESCE(confirmed_from.confirmed_amount, 0) as confirmed_from_amount, CASE - WHEN (COALESCE(yesterday_out.amount, 0) - COALESCE(today_in.amount, 0)) > ABS(u.balance) + WHEN (COALESCE(u.balance, 0) +COALESCE(confirmed_from.confirmed_amount, 0) ) > ABS(u.balance) THEN ABS(u.balance) - ELSE (COALESCE(yesterday_out.amount, 0) - COALESCE(today_in.amount, 0)) + ELSE (COALESCE(u.balance, 0)+ COALESCE(confirmed_from.confirmed_amount, 0) ) END as balance_needed FROM users u LEFT JOIN ( @@ -1580,13 +1581,29 @@ router.get('/daily-stats', AND status IN ('confirmed', 'received') GROUP BY to_user_id ) today_in ON u.id = today_in.to_user_id + left join ( + select + from_user_id, + sum(amount) as confirmed_amount + from + transfers + where + status = 'received' + and created_at >= ? + and created_at <= ? + group by + from_user_id + ) as confirmed_from on u.id = confirmed_from.from_user_id WHERE u.role != 'admin' AND u.is_system_account != 1 AND yesterday_out.amount > 0 AND u.balance < 0 ORDER BY balance_needed DESC, yesterday_out_amount DESC - `, [yesterdayStartStr, yesterdayEndStr, todayStartStr, todayEndStr]); - userStats = userStats.filter(item=>item.balance_needed >= 100) + `, [yesterdayStartStr, yesterdayEndStr, todayStartStr, todayEndStr, todayStartStr, todayEndStr]); + // userStats = userStats.filter(item=>item.balance_needed >= 100) + userStats.forEach(item=>{ + item.balance_needed = Math.abs(item.balance_needed) + }) res.json({ success: true, data: { diff --git a/routes/users.js b/routes/users.js index 1e7dd44..9d18bd8 100644 --- a/routes/users.js +++ b/routes/users.js @@ -53,6 +53,9 @@ const router = express.Router(); * is_system_account: * type: boolean * description: 是否为系统账户 + * is_distribute: + * type: boolean + * description: 是否为分发账户 * created_at: * type: string * format: date-time @@ -135,10 +138,10 @@ router.post('/', auth, adminAuth, async (req, res) => { try { const db = getDB(); await db.query('START TRANSACTION'); - - const { - username, - password, + + const { + username, + password, role = 'user', isSystemAccount = false, // 是否为虚拟商户 realName, @@ -149,52 +152,52 @@ router.post('/', auth, adminAuth, async (req, res) => { unionpayQr, phone } = req.body; - + if (!username || !password) { return res.status(400).json({ success: false, message: '用户名和密码不能为空' }); } - + if (!realName || !idCard) { return res.status(400).json({ success: false, message: '姓名和身份证号不能为空' }); } - + // 验证身份证号格式 const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/; if (!idCardRegex.test(idCard)) { return res.status(400).json({ success: false, message: '身份证号格式不正确' }); } - + // 检查用户是否已存在 const [existingUsers] = await db.execute( 'SELECT id FROM users WHERE username = ? OR id_card = ? OR (phone IS NOT NULL AND phone = ?)', [username, idCard, phone || null] ); - + 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, password, role, is_system_account, points, real_name, id_card, wechat_qr, alipay_qr, bank_card, unionpay_qr, phone) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [username, hashedPassword, role, isSystemAccount, 0, realName, idCard, wechatQr, alipayQr, bankCard, unionpayQr, phone] ); - + const userId = result.insertId; - + // 用户余额已在创建用户时设置为默认值0.00,无需额外操作 - + await db.query('COMMIT'); - + // 返回创建的用户信息(不包含密码) const [newUser] = await db.execute( 'SELECT id, username, role, avatar, points, real_name, phone, created_at, updated_at FROM users WHERE id = ?', [userId] ); - + res.status(201).json({ success: true, message: '用户创建成功', @@ -274,14 +277,14 @@ router.get('/pending-audit', auth, adminAuth, async (req, res) => { const pageNum = parseInt(page) || 1; const limitNum = parseInt(limit) || 10; const offset = (pageNum - 1) * limitNum; - + // 获取待审核用户总数 const [countResult] = await db.execute( 'SELECT COUNT(*) as total FROM users WHERE audit_status = ?', ['pending'] ); const total = countResult[0].total; - + // 获取待审核用户列表 const [users] = await db.execute( `SELECT id, username, phone, real_name, business_license, id_card_front, id_card_back, @@ -292,7 +295,7 @@ router.get('/pending-audit', auth, adminAuth, async (req, res) => { LIMIT ${limitNum} OFFSET ${offset}`, ['pending'] ); - + res.json({ success: true, data: { @@ -315,13 +318,13 @@ router.get('/pending-audit', auth, adminAuth, async (req, res) => { router.get('/for-transfer', auth, async (req, res) => { try { const db = getDB(); - + // 获取所有用户的基本信息(用于转账选择) const [users] = await db.execute( 'SELECT id, username, real_name FROM users WHERE id != ? ORDER BY username', [req.user.id] ); - + res.json({ success: true, data: users @@ -337,46 +340,46 @@ router.get('/', auth, adminAuth, async (req, res) => { try { const db = getDB(); const { page = 1, limit = 10, search = '', role = '', city = '', district = '', sort = 'created_at', order = 'desc' } = req.query; - + // 确保参数为有效数字 const pageNum = Math.max(1, parseInt(page) || 1); const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 10)); const offset = Math.max(0, (pageNum - 1) * limitNum); - + let whereConditions = []; let countParams = []; let listParams = []; - + // 构建查询条件 if (search) { whereConditions.push('(u.username LIKE ? OR u.real_name LIKE ?)'); countParams.push(`%${search}%`, `%${search}%`); listParams.push(`%${search}%`, `%${search}%`); } - + if (role && role !== 'all') { whereConditions.push('u.role = ?'); countParams.push(role); listParams.push(role); } - + if (city) { whereConditions.push('u.city = ?'); countParams.push(city); listParams.push(city); } - + if (district) { whereConditions.push('u.district_id = ?'); countParams.push(district); listParams.push(district); } - + const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; - + // 添加分页参数 listParams.push(limitNum.toString(), offset.toString()); - + // 获取总数 const [countResult] = await db.execute( @@ -385,22 +388,22 @@ router.get('/', auth, adminAuth, async (req, res) => { ${whereClause}`, countParams ); - + // 验证排序字段,防止SQL注入 const validSortFields = ['id', 'username', 'role', 'points', 'balance', 'created_at', 'updated_at']; const sortField = validSortFields.includes(sort) ? sort : 'created_at'; - + // 验证排序方向 - const sortOrder = (order && (order.toUpperCase() === 'ASC' || order.toUpperCase() === 'DESC')) - ? order.toUpperCase() + const sortOrder = (order && (order.toUpperCase() === 'ASC' || order.toUpperCase() === 'DESC')) + ? order.toUpperCase() : 'DESC'; - + // 获取用户列表,关联地区信息和转账统计 const [users] = await db.execute( `SELECT u.id, u.username, u.role, u.avatar, u.points, u.balance, u.real_name, u.id_card, u.phone, u.wechat_qr, u.alipay_qr, u.bank_card, u.unionpay_qr, u.audit_status, u.is_system_account, u.created_at, u.updated_at, u.city, u.district_id,u.id_card_front,u.id_card_back, - u.business_license, + u.business_license,u.is_distribute, r.city_name, r.district_name, COALESCE(yesterday_out.amount, 0) as yesterday_transfer_amount, COALESCE(today_in.amount, 0) as today_received_amount @@ -424,10 +427,10 @@ router.get('/', auth, adminAuth, async (req, res) => { ) today_in ON u.id = today_in.to_user_id ${whereClause} ORDER BY u.${sortField} ${sortOrder} - LIMIT ? OFFSET ?`, - listParams + LIMIT ${limitNum} OFFSET ${offset}`, + listParams.slice(0, -2) ); - + res.json({ success: true, users, @@ -447,12 +450,12 @@ router.get('/profile', auth, async (req, res) => { try { const db = getDB(); const userId = req.user.id; - + const [users] = await db.execute( 'SELECT * FROM users WHERE id = ?', [userId] ); - + if (users.length === 0) { return res.status(404).json({ success: false, message: '用户不存在' }); } @@ -484,20 +487,20 @@ router.get('/payment-codes-status', auth, async (req, res) => { try { const db = getDB(); const userId = req.user.id; - + const [users] = await db.execute( 'SELECT wechat_qr, alipay_qr, unionpay_qr FROM users WHERE id = ?', [userId] ); - + if (users.length === 0) { return res.status(404).json({ success: false, message: '用户不存在' }); } - + const paymentCodes = users[0]; - - res.json({ - success: true, + + res.json({ + success: true, data: { wechat_qr: paymentCodes.wechat_qr || '', alipay_qr: paymentCodes.alipay_qr || '', @@ -515,19 +518,19 @@ router.get('/payment-info/:userId', auth, async (req, res) => { try { const db = getDB(); const targetUserId = req.params.userId; - + const [users] = await db.execute( 'SELECT id, username, wechat_qr, alipay_qr, unionpay_qr, bank_card FROM users WHERE id = ?', [targetUserId] ); - + if (users.length === 0) { return res.status(404).json({ success: false, message: '用户不存在' }); } - + const user = users[0]; - res.json({ - success: true, + res.json({ + success: true, data: { id: user.id, username: user.username, @@ -548,51 +551,51 @@ router.get('/stats', auth, async (req, res) => { try { const db = getDB(); const userId = req.user.id; - + // 如果是管理员,返回全局统计 if (req.user.role === 'admin') { // 总用户数 const [totalUsers] = await db.execute('SELECT COUNT(*) as count FROM users'); - + // 管理员数量 const [adminUsers] = await db.execute('SELECT COUNT(*) as count FROM users WHERE role = "admin"'); - + // 普通用户数量 const [regularUsers] = await db.execute('SELECT COUNT(*) as count FROM users WHERE role = "user"'); - + // 本月新增用户 const [monthUsers] = await db.execute( 'SELECT COUNT(*) as count FROM users WHERE YEAR(created_at) = YEAR(NOW()) AND MONTH(created_at) = MONTH(NOW())' ); - + // 上月新增用户 const [lastMonthUsers] = await db.execute( 'SELECT COUNT(*) as count FROM users WHERE YEAR(created_at) = YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND MONTH(created_at) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH))' ); - + // 计算月增长率 - const monthGrowthRate = lastMonthUsers[0].count > 0 + const monthGrowthRate = lastMonthUsers[0].count > 0 ? ((monthUsers[0].count - lastMonthUsers[0].count) / lastMonthUsers[0].count * 100).toFixed(2) : 0; - + // 用户总积分 const [totalPoints] = await db.execute('SELECT COALESCE(SUM(points), 0) as total FROM users'); - + // 今日新增用户 const [todayUsers] = await db.execute( 'SELECT COUNT(*) as count FROM users WHERE DATE(created_at) = CURDATE()' ); - + // 昨日新增用户 const [yesterdayUsers] = await db.execute( 'SELECT COUNT(*) as count FROM users WHERE DATE(created_at) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)' ); - + // 活跃用户数(有订单的用户) const [activeUsers] = await db.execute( 'SELECT COUNT(DISTINCT user_id) as count FROM orders' ); - + res.json({ success: true, stats: { @@ -614,24 +617,24 @@ router.get('/stats', auth, async (req, res) => { 'SELECT COUNT(*) as count FROM orders WHERE user_id = ?', [userId] ); - + // 用户总消费 const [totalSpent] = await db.execute( 'SELECT COALESCE(SUM(total_amount), 0) as total FROM orders WHERE user_id = ? AND status = "completed"', [userId] ); - + // 用户积分历史 const [pointsEarned] = await db.execute( 'SELECT COALESCE(SUM(amount), 0) as total FROM points_history WHERE user_id = ? AND type = "earn"', [userId] ); - + const [pointsSpent] = await db.execute( 'SELECT COALESCE(SUM(amount), 0) as total FROM points_history WHERE user_id = ? AND type = "spend"', [userId] ); - + res.json({ success: true, stats: { @@ -655,46 +658,46 @@ router.get('/admin/stats', auth, adminAuth, async (req, res) => { const db = getDB(); // 总用户数 const [totalUsers] = await db.execute('SELECT COUNT(*) as count FROM users'); - + // 管理员数量 const [adminUsers] = await db.execute('SELECT COUNT(*) as count FROM users WHERE role = "admin"'); - + // 普通用户数量 const [regularUsers] = await db.execute('SELECT COUNT(*) as count FROM users WHERE role = "user"'); - + // 本月新增用户 const [monthUsers] = await db.execute( 'SELECT COUNT(*) as count FROM users WHERE YEAR(created_at) = YEAR(NOW()) AND MONTH(created_at) = MONTH(NOW())' ); - + // 上月新增用户 const [lastMonthUsers] = await db.execute( 'SELECT COUNT(*) as count FROM users WHERE YEAR(created_at) = YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND MONTH(created_at) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH))' ); - + // 计算月增长率 - const monthGrowthRate = lastMonthUsers[0].count > 0 + const monthGrowthRate = lastMonthUsers[0].count > 0 ? ((monthUsers[0].count - lastMonthUsers[0].count) / lastMonthUsers[0].count * 100).toFixed(2) : 0; - + // 用户总积分 const [totalPoints] = await db.execute('SELECT COALESCE(SUM(points), 0) as total FROM users'); - + // 今日新增用户 const [todayUsers] = await db.execute( 'SELECT COUNT(*) as count FROM users WHERE DATE(created_at) = CURDATE()' ); - + // 昨日新增用户 const [yesterdayUsers] = await db.execute( 'SELECT COUNT(*) as count FROM users WHERE DATE(created_at) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)' ); - + // 活跃用户数(有订单的用户) const [activeUsers] = await db.execute( 'SELECT COUNT(DISTINCT user_id) as count FROM orders' ); - + res.json({ success: true, stats: { @@ -719,16 +722,16 @@ router.get('/admin/stats', auth, adminAuth, async (req, res) => { router.get('/points', auth, async (req, res) => { try { const userId = req.user.id; - + const [users] = await getDB().execute( 'SELECT points FROM users WHERE id = ?', [userId] ); - + if (users.length === 0) { return res.status(404).json({ success: false, message: '用户不存在' }); } - + res.json({ success: true, points: users[0].points @@ -744,36 +747,36 @@ router.get('/points/history', auth, async (req, res) => { try { const userId = req.user.id; const { page = 1, limit = 20, type } = req.query; - + // 确保参数为有效数字 const pageNum = Math.max(1, parseInt(page) || 1); const limitNum = Math.max(1, Math.min(100, parseInt(limit) || 20)); const offset = Math.max(0, (pageNum - 1) * limitNum); - + let whereClause = 'WHERE user_id = ?'; let queryParams = [userId]; - + if (type && ['earn', 'spend'].includes(type)) { whereClause += ' AND type = ?'; queryParams.push(type); } - + // 获取总数 const [countResult] = await getDB().execute( `SELECT COUNT(*) as total FROM points_history ${whereClause}`, queryParams ); - + // 获取历史记录 const [records] = await getDB().execute( `SELECT id, type, amount, description, order_id, created_at FROM points_history ${whereClause} ORDER BY created_at DESC - LIMIT ? OFFSET ?`, - [...queryParams, limitNum.toString(), offset.toString()] + LIMIT ${limitNum} OFFSET ${offset}`, + queryParams ); - + res.json({ success: true, data: { @@ -798,7 +801,7 @@ router.get('/growth-trend', auth, adminAuth, async (req, res) => { const db = getDB(); const { days = 7 } = req.query; const daysNum = Math.min(90, Math.max(1, parseInt(days) || 7)); - + // 获取指定天数内的用户注册趋势 const [trendData] = await db.execute(` SELECT @@ -809,26 +812,26 @@ router.get('/growth-trend', auth, adminAuth, async (req, res) => { GROUP BY DATE(created_at) ORDER BY date ASC `, [daysNum]); - + // 填充缺失的日期(注册数为0) const result = []; - + for (let i = daysNum - 1; i >= 0; i--) { const date = dayjs().subtract(i, 'day'); const dateStr = date.format('YYYY-MM-DD'); - + // 修复日期比较:将数据库返回的Date对象转换为字符串进行比较 const existingData = trendData.find(item => { const itemDateStr = dayjs(item.date).format('YYYY-MM-DD'); return itemDateStr === dateStr; }); - + result.push({ date: date.format('MM-DD'), count: existingData ? existingData.count : 0 }); } - + res.json({ success: true, data: result @@ -845,7 +848,7 @@ router.get('/daily-revenue', auth, adminAuth, async (req, res) => { const db = getDB(); const { days = 30 } = req.query; const daysNum = Math.min(90, Math.max(1, parseInt(days) || 30)); - + // 获取指定天数内的用户注册数据,按天统计 const [dailyData] = await db.execute(` SELECT @@ -856,29 +859,29 @@ router.get('/daily-revenue', auth, adminAuth, async (req, res) => { GROUP BY DATE(created_at) ORDER BY date ASC `, [daysNum]); - + // 填充缺失的日期(注册数为0) const result = []; - + for (let i = daysNum - 1; i >= 0; i--) { const date = dayjs().subtract(i, 'day'); const dateStr = date.format('YYYY-MM-DD'); // YYYY-MM-DD格式 const dateDisplay = date.format('M/D'); // 显示格式 - + const existingData = dailyData.find(item => { const itemDateStr = dayjs(item.date).format('YYYY-MM-DD'); return itemDateStr === dateStr; }); const userCount = existingData ? existingData.user_count : 0; const revenue = userCount * 398; // 每个用户398元收入 - + result.push({ date: dateDisplay, userCount: userCount, amount: revenue }); } - + res.json({ success: true, data: result @@ -898,7 +901,7 @@ router.post('/registration-codes', auth, adminAuth, async (req, res) => { try { const db = getDB(); const adminId = req.user.id; - + // 生成6位随机激活码 const crypto = require('crypto'); const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; @@ -906,16 +909,16 @@ router.post('/registration-codes', auth, adminAuth, async (req, res) => { for (let i = 0; i < 6; i++) { code += chars.charAt(Math.floor(Math.random() * chars.length)); } - + // 设置过期时间为1小时后 const expiresAt = req.body.expiresAt || new Date(Date.now() + 60 * 60 * 1000); - + // 插入激活码 const [result] = await db.execute( 'INSERT INTO registration_codes (code, expires_at, created_by_admin_id) VALUES (?, ?, ?)', [code, expiresAt, adminId] ); - + res.status(201).json({ success: true, message: '激活码生成成功', @@ -940,15 +943,15 @@ router.post('/registration-codes/batch', auth, adminAuth, async (req, res) => { const db = getDB(); const adminId = req.user.id; const { count = 1 } = req.body; - + // 验证参数 const codeCount = Math.max(1, Math.min(100, parseInt(count) || 1)); - + const crypto = require('crypto'); const codes = []; const values = []; const expiresAt = req.body.expiresAt || new Date(Date.now() + 60 * 60 * 1000); - + // 生成指定数量的激活码 const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; for (let i = 0; i < codeCount; i++) { @@ -959,15 +962,15 @@ router.post('/registration-codes/batch', auth, adminAuth, async (req, res) => { codes.push(code); values.push(code, expiresAt, adminId); } - + // 批量插入数据库 const placeholders = Array(codeCount).fill('(?, ?, ?)').join(', '); - + await db.execute( `INSERT INTO registration_codes (code, expires_at, created_by_admin_id) VALUES ${placeholders}`, values ); - + res.status(201).json({ success: true, message: `成功生成 ${codeCount} 个激活码`, @@ -991,12 +994,12 @@ router.get('/registration-codes', auth, adminAuth, async (req, res) => { const db = getDB(); const { page = 1, limit = 20, status, keyword, sort = 'created_at', order = 'desc' } = req.query; const offset = (page - 1) * limit; - + let whereClause = ''; let whereConditions = []; let countParams = []; let listParams = []; - + // 根据状态筛选 if (status === 'unused') { whereConditions.push('rc.is_used = FALSE AND rc.expires_at > NOW()'); @@ -1005,24 +1008,24 @@ router.get('/registration-codes', auth, adminAuth, async (req, res) => { } else if (status === 'expired') { whereConditions.push('rc.is_used = FALSE AND rc.expires_at <= NOW()'); } - + // 关键词搜索 - if(keyword){ + if (keyword) { whereConditions.push(`rc.code LIKE '%${keyword}%'`); } - + // 构建WHERE子句 if (whereConditions.length > 0) { whereClause = 'WHERE ' + whereConditions.join(' AND '); } - + // 处理排序参数 const allowedSortFields = ['created_at', 'expires_at', 'used_at', 'code', 'status']; const allowedOrders = ['asc', 'desc']; - + let sortField = 'rc.created_at'; let sortOrder = 'DESC'; - + if (allowedSortFields.includes(sort)) { if (sort === 'status') { // 状态字段需要使用CASE表达式 @@ -1035,17 +1038,17 @@ router.get('/registration-codes', auth, adminAuth, async (req, res) => { sortField = `rc.${sort}`; } } - + if (allowedOrders.includes(order.toLowerCase())) { sortOrder = order.toUpperCase(); } - + // 设置查询参数(MySQL驱动需要字符串形式的LIMIT和OFFSET) const limitStr = String(parseInt(limit)); const offsetStr = String(parseInt(offset)); listParams = [limitStr, offsetStr]; countParams = []; - + // 获取激活码列表 const [codes] = await db.execute(` SELECT @@ -1067,18 +1070,18 @@ router.get('/registration-codes', auth, adminAuth, async (req, res) => { LEFT JOIN users user ON rc.used_by_user_id = user.id ${whereClause} ORDER BY ${sortField} ${sortOrder} - LIMIT ? OFFSET ? - `, listParams); - + LIMIT ${limit} OFFSET ${offset} + `, countParams); + // 获取总数 const [countResult] = await db.execute(` SELECT COUNT(*) as total FROM registration_codes rc ${whereClause} `, countParams); - + const total = countResult[0].total; - + res.json({ success: true, data: { @@ -1104,25 +1107,25 @@ router.delete('/registration-codes/:id', auth, adminAuth, async (req, res) => { try { const db = getDB(); const codeId = req.params.id; - + // 检查激活码是否存在 const [codes] = await db.execute( 'SELECT id, is_used FROM registration_codes WHERE id = ?', [codeId] ); - + if (codes.length === 0) { return res.status(404).json({ success: false, message: '激活码不存在' }); } - + // 不能删除已使用的激活码 if (codes[0].is_used) { return res.status(400).json({ success: false, message: '不能删除已使用的激活码' }); } - + // 删除激活码 await db.execute('DELETE FROM registration_codes WHERE id = ?', [codeId]); - + res.json({ success: true, message: '激活码删除成功' }); } catch (error) { console.error('删除激活码错误:', error); @@ -1134,16 +1137,16 @@ router.get('/profile', auth, async (req, res) => { try { const db = getDB(); const userId = req.user.id; - + const [users] = await db.execute( 'SELECT id, username, role, avatar, points, real_name, id_card, phone, wechat_qr, alipay_qr, bank_card, unionpay_qr, business_license, id_card_front, id_card_back, audit_status, created_at, updated_at FROM users WHERE id = ?', [userId] ); - + if (users.length === 0) { return res.status(404).json({ success: false, message: '用户不存在' }); } - + // 转换字段名以匹配前端 const user = users[0]; const profile = { @@ -1160,7 +1163,7 @@ router.get('/profile', auth, async (req, res) => { idCardBack: user.id_card_back, auditStatus: user.audit_status }; - + res.json({ success: true, user: profile }); } catch (error) { console.error('获取用户个人资料错误:', error); @@ -1173,16 +1176,16 @@ router.put('/profile', auth, async (req, res) => { try { const db = getDB(); const userId = req.user.id; - const { - username, - nickname, - avatar, - realName, - idCard, - phone, - wechatQr, - alipayQr, - bankCard, + const { + username, + nickname, + avatar, + realName, + idCard, + phone, + wechatQr, + alipayQr, + bankCard, unionpayQr, businessLicense, idCardFront, @@ -1190,15 +1193,15 @@ router.put('/profile', auth, async (req, res) => { city, districtId } = req.body; - + // 处理nickname字段,如果提供了nickname,则使用nickname作为username const finalUsername = nickname || username; - + // 检查用户名、身份证号和手机号是否已被其他用户使用 if (finalUsername || idCard || phone) { const conditions = []; const checkValues = []; - + if (finalUsername) { conditions.push('username = ?'); checkValues.push(finalUsername); @@ -1211,19 +1214,19 @@ router.put('/profile', auth, async (req, res) => { conditions.push('phone = ?'); checkValues.push(phone); } - + if (conditions.length > 0) { const [existingUsers] = await db.execute( `SELECT id FROM users WHERE (${conditions.join(' OR ')}) AND id != ?`, [...checkValues, userId] ); - + if (existingUsers.length > 0) { return res.status(400).json({ success: false, message: '用户名、身份证号或手机号已被使用' }); } } } - + // 验证身份证号格式 if (idCard) { const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/; @@ -1231,125 +1234,125 @@ router.put('/profile', auth, async (req, res) => { return res.status(400).json({ success: false, message: '身份证号格式不正确' }); } } - + // 构建更新字段 const updateFields = []; const updateValues = []; - + if (finalUsername !== undefined) { updateFields.push('username = ?'); updateValues.push(finalUsername); } - + if (avatar !== undefined) { updateFields.push('avatar = ?'); updateValues.push(avatar); } - + if (realName !== undefined) { updateFields.push('real_name = ?'); updateValues.push(realName); } - + if (idCard !== undefined) { updateFields.push('id_card = ?'); updateValues.push(idCard); } - + if (phone !== undefined) { updateFields.push('phone = ?'); updateValues.push(phone); } - + // 添加城市和地区字段更新 if (city !== undefined) { updateFields.push('city = ?'); updateValues.push(city); } - + if (districtId !== undefined) { updateFields.push('district_id = ?'); updateValues.push(districtId); } - + // 检查是否更新了需要重新审核的关键信息 let needsReaudit = false; - + if (wechatQr !== undefined) { updateFields.push('wechat_qr = ?'); updateValues.push(wechatQr); needsReaudit = true; } - + if (alipayQr !== undefined) { updateFields.push('alipay_qr = ?'); updateValues.push(alipayQr); needsReaudit = true; } - + if (bankCard !== undefined) { updateFields.push('bank_card = ?'); updateValues.push(bankCard); needsReaudit = true; } - + if (unionpayQr !== undefined) { updateFields.push('unionpay_qr = ?'); updateValues.push(unionpayQr); needsReaudit = true; } - + if (city !== undefined) { updateFields.push('city = ?'); updateValues.push(city); } - + if (districtId !== undefined) { updateFields.push('district_id = ?'); updateValues.push(districtId); } - + if (businessLicense !== undefined) { updateFields.push('business_license = ?'); updateValues.push(businessLicense); needsReaudit = true; } - + if (idCardFront !== undefined) { updateFields.push('id_card_front = ?'); updateValues.push(idCardFront); needsReaudit = true; } - + if (idCardBack !== undefined) { updateFields.push('id_card_back = ?'); updateValues.push(idCardBack); needsReaudit = true; } - + // 如果更新了关键信息且用户不是管理员,则重置审核状态为待审核 if (needsReaudit && req.user.role !== 'admin') { updateFields.push('audit_status = ?'); updateValues.push('pending'); } - + if (updateFields.length === 0) { return res.status(400).json({ success: false, message: '没有要更新的字段' }); } - + updateValues.push(userId); - + await db.execute( `UPDATE users SET ${updateFields.join(', ')} WHERE id = ?`, updateValues ); - + // 返回更新后的用户信息 const [updatedUsers] = await db.execute( 'SELECT id, username, role, avatar, points, real_name, id_card, phone, wechat_qr, alipay_qr, bank_card, unionpay_qr, business_license, id_card_front, id_card_back, audit_status, is_system_account, created_at, updated_at FROM users WHERE id = ?', [userId] ); - + // 转换字段名以匹配前端 const user = updatedUsers[0]; const profile = { @@ -1366,7 +1369,7 @@ router.put('/profile', auth, async (req, res) => { idCardBack: user.id_card_back, auditStatus: user.audit_status }; - + res.json({ success: true, message: '个人资料更新成功', @@ -1382,21 +1385,21 @@ router.get('/:id', auth, async (req, res) => { try { const db = getDB(); const userId = req.params.id; - + // 只有管理员或用户本人可以查看详情 if (req.user.role !== 'admin' && req.user.id != userId) { return res.status(403).json({ success: false, message: '权限不足' }); } - + const [users] = await db.execute( 'SELECT id, username, role, avatar, points, real_name, id_card, phone, wechat_qr, alipay_qr, bank_card, unionpay_qr, created_at, updated_at FROM users WHERE id = ?', [userId] ); - + if (users.length === 0) { return res.status(404).json({ success: false, message: '用户不存在' }); } - + res.json({ success: true, user: users[0] }); } catch (error) { console.error('获取用户详情错误:', error); @@ -1409,18 +1412,18 @@ router.put('/:id', auth, async (req, res) => { try { const db = getDB(); const userId = req.params.id; - const { - username, + const { + username, password, - role, + role, isSystemAccount, - avatar, - realName, - idCard, - phone, - wechatQr, - alipayQr, - bankCard, + avatar, + realName, + idCard, + phone, + wechatQr, + alipayQr, + bankCard, unionpayQr, city, districtId, @@ -1428,22 +1431,22 @@ router.put('/:id', auth, async (req, res) => { idCardBack, businessLicense, } = req.body; - + // 只有管理员或用户本人可以更新信息 if (req.user.role !== 'admin' && req.user.id != userId) { return res.status(403).json({ success: false, message: '权限不足' }); } - + // 非管理员不能修改角色 if (req.user.role !== 'admin' && role) { return res.status(403).json({ success: false, message: '无权限修改用户角色' }); } - + // 检查用户名、身份证号和手机号是否已被其他用户使用 if (username || idCard || phone) { const conditions = []; const checkValues = []; - + if (username) { conditions.push('username = ?'); checkValues.push(username); @@ -1456,19 +1459,19 @@ router.put('/:id', auth, async (req, res) => { conditions.push('phone = ?'); checkValues.push(phone); } - + if (conditions.length > 0) { const [existingUsers] = await db.execute( `SELECT id FROM users WHERE (${conditions.join(' OR ')}) AND id != ?`, [...checkValues, userId] ); - + if (existingUsers.length > 0) { return res.status(400).json({ success: false, message: '用户名、身份证号或手机号已被使用' }); } } } - + // 验证身份证号格式 if (idCard) { const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/; @@ -1476,83 +1479,83 @@ router.put('/:id', auth, async (req, res) => { return res.status(400).json({ success: false, message: '身份证号格式不正确' }); } } - + // 构建更新字段 const updateFields = []; const updateValues = []; - + if (username) { updateFields.push('username = ?'); updateValues.push(username); } - + // 处理密码更新 if (password && password.trim() !== '') { const hashedPassword = await bcrypt.hash(password, 10); updateFields.push('password = ?'); updateValues.push(hashedPassword); } - + if (role && req.user.role === 'admin') { updateFields.push('role = ?'); updateValues.push(role); } - + // 只有管理员可以修改账户类型 if (isSystemAccount !== undefined && req.user.role === 'admin') { updateFields.push('is_system_account = ?'); updateValues.push(isSystemAccount); } - + if (avatar !== undefined) { updateFields.push('avatar = ?'); updateValues.push(avatar); } - + if (realName !== undefined) { updateFields.push('real_name = ?'); updateValues.push(realName); } - + if (idCard !== undefined) { updateFields.push('id_card = ?'); updateValues.push(idCard); } - + if (phone !== undefined) { updateFields.push('phone = ?'); updateValues.push(phone); } - if (city !== undefined) { + if (city !== undefined) { updateFields.push('city = ?'); updateValues.push(city); } - + if (districtId !== undefined) { updateFields.push('district_id = ?'); updateValues.push(districtId); } // 检查是否更新了需要重新审核的关键信息 let needsReaudit = false; - + if (wechatQr !== undefined) { updateFields.push('wechat_qr = ?'); updateValues.push(wechatQr); needsReaudit = true; } - + if (alipayQr !== undefined) { updateFields.push('alipay_qr = ?'); updateValues.push(alipayQr); needsReaudit = true; } - + if (bankCard !== undefined) { updateFields.push('bank_card = ?'); updateValues.push(bankCard); needsReaudit = true; } - + if (unionpayQr !== undefined) { updateFields.push('unionpay_qr = ?'); updateValues.push(unionpayQr); @@ -1573,30 +1576,30 @@ router.put('/:id', auth, async (req, res) => { updateValues.push(businessLicense); needsReaudit = true; } - + // 如果更新了关键信息且用户不是管理员,则重置审核状态为待审核 if (needsReaudit && req.user.role !== 'admin') { updateFields.push('audit_status = ?'); updateValues.push('pending'); } - + if (updateFields.length === 0) { return res.status(400).json({ success: false, message: '没有要更新的字段' }); } - + updateValues.push(userId); - + await db.execute( `UPDATE users SET ${updateFields.join(', ')} WHERE id = ?`, updateValues ); - + // 返回更新后的用户信息 const [updatedUsers] = await db.execute( 'SELECT id, username, role, avatar, points, real_name, id_card, phone, wechat_qr, alipay_qr, bank_card, unionpay_qr, city, district_id, created_at, updated_at FROM users WHERE id = ?', [userId] ); - + res.json({ success: true, message: '用户信息更新成功', @@ -1615,25 +1618,25 @@ router.delete('/:id', auth, adminAuth, async (req, res) => { try { const db = getDB(); const userId = req.params.id; - + // 不能删除自己 if (req.user.id == userId) { return res.status(400).json({ success: false, message: '不能删除自己的账户' }); } - + // 检查用户是否存在 const [users] = await db.execute( 'SELECT id FROM users WHERE id = ?', [userId] ); - + if (users.length === 0) { return res.status(404).json({ success: false, message: '用户不存在' }); } - + // 删除用户 await db.execute('DELETE FROM users WHERE id = ?', [userId]); - + res.json({ success: true, message: '用户删除成功' }); } catch (error) { console.error('删除用户错误:', error); @@ -1649,26 +1652,26 @@ router.put('/:id/audit', auth, adminAuth, async (req, res) => { const db = getDB(); const userId = req.params.id; const { action, note } = req.body; // action: 'approve' 或 'reject' - + if (!action || !['approve', 'reject'].includes(action)) { return res.status(400).json({ success: false, message: '审核操作无效' }); } - + // 检查用户是否存在且为待审核状态 const [users] = await db.execute( 'SELECT id, username, audit_status FROM users WHERE id = ?', [userId] ); - + if (users.length === 0) { return res.status(404).json({ success: false, message: '用户不存在' }); } - + const user = users[0]; if (user.audit_status !== 'pending') { return res.status(400).json({ success: false, message: '该用户不是待审核状态' }); } - + // 更新审核状态 const auditStatus = action === 'approve' ? 'approved' : 'rejected'; await db.execute( @@ -1680,7 +1683,7 @@ router.put('/:id/audit', auth, adminAuth, async (req, res) => { WHERE id = ?`, [auditStatus, note || null, req.user.id, userId] ); - + const message = action === 'approve' ? '用户审核通过' : '用户审核拒绝'; res.json({ success: true, message }); } catch (error) { @@ -1696,7 +1699,7 @@ router.get('/:id/audit-detail', auth, adminAuth, async (req, res) => { try { const db = getDB(); const userId = req.params.id; - + // 获取用户详细信息 const [users] = await db.execute( `SELECT u.id, u.username, u.phone, u.real_name, u.business_license, @@ -1708,11 +1711,11 @@ router.get('/:id/audit-detail', auth, adminAuth, async (req, res) => { WHERE u.id = ?`, [userId] ); - + if (users.length === 0) { return res.status(404).json({ success: false, message: '用户不存在' }); } - + res.json({ success: true, data: users[0] @@ -1722,5 +1725,127 @@ router.get('/:id/audit-detail', auth, adminAuth, async (req, res) => { res.status(500).json({ success: false, message: '获取用户审核详情失败' }); } }); +/** + * @swagger + * /api/users/{id}/distribute: + * put: + * summary: 设置用户分发状态 + * description: 更新指定用户的分发状态 + * tags: [Users] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: integer + * description: 用户ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - is_distribute + * properties: + * is_distribute: + * type: boolean + * description: 分发状态,true为启用分发,false为禁用分发 + * example: true + * responses: + * 200: + * description: 分发状态更新成功 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * example: true + * message: + * type: string + * example: "分发状态更新成功" + * is_distribute: + * type: boolean + * description: 更新后的分发状态 + * example: true + * 400: + * description: 请求参数错误 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * example: false + * message: + * type: string + * example: "分发状态无效" + * 404: + * description: 用户不存在 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * example: false + * message: + * type: string + * example: "用户不存在" + * 500: + * description: 服务器内部错误 + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * example: false + * message: + * type: string + * example: "服务器内部错误" + */ +router.put('/:id/distribute', auth, async (req, res) => { + try { + const db = getDB(); + const userId = req.params.id; + const { is_distribute } = req.body; + + if (typeof is_distribute !== 'boolean') { + return res.status(400).json({ success: false, message: '分发状态无效' }); + } + + // 检查用户是否存在 + const [users] = await db.execute( + 'SELECT id FROM users WHERE id = ?', + [userId] + ); + + if (users.length === 0) { + return res.status(404).json({ success: false, message: '用户不存在' }); + } + + // 更新分发状态 + await db.execute( + 'UPDATE users SET is_distribute = ? WHERE id = ?', + [is_distribute, userId] + ); + + res.json({ + success: true, + message: '分发状态更新成功', + is_distribute + }); + } catch (error) { + + } +}) module.exports = router; \ No newline at end of file diff --git a/scripts/fix_sql_syntax.js b/scripts/fix_sql_syntax.js deleted file mode 100644 index e10ef4f..0000000 --- a/scripts/fix_sql_syntax.js +++ /dev/null @@ -1,154 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -/** - * SQL 语法修复脚本:修复自动替换产生的 SQL 语法错误 - */ - -class SQLSyntaxFixer { - constructor() { - this.filesToFix = [ - 'services/matchingService.js', - 'routes/matchingAdmin.js', - 'routes/transfers.js', - 'routes/matching.js' - ]; - } - - /** - * 修复单个文件中的 SQL 语法错误 - * @param {string} filePath - 文件路径 - */ - async fixFile(filePath) { - const fullPath = path.join(process.cwd(), filePath); - - if (!fs.existsSync(fullPath)) { - console.log(`文件不存在: ${filePath}`); - return; - } - - console.log(`正在修复文件: ${filePath}`); - - let content = fs.readFileSync(fullPath, 'utf8'); - let originalContent = content; - - // 1. 修复 WHERE source_type = 'allocation' FROM transfers 的错误顺序 - content = content.replace( - /WHERE source_type = 'allocation' FROM transfers/g, - "FROM transfers WHERE source_type = 'allocation'" - ); - - // 2. 修复多个 WHERE 子句的问题 - content = content.replace( - /FROM transfers WHERE source_type = 'allocation'([\s\S]*?)WHERE/g, - "FROM transfers WHERE source_type = 'allocation'$1AND" - ); - - // 3. 修复 INSERT 语句中的引号问题 - content = content.replace( - /'allocation'/g, - "'allocation'" - ); - - // 4. 修复 JOIN 语句中的表别名问题 - content = content.replace( - /FROM transfers oa WHERE oa\.source_type = 'allocation'/g, - "FROM transfers oa WHERE oa.source_type = 'allocation'" - ); - - // 5. 修复复杂查询中的语法问题 - content = this.fixComplexQueries(content, filePath); - - if (content !== originalContent) { - fs.writeFileSync(fullPath, content); - console.log(`✓ 已修复: ${filePath}`); - } else { - console.log(`- 无需修复: ${filePath}`); - } - } - - /** - * 修复复杂查询 - * @param {string} content - 文件内容 - * @param {string} filePath - 文件路径 - * @returns {string} 修复后的内容 - */ - fixComplexQueries(content, filePath) { - if (filePath.includes('matchingService.js')) { - // 修复 matchingService.js 中的特定查询 - - // 修复获取匹配目标的查询 - content = content.replace( - /FROM transfers oa\s+WHERE oa\.source_type = 'allocation'\s+JOIN users u ON oa\.from_user_id = u\.id/g, - "FROM transfers oa JOIN users u ON oa.from_user_id = u.id WHERE oa.source_type = 'allocation'" - ); - - // 修复获取用户待处理分配的查询 - content = content.replace( - /SELECT \* FROM transfers WHERE source_type = 'allocation' WHERE matching_order_id = \? ORDER BY cycle_number, created_at/g, - "SELECT * FROM transfers WHERE source_type = 'allocation' AND matching_order_id = ? ORDER BY cycle_number, created_at" - ); - - // 修复检查周期完成的查询 - content = content.replace( - /SELECT COUNT\(\*\) as count FROM transfers WHERE source_type = 'allocation' WHERE matching_order_id = \? AND cycle_number = \? AND status = "pending"/g, - "SELECT COUNT(*) as count FROM transfers WHERE source_type = 'allocation' AND matching_order_id = ? AND cycle_number = ? AND status = 'pending'" - ); - } - - if (filePath.includes('matchingAdmin.js')) { - // 修复 matchingAdmin.js 中的查询 - content = content.replace( - /FROM transfers oa WHERE oa\.source_type = 'allocation'\s+JOIN/g, - "FROM transfers oa JOIN" - ); - - // 在 JOIN 后添加 WHERE 条件 - content = content.replace( - /(FROM transfers oa JOIN[\s\S]*?)WHERE(?!.*source_type)/g, - "$1WHERE oa.source_type = 'allocation' AND" - ); - } - - if (filePath.includes('matching.js')) { - // 修复 matching.js 中的查询 - content = content.replace( - /LEFT JOIN transfers oa ON mo\.id = oa\.matching_order_id WHERE oa\.source_type = 'allocation'/g, - "LEFT JOIN transfers oa ON mo.id = oa.matching_order_id AND oa.source_type = 'allocation'" - ); - } - - return content; - } - - /** - * 执行所有文件的修复 - */ - async fixAllFiles() { - console.log('开始修复 SQL 语法错误...'); - console.log('=' .repeat(60)); - - for (const filePath of this.filesToFix) { - try { - await this.fixFile(filePath); - } catch (error) { - console.error(`修复文件 ${filePath} 失败:`, error.message); - } - } - - console.log('\n' + '=' .repeat(60)); - console.log('✓ SQL 语法修复完成!'); - } -} - -async function main() { - const fixer = new SQLSyntaxFixer(); - await fixer.fixAllFiles(); -} - -// 如果直接运行此脚本 -if (require.main === module) { - main().catch(console.error); -} - -module.exports = SQLSyntaxFixer; \ No newline at end of file diff --git a/scripts/fix_table_aliases.js b/scripts/fix_table_aliases.js deleted file mode 100644 index e065977..0000000 --- a/scripts/fix_table_aliases.js +++ /dev/null @@ -1,133 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -/** - * 表别名修复脚本:修复 SQL 查询中的表别名问题 - */ - -class TableAliasFixer { - constructor() { - this.filesToFix = [ - 'services/matchingService.js', - 'routes/matchingAdmin.js', - 'routes/transfers.js', - 'routes/matching.js' - ]; - } - - /** - * 修复单个文件中的表别名问题 - * @param {string} filePath - 文件路径 - */ - async fixFile(filePath) { - const fullPath = path.join(process.cwd(), filePath); - - if (!fs.existsSync(fullPath)) { - console.log(`文件不存在: ${filePath}`); - return; - } - - console.log(`正在修复文件: ${filePath}`); - - let content = fs.readFileSync(fullPath, 'utf8'); - let originalContent = content; - - // 1. 修复 "FROM transfers WHERE source_type = 'allocation' oa" 的问题 - content = content.replace( - /FROM transfers WHERE source_type = 'allocation' (\w+)/g, - "FROM transfers $1 WHERE $1.source_type = 'allocation'" - ); - - // 2. 修复重复的 source_type 条件 - content = content.replace( - /FROM transfers WHERE source_type = 'allocation' (\w+) AND \1\.source_type = 'allocation'/g, - "FROM transfers $1 WHERE $1.source_type = 'allocation'" - ); - - // 3. 修复 "FROM transfers WHERE source_type = 'allocation'" 后面直接跟其他子句的情况 - content = content.replace( - /FROM transfers WHERE source_type = 'allocation'\s+(JOIN|ORDER|GROUP|LIMIT)/g, - "FROM transfers WHERE source_type = 'allocation' $1" - ); - - // 4. 修复子查询中的问题 - content = content.replace( - /\(SELECT[^)]*FROM transfers WHERE source_type = 'allocation' (\w+)/g, - (match, alias) => { - return match.replace( - `FROM transfers WHERE source_type = 'allocation' ${alias}`, - `FROM transfers ${alias} WHERE ${alias}.source_type = 'allocation'` - ); - } - ); - - // 5. 修复特定的查询模式 - content = this.fixSpecificPatterns(content, filePath); - - if (content !== originalContent) { - fs.writeFileSync(fullPath, content); - console.log(`✓ 已修复: ${filePath}`); - } else { - console.log(`- 无需修复: ${filePath}`); - } - } - - /** - * 修复特定的查询模式 - * @param {string} content - 文件内容 - * @param {string} filePath - 文件路径 - * @returns {string} 修复后的内容 - */ - fixSpecificPatterns(content, filePath) { - // 修复 SELECT 语句中的表别名问题 - content = content.replace( - /SELECT ([^F]*?) FROM transfers WHERE source_type = 'allocation' (\w+)/g, - "SELECT $1 FROM transfers $2 WHERE $2.source_type = 'allocation'" - ); - - // 修复 UPDATE 语句 - content = content.replace( - /UPDATE transfers WHERE source_type = 'allocation' SET/g, - "UPDATE transfers SET" - ); - - // 修复 WHERE 子句中的条件 - content = content.replace( - /WHERE source_type = 'allocation' AND (\w+)\./g, - "WHERE $1.source_type = 'allocation' AND $1." - ); - - return content; - } - - /** - * 执行所有文件的修复 - */ - async fixAllFiles() { - console.log('开始修复表别名问题...'); - console.log('=' .repeat(60)); - - for (const filePath of this.filesToFix) { - try { - await this.fixFile(filePath); - } catch (error) { - console.error(`修复文件 ${filePath} 失败:`, error.message); - } - } - - console.log('\n' + '=' .repeat(60)); - console.log('✓ 表别名修复完成!'); - } -} - -async function main() { - const fixer = new TableAliasFixer(); - await fixer.fixAllFiles(); -} - -// 如果直接运行此脚本 -if (require.main === module) { - main().catch(console.error); -} - -module.exports = TableAliasFixer; \ No newline at end of file diff --git a/scripts/import_china_regions.js b/scripts/import_china_regions.js new file mode 100644 index 0000000..99af78d --- /dev/null +++ b/scripts/import_china_regions.js @@ -0,0 +1,144 @@ +const fs = require('fs'); +const path = require('path'); +const mysql = require('mysql2/promise'); +require('dotenv').config(); + +// 数据库配置 +const dbConfig = { + host: process.env.DB_HOST || '114.55.111.44', + user: process.env.DB_USER || 'maov2', + password: process.env.DB_PASSWORD || '5fYhw8z6T62b7heS', + database: process.env.DB_NAME || 'maov2', +}; + +// 初始化数据库连接 +async function initDB() { + try { + const connection = await mysql.createConnection(dbConfig); + console.log('数据库连接成功'); + return connection; + } catch (error) { + console.error('数据库连接失败:', error); + throw error; + } +} + +// 递归解析省市区数据 +function parseRegionData(regions, parentCode = null, level = 1) { + const result = []; + let sortOrder = 1; + + for (const region of regions) { + // 添加当前区域 + result.push({ + code: region.code, + name: region.name, + parent_code: parentCode, + level: level, + sort_order: sortOrder++ + }); + + // 递归处理子区域 + if (region.children && region.children.length > 0) { + const childrenData = parseRegionData(region.children, region.code, level + 1); + result.push(...childrenData); + } + } + + return result; +} + +// 导入省市区数据 +async function importChinaRegions() { + let connection; + + try { + // 读取 JSON 数据文件 + const jsonFilePath = path.join(__dirname, 'pca-code.json'); + const jsonData = fs.readFileSync(jsonFilePath, 'utf8'); + const regionsData = JSON.parse(jsonData); + + console.log('成功读取省市区数据文件'); + + // 解析数据 + const parsedData = parseRegionData(regionsData); + console.log(`解析完成,共 ${parsedData.length} 条记录`); + + // 连接数据库 + connection = await initDB(); + + // 清空现有数据 + await connection.execute('DELETE FROM china_regions'); + console.log('已清空现有数据'); + + // 批量插入数据 + const batchSize = 100; + let insertedCount = 0; + + for (let i = 0; i < parsedData.length; i += batchSize) { + const batch = parsedData.slice(i, i + batchSize); + const values = batch.map(item => [ + item.code, + item.name, + item.parent_code, + item.level, + item.sort_order + ]); + + const placeholders = values.map(() => '(?, ?, ?, ?, ?)').join(', '); + const flatValues = values.flat(); + + await connection.execute( + `INSERT INTO china_regions (code, name, parent_code, level, sort_order) VALUES ${placeholders}`, + flatValues + ); + + insertedCount += batch.length; + console.log(`已插入 ${insertedCount}/${parsedData.length} 条记录`); + } + + // 统计导入结果 + const [provinceResult] = await connection.execute( + 'SELECT COUNT(*) as count FROM china_regions WHERE level = 1' + ); + const [cityResult] = await connection.execute( + 'SELECT COUNT(*) as count FROM china_regions WHERE level = 2' + ); + const [districtResult] = await connection.execute( + 'SELECT COUNT(*) as count FROM china_regions WHERE level = 3' + ); + const [totalResult] = await connection.execute( + 'SELECT COUNT(*) as count FROM china_regions' + ); + + console.log('\n=== 导入完成 ==='); + console.log(`省份数量: ${provinceResult[0].count}`); + console.log(`城市数量: ${cityResult[0].count}`); + console.log(`区县数量: ${districtResult[0].count}`); + console.log(`总记录数: ${totalResult[0].count}`); + + } catch (error) { + console.error('导入失败:', error); + throw error; + } finally { + if (connection) { + await connection.end(); + console.log('数据库连接已关闭'); + } + } +} + +// 如果直接运行此脚本 +if (require.main === module) { + importChinaRegions() + .then(() => { + console.log('省市区数据导入成功!'); + process.exit(0); + }) + .catch((error) => { + console.error('导入过程中发生错误:', error); + process.exit(1); + }); +} + +module.exports = { importChinaRegions }; \ No newline at end of file diff --git a/scripts/pca-code.json b/scripts/pca-code.json new file mode 100644 index 0000000..aaca902 --- /dev/null +++ b/scripts/pca-code.json @@ -0,0 +1,14625 @@ +[ + { + "code": "11", + "name": "北京市", + "children": [ + { + "code": "1101", + "name": "市辖区", + "children": [ + { + "code": "110101", + "name": "东城区" + }, + { + "code": "110102", + "name": "西城区" + }, + { + "code": "110105", + "name": "朝阳区" + }, + { + "code": "110106", + "name": "丰台区" + }, + { + "code": "110107", + "name": "石景山区" + }, + { + "code": "110108", + "name": "海淀区" + }, + { + "code": "110109", + "name": "门头沟区" + }, + { + "code": "110111", + "name": "房山区" + }, + { + "code": "110112", + "name": "通州区" + }, + { + "code": "110113", + "name": "顺义区" + }, + { + "code": "110114", + "name": "昌平区" + }, + { + "code": "110115", + "name": "大兴区" + }, + { + "code": "110116", + "name": "怀柔区" + }, + { + "code": "110117", + "name": "平谷区" + }, + { + "code": "110118", + "name": "密云区" + }, + { + "code": "110119", + "name": "延庆区" + } + ] + } + ] + }, + { + "code": "12", + "name": "天津市", + "children": [ + { + "code": "1201", + "name": "市辖区", + "children": [ + { + "code": "120101", + "name": "和平区" + }, + { + "code": "120102", + "name": "河东区" + }, + { + "code": "120103", + "name": "河西区" + }, + { + "code": "120104", + "name": "南开区" + }, + { + "code": "120105", + "name": "河北区" + }, + { + "code": "120106", + "name": "红桥区" + }, + { + "code": "120110", + "name": "东丽区" + }, + { + "code": "120111", + "name": "西青区" + }, + { + "code": "120112", + "name": "津南区" + }, + { + "code": "120113", + "name": "北辰区" + }, + { + "code": "120114", + "name": "武清区" + }, + { + "code": "120115", + "name": "宝坻区" + }, + { + "code": "120116", + "name": "滨海新区" + }, + { + "code": "120117", + "name": "宁河区" + }, + { + "code": "120118", + "name": "静海区" + }, + { + "code": "120119", + "name": "蓟州区" + } + ] + } + ] + }, + { + "code": "13", + "name": "河北省", + "children": [ + { + "code": "1301", + "name": "石家庄市", + "children": [ + { + "code": "130102", + "name": "长安区" + }, + { + "code": "130104", + "name": "桥西区" + }, + { + "code": "130105", + "name": "新华区" + }, + { + "code": "130107", + "name": "井陉矿区" + }, + { + "code": "130108", + "name": "裕华区" + }, + { + "code": "130109", + "name": "藁城区" + }, + { + "code": "130110", + "name": "鹿泉区" + }, + { + "code": "130111", + "name": "栾城区" + }, + { + "code": "130121", + "name": "井陉县" + }, + { + "code": "130123", + "name": "正定县" + }, + { + "code": "130125", + "name": "行唐县" + }, + { + "code": "130126", + "name": "灵寿县" + }, + { + "code": "130127", + "name": "高邑县" + }, + { + "code": "130128", + "name": "深泽县" + }, + { + "code": "130129", + "name": "赞皇县" + }, + { + "code": "130130", + "name": "无极县" + }, + { + "code": "130131", + "name": "平山县" + }, + { + "code": "130132", + "name": "元氏县" + }, + { + "code": "130133", + "name": "赵县" + }, + { + "code": "130171", + "name": "石家庄高新技术产业开发区" + }, + { + "code": "130172", + "name": "石家庄循环化工园区" + }, + { + "code": "130181", + "name": "辛集市" + }, + { + "code": "130183", + "name": "晋州市" + }, + { + "code": "130184", + "name": "新乐市" + } + ] + }, + { + "code": "1302", + "name": "唐山市", + "children": [ + { + "code": "130202", + "name": "路南区" + }, + { + "code": "130203", + "name": "路北区" + }, + { + "code": "130204", + "name": "古冶区" + }, + { + "code": "130205", + "name": "开平区" + }, + { + "code": "130207", + "name": "丰南区" + }, + { + "code": "130208", + "name": "丰润区" + }, + { + "code": "130209", + "name": "曹妃甸区" + }, + { + "code": "130224", + "name": "滦南县" + }, + { + "code": "130225", + "name": "乐亭县" + }, + { + "code": "130227", + "name": "迁西县" + }, + { + "code": "130229", + "name": "玉田县" + }, + { + "code": "130271", + "name": "河北唐山芦台经济开发区" + }, + { + "code": "130272", + "name": "唐山市汉沽管理区" + }, + { + "code": "130273", + "name": "唐山高新技术产业开发区" + }, + { + "code": "130274", + "name": "河北唐山海港经济开发区" + }, + { + "code": "130281", + "name": "遵化市" + }, + { + "code": "130283", + "name": "迁安市" + }, + { + "code": "130284", + "name": "滦州市" + } + ] + }, + { + "code": "1303", + "name": "秦皇岛市", + "children": [ + { + "code": "130302", + "name": "海港区" + }, + { + "code": "130303", + "name": "山海关区" + }, + { + "code": "130304", + "name": "北戴河区" + }, + { + "code": "130306", + "name": "抚宁区" + }, + { + "code": "130321", + "name": "青龙满族自治县" + }, + { + "code": "130322", + "name": "昌黎县" + }, + { + "code": "130324", + "name": "卢龙县" + }, + { + "code": "130371", + "name": "秦皇岛市经济技术开发区" + }, + { + "code": "130372", + "name": "北戴河新区" + } + ] + }, + { + "code": "1304", + "name": "邯郸市", + "children": [ + { + "code": "130402", + "name": "邯山区" + }, + { + "code": "130403", + "name": "丛台区" + }, + { + "code": "130404", + "name": "复兴区" + }, + { + "code": "130406", + "name": "峰峰矿区" + }, + { + "code": "130407", + "name": "肥乡区" + }, + { + "code": "130408", + "name": "永年区" + }, + { + "code": "130423", + "name": "临漳县" + }, + { + "code": "130424", + "name": "成安县" + }, + { + "code": "130425", + "name": "大名县" + }, + { + "code": "130426", + "name": "涉县" + }, + { + "code": "130427", + "name": "磁县" + }, + { + "code": "130430", + "name": "邱县" + }, + { + "code": "130431", + "name": "鸡泽县" + }, + { + "code": "130432", + "name": "广平县" + }, + { + "code": "130433", + "name": "馆陶县" + }, + { + "code": "130434", + "name": "魏县" + }, + { + "code": "130435", + "name": "曲周县" + }, + { + "code": "130471", + "name": "邯郸经济技术开发区" + }, + { + "code": "130473", + "name": "邯郸冀南新区" + }, + { + "code": "130481", + "name": "武安市" + } + ] + }, + { + "code": "1305", + "name": "邢台市", + "children": [ + { + "code": "130502", + "name": "襄都区" + }, + { + "code": "130503", + "name": "信都区" + }, + { + "code": "130505", + "name": "任泽区" + }, + { + "code": "130506", + "name": "南和区" + }, + { + "code": "130522", + "name": "临城县" + }, + { + "code": "130523", + "name": "内丘县" + }, + { + "code": "130524", + "name": "柏乡县" + }, + { + "code": "130525", + "name": "隆尧县" + }, + { + "code": "130528", + "name": "宁晋县" + }, + { + "code": "130529", + "name": "巨鹿县" + }, + { + "code": "130530", + "name": "新河县" + }, + { + "code": "130531", + "name": "广宗县" + }, + { + "code": "130532", + "name": "平乡县" + }, + { + "code": "130533", + "name": "威县" + }, + { + "code": "130534", + "name": "清河县" + }, + { + "code": "130535", + "name": "临西县" + }, + { + "code": "130571", + "name": "河北邢台经济开发区" + }, + { + "code": "130581", + "name": "南宫市" + }, + { + "code": "130582", + "name": "沙河市" + } + ] + }, + { + "code": "1306", + "name": "保定市", + "children": [ + { + "code": "130602", + "name": "竞秀区" + }, + { + "code": "130606", + "name": "莲池区" + }, + { + "code": "130607", + "name": "满城区" + }, + { + "code": "130608", + "name": "清苑区" + }, + { + "code": "130609", + "name": "徐水区" + }, + { + "code": "130623", + "name": "涞水县" + }, + { + "code": "130624", + "name": "阜平县" + }, + { + "code": "130626", + "name": "定兴县" + }, + { + "code": "130627", + "name": "唐县" + }, + { + "code": "130628", + "name": "高阳县" + }, + { + "code": "130629", + "name": "容城县" + }, + { + "code": "130630", + "name": "涞源县" + }, + { + "code": "130631", + "name": "望都县" + }, + { + "code": "130632", + "name": "安新县" + }, + { + "code": "130633", + "name": "易县" + }, + { + "code": "130634", + "name": "曲阳县" + }, + { + "code": "130635", + "name": "蠡县" + }, + { + "code": "130636", + "name": "顺平县" + }, + { + "code": "130637", + "name": "博野县" + }, + { + "code": "130638", + "name": "雄县" + }, + { + "code": "130671", + "name": "保定高新技术产业开发区" + }, + { + "code": "130672", + "name": "保定白沟新城" + }, + { + "code": "130681", + "name": "涿州市" + }, + { + "code": "130682", + "name": "定州市" + }, + { + "code": "130683", + "name": "安国市" + }, + { + "code": "130684", + "name": "高碑店市" + } + ] + }, + { + "code": "1307", + "name": "张家口市", + "children": [ + { + "code": "130702", + "name": "桥东区" + }, + { + "code": "130703", + "name": "桥西区" + }, + { + "code": "130705", + "name": "宣化区" + }, + { + "code": "130706", + "name": "下花园区" + }, + { + "code": "130708", + "name": "万全区" + }, + { + "code": "130709", + "name": "崇礼区" + }, + { + "code": "130722", + "name": "张北县" + }, + { + "code": "130723", + "name": "康保县" + }, + { + "code": "130724", + "name": "沽源县" + }, + { + "code": "130725", + "name": "尚义县" + }, + { + "code": "130726", + "name": "蔚县" + }, + { + "code": "130727", + "name": "阳原县" + }, + { + "code": "130728", + "name": "怀安县" + }, + { + "code": "130730", + "name": "怀来县" + }, + { + "code": "130731", + "name": "涿鹿县" + }, + { + "code": "130732", + "name": "赤城县" + }, + { + "code": "130771", + "name": "张家口经济开发区" + }, + { + "code": "130772", + "name": "张家口市察北管理区" + }, + { + "code": "130773", + "name": "张家口市塞北管理区" + } + ] + }, + { + "code": "1308", + "name": "承德市", + "children": [ + { + "code": "130802", + "name": "双桥区" + }, + { + "code": "130803", + "name": "双滦区" + }, + { + "code": "130804", + "name": "鹰手营子矿区" + }, + { + "code": "130821", + "name": "承德县" + }, + { + "code": "130822", + "name": "兴隆县" + }, + { + "code": "130824", + "name": "滦平县" + }, + { + "code": "130825", + "name": "隆化县" + }, + { + "code": "130826", + "name": "丰宁满族自治县" + }, + { + "code": "130827", + "name": "宽城满族自治县" + }, + { + "code": "130828", + "name": "围场满族蒙古族自治县" + }, + { + "code": "130871", + "name": "承德高新技术产业开发区" + }, + { + "code": "130881", + "name": "平泉市" + } + ] + }, + { + "code": "1309", + "name": "沧州市", + "children": [ + { + "code": "130902", + "name": "新华区" + }, + { + "code": "130903", + "name": "运河区" + }, + { + "code": "130921", + "name": "沧县" + }, + { + "code": "130922", + "name": "青县" + }, + { + "code": "130923", + "name": "东光县" + }, + { + "code": "130924", + "name": "海兴县" + }, + { + "code": "130925", + "name": "盐山县" + }, + { + "code": "130926", + "name": "肃宁县" + }, + { + "code": "130927", + "name": "南皮县" + }, + { + "code": "130928", + "name": "吴桥县" + }, + { + "code": "130929", + "name": "献县" + }, + { + "code": "130930", + "name": "孟村回族自治县" + }, + { + "code": "130971", + "name": "河北沧州经济开发区" + }, + { + "code": "130972", + "name": "沧州高新技术产业开发区" + }, + { + "code": "130973", + "name": "沧州渤海新区" + }, + { + "code": "130981", + "name": "泊头市" + }, + { + "code": "130982", + "name": "任丘市" + }, + { + "code": "130983", + "name": "黄骅市" + }, + { + "code": "130984", + "name": "河间市" + } + ] + }, + { + "code": "1310", + "name": "廊坊市", + "children": [ + { + "code": "131002", + "name": "安次区" + }, + { + "code": "131003", + "name": "广阳区" + }, + { + "code": "131022", + "name": "固安县" + }, + { + "code": "131023", + "name": "永清县" + }, + { + "code": "131024", + "name": "香河县" + }, + { + "code": "131025", + "name": "大城县" + }, + { + "code": "131026", + "name": "文安县" + }, + { + "code": "131028", + "name": "大厂回族自治县" + }, + { + "code": "131071", + "name": "廊坊经济技术开发区" + }, + { + "code": "131081", + "name": "霸州市" + }, + { + "code": "131082", + "name": "三河市" + } + ] + }, + { + "code": "1311", + "name": "衡水市", + "children": [ + { + "code": "131102", + "name": "桃城区" + }, + { + "code": "131103", + "name": "冀州区" + }, + { + "code": "131121", + "name": "枣强县" + }, + { + "code": "131122", + "name": "武邑县" + }, + { + "code": "131123", + "name": "武强县" + }, + { + "code": "131124", + "name": "饶阳县" + }, + { + "code": "131125", + "name": "安平县" + }, + { + "code": "131126", + "name": "故城县" + }, + { + "code": "131127", + "name": "景县" + }, + { + "code": "131128", + "name": "阜城县" + }, + { + "code": "131171", + "name": "河北衡水高新技术产业开发区" + }, + { + "code": "131172", + "name": "衡水滨湖新区" + }, + { + "code": "131182", + "name": "深州市" + } + ] + } + ] + }, + { + "code": "14", + "name": "山西省", + "children": [ + { + "code": "1401", + "name": "太原市", + "children": [ + { + "code": "140105", + "name": "小店区" + }, + { + "code": "140106", + "name": "迎泽区" + }, + { + "code": "140107", + "name": "杏花岭区" + }, + { + "code": "140108", + "name": "尖草坪区" + }, + { + "code": "140109", + "name": "万柏林区" + }, + { + "code": "140110", + "name": "晋源区" + }, + { + "code": "140121", + "name": "清徐县" + }, + { + "code": "140122", + "name": "阳曲县" + }, + { + "code": "140123", + "name": "娄烦县" + }, + { + "code": "140171", + "name": "山西转型综合改革示范区" + }, + { + "code": "140181", + "name": "古交市" + } + ] + }, + { + "code": "1402", + "name": "大同市", + "children": [ + { + "code": "140212", + "name": "新荣区" + }, + { + "code": "140213", + "name": "平城区" + }, + { + "code": "140214", + "name": "云冈区" + }, + { + "code": "140215", + "name": "云州区" + }, + { + "code": "140221", + "name": "阳高县" + }, + { + "code": "140222", + "name": "天镇县" + }, + { + "code": "140223", + "name": "广灵县" + }, + { + "code": "140224", + "name": "灵丘县" + }, + { + "code": "140225", + "name": "浑源县" + }, + { + "code": "140226", + "name": "左云县" + }, + { + "code": "140271", + "name": "山西大同经济开发区" + } + ] + }, + { + "code": "1403", + "name": "阳泉市", + "children": [ + { + "code": "140302", + "name": "城区" + }, + { + "code": "140303", + "name": "矿区" + }, + { + "code": "140311", + "name": "郊区" + }, + { + "code": "140321", + "name": "平定县" + }, + { + "code": "140322", + "name": "盂县" + } + ] + }, + { + "code": "1404", + "name": "长治市", + "children": [ + { + "code": "140403", + "name": "潞州区" + }, + { + "code": "140404", + "name": "上党区" + }, + { + "code": "140405", + "name": "屯留区" + }, + { + "code": "140406", + "name": "潞城区" + }, + { + "code": "140423", + "name": "襄垣县" + }, + { + "code": "140425", + "name": "平顺县" + }, + { + "code": "140426", + "name": "黎城县" + }, + { + "code": "140427", + "name": "壶关县" + }, + { + "code": "140428", + "name": "长子县" + }, + { + "code": "140429", + "name": "武乡县" + }, + { + "code": "140430", + "name": "沁县" + }, + { + "code": "140431", + "name": "沁源县" + } + ] + }, + { + "code": "1405", + "name": "晋城市", + "children": [ + { + "code": "140502", + "name": "城区" + }, + { + "code": "140521", + "name": "沁水县" + }, + { + "code": "140522", + "name": "阳城县" + }, + { + "code": "140524", + "name": "陵川县" + }, + { + "code": "140525", + "name": "泽州县" + }, + { + "code": "140581", + "name": "高平市" + } + ] + }, + { + "code": "1406", + "name": "朔州市", + "children": [ + { + "code": "140602", + "name": "朔城区" + }, + { + "code": "140603", + "name": "平鲁区" + }, + { + "code": "140621", + "name": "山阴县" + }, + { + "code": "140622", + "name": "应县" + }, + { + "code": "140623", + "name": "右玉县" + }, + { + "code": "140671", + "name": "山西朔州经济开发区" + }, + { + "code": "140681", + "name": "怀仁市" + } + ] + }, + { + "code": "1407", + "name": "晋中市", + "children": [ + { + "code": "140702", + "name": "榆次区" + }, + { + "code": "140703", + "name": "太谷区" + }, + { + "code": "140721", + "name": "榆社县" + }, + { + "code": "140722", + "name": "左权县" + }, + { + "code": "140723", + "name": "和顺县" + }, + { + "code": "140724", + "name": "昔阳县" + }, + { + "code": "140725", + "name": "寿阳县" + }, + { + "code": "140727", + "name": "祁县" + }, + { + "code": "140728", + "name": "平遥县" + }, + { + "code": "140729", + "name": "灵石县" + }, + { + "code": "140781", + "name": "介休市" + } + ] + }, + { + "code": "1408", + "name": "运城市", + "children": [ + { + "code": "140802", + "name": "盐湖区" + }, + { + "code": "140821", + "name": "临猗县" + }, + { + "code": "140822", + "name": "万荣县" + }, + { + "code": "140823", + "name": "闻喜县" + }, + { + "code": "140824", + "name": "稷山县" + }, + { + "code": "140825", + "name": "新绛县" + }, + { + "code": "140826", + "name": "绛县" + }, + { + "code": "140827", + "name": "垣曲县" + }, + { + "code": "140828", + "name": "夏县" + }, + { + "code": "140829", + "name": "平陆县" + }, + { + "code": "140830", + "name": "芮城县" + }, + { + "code": "140881", + "name": "永济市" + }, + { + "code": "140882", + "name": "河津市" + } + ] + }, + { + "code": "1409", + "name": "忻州市", + "children": [ + { + "code": "140902", + "name": "忻府区" + }, + { + "code": "140921", + "name": "定襄县" + }, + { + "code": "140922", + "name": "五台县" + }, + { + "code": "140923", + "name": "代县" + }, + { + "code": "140924", + "name": "繁峙县" + }, + { + "code": "140925", + "name": "宁武县" + }, + { + "code": "140926", + "name": "静乐县" + }, + { + "code": "140927", + "name": "神池县" + }, + { + "code": "140928", + "name": "五寨县" + }, + { + "code": "140929", + "name": "岢岚县" + }, + { + "code": "140930", + "name": "河曲县" + }, + { + "code": "140931", + "name": "保德县" + }, + { + "code": "140932", + "name": "偏关县" + }, + { + "code": "140971", + "name": "五台山风景名胜区" + }, + { + "code": "140981", + "name": "原平市" + } + ] + }, + { + "code": "1410", + "name": "临汾市", + "children": [ + { + "code": "141002", + "name": "尧都区" + }, + { + "code": "141021", + "name": "曲沃县" + }, + { + "code": "141022", + "name": "翼城县" + }, + { + "code": "141023", + "name": "襄汾县" + }, + { + "code": "141024", + "name": "洪洞县" + }, + { + "code": "141025", + "name": "古县" + }, + { + "code": "141026", + "name": "安泽县" + }, + { + "code": "141027", + "name": "浮山县" + }, + { + "code": "141028", + "name": "吉县" + }, + { + "code": "141029", + "name": "乡宁县" + }, + { + "code": "141030", + "name": "大宁县" + }, + { + "code": "141031", + "name": "隰县" + }, + { + "code": "141032", + "name": "永和县" + }, + { + "code": "141033", + "name": "蒲县" + }, + { + "code": "141034", + "name": "汾西县" + }, + { + "code": "141081", + "name": "侯马市" + }, + { + "code": "141082", + "name": "霍州市" + } + ] + }, + { + "code": "1411", + "name": "吕梁市", + "children": [ + { + "code": "141102", + "name": "离石区" + }, + { + "code": "141121", + "name": "文水县" + }, + { + "code": "141122", + "name": "交城县" + }, + { + "code": "141123", + "name": "兴县" + }, + { + "code": "141124", + "name": "临县" + }, + { + "code": "141125", + "name": "柳林县" + }, + { + "code": "141126", + "name": "石楼县" + }, + { + "code": "141127", + "name": "岚县" + }, + { + "code": "141128", + "name": "方山县" + }, + { + "code": "141129", + "name": "中阳县" + }, + { + "code": "141130", + "name": "交口县" + }, + { + "code": "141181", + "name": "孝义市" + }, + { + "code": "141182", + "name": "汾阳市" + } + ] + } + ] + }, + { + "code": "15", + "name": "内蒙古自治区", + "children": [ + { + "code": "1501", + "name": "呼和浩特市", + "children": [ + { + "code": "150102", + "name": "新城区" + }, + { + "code": "150103", + "name": "回民区" + }, + { + "code": "150104", + "name": "玉泉区" + }, + { + "code": "150105", + "name": "赛罕区" + }, + { + "code": "150121", + "name": "土默特左旗" + }, + { + "code": "150122", + "name": "托克托县" + }, + { + "code": "150123", + "name": "和林格尔县" + }, + { + "code": "150124", + "name": "清水河县" + }, + { + "code": "150125", + "name": "武川县" + }, + { + "code": "150172", + "name": "呼和浩特经济技术开发区" + } + ] + }, + { + "code": "1502", + "name": "包头市", + "children": [ + { + "code": "150202", + "name": "东河区" + }, + { + "code": "150203", + "name": "昆都仑区" + }, + { + "code": "150204", + "name": "青山区" + }, + { + "code": "150205", + "name": "石拐区" + }, + { + "code": "150206", + "name": "白云鄂博矿区" + }, + { + "code": "150207", + "name": "九原区" + }, + { + "code": "150221", + "name": "土默特右旗" + }, + { + "code": "150222", + "name": "固阳县" + }, + { + "code": "150223", + "name": "达尔罕茂明安联合旗" + }, + { + "code": "150271", + "name": "包头稀土高新技术产业开发区" + } + ] + }, + { + "code": "1503", + "name": "乌海市", + "children": [ + { + "code": "150302", + "name": "海勃湾区" + }, + { + "code": "150303", + "name": "海南区" + }, + { + "code": "150304", + "name": "乌达区" + } + ] + }, + { + "code": "1504", + "name": "赤峰市", + "children": [ + { + "code": "150402", + "name": "红山区" + }, + { + "code": "150403", + "name": "元宝山区" + }, + { + "code": "150404", + "name": "松山区" + }, + { + "code": "150421", + "name": "阿鲁科尔沁旗" + }, + { + "code": "150422", + "name": "巴林左旗" + }, + { + "code": "150423", + "name": "巴林右旗" + }, + { + "code": "150424", + "name": "林西县" + }, + { + "code": "150425", + "name": "克什克腾旗" + }, + { + "code": "150426", + "name": "翁牛特旗" + }, + { + "code": "150428", + "name": "喀喇沁旗" + }, + { + "code": "150429", + "name": "宁城县" + }, + { + "code": "150430", + "name": "敖汉旗" + } + ] + }, + { + "code": "1505", + "name": "通辽市", + "children": [ + { + "code": "150502", + "name": "科尔沁区" + }, + { + "code": "150521", + "name": "科尔沁左翼中旗" + }, + { + "code": "150522", + "name": "科尔沁左翼后旗" + }, + { + "code": "150523", + "name": "开鲁县" + }, + { + "code": "150524", + "name": "库伦旗" + }, + { + "code": "150525", + "name": "奈曼旗" + }, + { + "code": "150526", + "name": "扎鲁特旗" + }, + { + "code": "150571", + "name": "通辽经济技术开发区" + }, + { + "code": "150581", + "name": "霍林郭勒市" + } + ] + }, + { + "code": "1506", + "name": "鄂尔多斯市", + "children": [ + { + "code": "150602", + "name": "东胜区" + }, + { + "code": "150603", + "name": "康巴什区" + }, + { + "code": "150621", + "name": "达拉特旗" + }, + { + "code": "150622", + "name": "准格尔旗" + }, + { + "code": "150623", + "name": "鄂托克前旗" + }, + { + "code": "150624", + "name": "鄂托克旗" + }, + { + "code": "150625", + "name": "杭锦旗" + }, + { + "code": "150626", + "name": "乌审旗" + }, + { + "code": "150627", + "name": "伊金霍洛旗" + } + ] + }, + { + "code": "1507", + "name": "呼伦贝尔市", + "children": [ + { + "code": "150702", + "name": "海拉尔区" + }, + { + "code": "150703", + "name": "扎赉诺尔区" + }, + { + "code": "150721", + "name": "阿荣旗" + }, + { + "code": "150722", + "name": "莫力达瓦达斡尔族自治旗" + }, + { + "code": "150723", + "name": "鄂伦春自治旗" + }, + { + "code": "150724", + "name": "鄂温克族自治旗" + }, + { + "code": "150725", + "name": "陈巴尔虎旗" + }, + { + "code": "150726", + "name": "新巴尔虎左旗" + }, + { + "code": "150727", + "name": "新巴尔虎右旗" + }, + { + "code": "150781", + "name": "满洲里市" + }, + { + "code": "150782", + "name": "牙克石市" + }, + { + "code": "150783", + "name": "扎兰屯市" + }, + { + "code": "150784", + "name": "额尔古纳市" + }, + { + "code": "150785", + "name": "根河市" + } + ] + }, + { + "code": "1508", + "name": "巴彦淖尔市", + "children": [ + { + "code": "150802", + "name": "临河区" + }, + { + "code": "150821", + "name": "五原县" + }, + { + "code": "150822", + "name": "磴口县" + }, + { + "code": "150823", + "name": "乌拉特前旗" + }, + { + "code": "150824", + "name": "乌拉特中旗" + }, + { + "code": "150825", + "name": "乌拉特后旗" + }, + { + "code": "150826", + "name": "杭锦后旗" + } + ] + }, + { + "code": "1509", + "name": "乌兰察布市", + "children": [ + { + "code": "150902", + "name": "集宁区" + }, + { + "code": "150921", + "name": "卓资县" + }, + { + "code": "150922", + "name": "化德县" + }, + { + "code": "150923", + "name": "商都县" + }, + { + "code": "150924", + "name": "兴和县" + }, + { + "code": "150925", + "name": "凉城县" + }, + { + "code": "150926", + "name": "察哈尔右翼前旗" + }, + { + "code": "150927", + "name": "察哈尔右翼中旗" + }, + { + "code": "150928", + "name": "察哈尔右翼后旗" + }, + { + "code": "150929", + "name": "四子王旗" + }, + { + "code": "150981", + "name": "丰镇市" + } + ] + }, + { + "code": "1522", + "name": "兴安盟", + "children": [ + { + "code": "152201", + "name": "乌兰浩特市" + }, + { + "code": "152202", + "name": "阿尔山市" + }, + { + "code": "152221", + "name": "科尔沁右翼前旗" + }, + { + "code": "152222", + "name": "科尔沁右翼中旗" + }, + { + "code": "152223", + "name": "扎赉特旗" + }, + { + "code": "152224", + "name": "突泉县" + } + ] + }, + { + "code": "1525", + "name": "锡林郭勒盟", + "children": [ + { + "code": "152501", + "name": "二连浩特市" + }, + { + "code": "152502", + "name": "锡林浩特市" + }, + { + "code": "152522", + "name": "阿巴嘎旗" + }, + { + "code": "152523", + "name": "苏尼特左旗" + }, + { + "code": "152524", + "name": "苏尼特右旗" + }, + { + "code": "152525", + "name": "东乌珠穆沁旗" + }, + { + "code": "152526", + "name": "西乌珠穆沁旗" + }, + { + "code": "152527", + "name": "太仆寺旗" + }, + { + "code": "152528", + "name": "镶黄旗" + }, + { + "code": "152529", + "name": "正镶白旗" + }, + { + "code": "152530", + "name": "正蓝旗" + }, + { + "code": "152531", + "name": "多伦县" + }, + { + "code": "152571", + "name": "乌拉盖管理区管委会" + } + ] + }, + { + "code": "1529", + "name": "阿拉善盟", + "children": [ + { + "code": "152921", + "name": "阿拉善左旗" + }, + { + "code": "152922", + "name": "阿拉善右旗" + }, + { + "code": "152923", + "name": "额济纳旗" + }, + { + "code": "152971", + "name": "内蒙古阿拉善高新技术产业开发区" + } + ] + } + ] + }, + { + "code": "21", + "name": "辽宁省", + "children": [ + { + "code": "2101", + "name": "沈阳市", + "children": [ + { + "code": "210102", + "name": "和平区" + }, + { + "code": "210103", + "name": "沈河区" + }, + { + "code": "210104", + "name": "大东区" + }, + { + "code": "210105", + "name": "皇姑区" + }, + { + "code": "210106", + "name": "铁西区" + }, + { + "code": "210111", + "name": "苏家屯区" + }, + { + "code": "210112", + "name": "浑南区" + }, + { + "code": "210113", + "name": "沈北新区" + }, + { + "code": "210114", + "name": "于洪区" + }, + { + "code": "210115", + "name": "辽中区" + }, + { + "code": "210123", + "name": "康平县" + }, + { + "code": "210124", + "name": "法库县" + }, + { + "code": "210181", + "name": "新民市" + } + ] + }, + { + "code": "2102", + "name": "大连市", + "children": [ + { + "code": "210202", + "name": "中山区" + }, + { + "code": "210203", + "name": "西岗区" + }, + { + "code": "210204", + "name": "沙河口区" + }, + { + "code": "210211", + "name": "甘井子区" + }, + { + "code": "210212", + "name": "旅顺口区" + }, + { + "code": "210213", + "name": "金州区" + }, + { + "code": "210214", + "name": "普兰店区" + }, + { + "code": "210224", + "name": "长海县" + }, + { + "code": "210281", + "name": "瓦房店市" + }, + { + "code": "210283", + "name": "庄河市" + } + ] + }, + { + "code": "2103", + "name": "鞍山市", + "children": [ + { + "code": "210302", + "name": "铁东区" + }, + { + "code": "210303", + "name": "铁西区" + }, + { + "code": "210304", + "name": "立山区" + }, + { + "code": "210311", + "name": "千山区" + }, + { + "code": "210321", + "name": "台安县" + }, + { + "code": "210323", + "name": "岫岩满族自治县" + }, + { + "code": "210381", + "name": "海城市" + } + ] + }, + { + "code": "2104", + "name": "抚顺市", + "children": [ + { + "code": "210402", + "name": "新抚区" + }, + { + "code": "210403", + "name": "东洲区" + }, + { + "code": "210404", + "name": "望花区" + }, + { + "code": "210411", + "name": "顺城区" + }, + { + "code": "210421", + "name": "抚顺县" + }, + { + "code": "210422", + "name": "新宾满族自治县" + }, + { + "code": "210423", + "name": "清原满族自治县" + } + ] + }, + { + "code": "2105", + "name": "本溪市", + "children": [ + { + "code": "210502", + "name": "平山区" + }, + { + "code": "210503", + "name": "溪湖区" + }, + { + "code": "210504", + "name": "明山区" + }, + { + "code": "210505", + "name": "南芬区" + }, + { + "code": "210521", + "name": "本溪满族自治县" + }, + { + "code": "210522", + "name": "桓仁满族自治县" + } + ] + }, + { + "code": "2106", + "name": "丹东市", + "children": [ + { + "code": "210602", + "name": "元宝区" + }, + { + "code": "210603", + "name": "振兴区" + }, + { + "code": "210604", + "name": "振安区" + }, + { + "code": "210624", + "name": "宽甸满族自治县" + }, + { + "code": "210681", + "name": "东港市" + }, + { + "code": "210682", + "name": "凤城市" + } + ] + }, + { + "code": "2107", + "name": "锦州市", + "children": [ + { + "code": "210702", + "name": "古塔区" + }, + { + "code": "210703", + "name": "凌河区" + }, + { + "code": "210711", + "name": "太和区" + }, + { + "code": "210726", + "name": "黑山县" + }, + { + "code": "210727", + "name": "义县" + }, + { + "code": "210781", + "name": "凌海市" + }, + { + "code": "210782", + "name": "北镇市" + } + ] + }, + { + "code": "2108", + "name": "营口市", + "children": [ + { + "code": "210802", + "name": "站前区" + }, + { + "code": "210803", + "name": "西市区" + }, + { + "code": "210804", + "name": "鲅鱼圈区" + }, + { + "code": "210811", + "name": "老边区" + }, + { + "code": "210881", + "name": "盖州市" + }, + { + "code": "210882", + "name": "大石桥市" + } + ] + }, + { + "code": "2109", + "name": "阜新市", + "children": [ + { + "code": "210902", + "name": "海州区" + }, + { + "code": "210903", + "name": "新邱区" + }, + { + "code": "210904", + "name": "太平区" + }, + { + "code": "210905", + "name": "清河门区" + }, + { + "code": "210911", + "name": "细河区" + }, + { + "code": "210921", + "name": "阜新蒙古族自治县" + }, + { + "code": "210922", + "name": "彰武县" + } + ] + }, + { + "code": "2110", + "name": "辽阳市", + "children": [ + { + "code": "211002", + "name": "白塔区" + }, + { + "code": "211003", + "name": "文圣区" + }, + { + "code": "211004", + "name": "宏伟区" + }, + { + "code": "211005", + "name": "弓长岭区" + }, + { + "code": "211011", + "name": "太子河区" + }, + { + "code": "211021", + "name": "辽阳县" + }, + { + "code": "211081", + "name": "灯塔市" + } + ] + }, + { + "code": "2111", + "name": "盘锦市", + "children": [ + { + "code": "211102", + "name": "双台子区" + }, + { + "code": "211103", + "name": "兴隆台区" + }, + { + "code": "211104", + "name": "大洼区" + }, + { + "code": "211122", + "name": "盘山县" + } + ] + }, + { + "code": "2112", + "name": "铁岭市", + "children": [ + { + "code": "211202", + "name": "银州区" + }, + { + "code": "211204", + "name": "清河区" + }, + { + "code": "211221", + "name": "铁岭县" + }, + { + "code": "211223", + "name": "西丰县" + }, + { + "code": "211224", + "name": "昌图县" + }, + { + "code": "211281", + "name": "调兵山市" + }, + { + "code": "211282", + "name": "开原市" + } + ] + }, + { + "code": "2113", + "name": "朝阳市", + "children": [ + { + "code": "211302", + "name": "双塔区" + }, + { + "code": "211303", + "name": "龙城区" + }, + { + "code": "211321", + "name": "朝阳县" + }, + { + "code": "211322", + "name": "建平县" + }, + { + "code": "211324", + "name": "喀喇沁左翼蒙古族自治县" + }, + { + "code": "211381", + "name": "北票市" + }, + { + "code": "211382", + "name": "凌源市" + } + ] + }, + { + "code": "2114", + "name": "葫芦岛市", + "children": [ + { + "code": "211402", + "name": "连山区" + }, + { + "code": "211403", + "name": "龙港区" + }, + { + "code": "211404", + "name": "南票区" + }, + { + "code": "211421", + "name": "绥中县" + }, + { + "code": "211422", + "name": "建昌县" + }, + { + "code": "211481", + "name": "兴城市" + } + ] + } + ] + }, + { + "code": "22", + "name": "吉林省", + "children": [ + { + "code": "2201", + "name": "长春市", + "children": [ + { + "code": "220102", + "name": "南关区" + }, + { + "code": "220103", + "name": "宽城区" + }, + { + "code": "220104", + "name": "朝阳区" + }, + { + "code": "220105", + "name": "二道区" + }, + { + "code": "220106", + "name": "绿园区" + }, + { + "code": "220112", + "name": "双阳区" + }, + { + "code": "220113", + "name": "九台区" + }, + { + "code": "220122", + "name": "农安县" + }, + { + "code": "220171", + "name": "长春经济技术开发区" + }, + { + "code": "220172", + "name": "长春净月高新技术产业开发区" + }, + { + "code": "220173", + "name": "长春高新技术产业开发区" + }, + { + "code": "220174", + "name": "长春汽车经济技术开发区" + }, + { + "code": "220182", + "name": "榆树市" + }, + { + "code": "220183", + "name": "德惠市" + }, + { + "code": "220184", + "name": "公主岭市" + } + ] + }, + { + "code": "2202", + "name": "吉林市", + "children": [ + { + "code": "220202", + "name": "昌邑区" + }, + { + "code": "220203", + "name": "龙潭区" + }, + { + "code": "220204", + "name": "船营区" + }, + { + "code": "220211", + "name": "丰满区" + }, + { + "code": "220221", + "name": "永吉县" + }, + { + "code": "220271", + "name": "吉林经济开发区" + }, + { + "code": "220272", + "name": "吉林高新技术产业开发区" + }, + { + "code": "220273", + "name": "吉林中国新加坡食品区" + }, + { + "code": "220281", + "name": "蛟河市" + }, + { + "code": "220282", + "name": "桦甸市" + }, + { + "code": "220283", + "name": "舒兰市" + }, + { + "code": "220284", + "name": "磐石市" + } + ] + }, + { + "code": "2203", + "name": "四平市", + "children": [ + { + "code": "220302", + "name": "铁西区" + }, + { + "code": "220303", + "name": "铁东区" + }, + { + "code": "220322", + "name": "梨树县" + }, + { + "code": "220323", + "name": "伊通满族自治县" + }, + { + "code": "220382", + "name": "双辽市" + } + ] + }, + { + "code": "2204", + "name": "辽源市", + "children": [ + { + "code": "220402", + "name": "龙山区" + }, + { + "code": "220403", + "name": "西安区" + }, + { + "code": "220421", + "name": "东丰县" + }, + { + "code": "220422", + "name": "东辽县" + } + ] + }, + { + "code": "2205", + "name": "通化市", + "children": [ + { + "code": "220502", + "name": "东昌区" + }, + { + "code": "220503", + "name": "二道江区" + }, + { + "code": "220521", + "name": "通化县" + }, + { + "code": "220523", + "name": "辉南县" + }, + { + "code": "220524", + "name": "柳河县" + }, + { + "code": "220581", + "name": "梅河口市" + }, + { + "code": "220582", + "name": "集安市" + } + ] + }, + { + "code": "2206", + "name": "白山市", + "children": [ + { + "code": "220602", + "name": "浑江区" + }, + { + "code": "220605", + "name": "江源区" + }, + { + "code": "220621", + "name": "抚松县" + }, + { + "code": "220622", + "name": "靖宇县" + }, + { + "code": "220623", + "name": "长白朝鲜族自治县" + }, + { + "code": "220681", + "name": "临江市" + } + ] + }, + { + "code": "2207", + "name": "松原市", + "children": [ + { + "code": "220702", + "name": "宁江区" + }, + { + "code": "220721", + "name": "前郭尔罗斯蒙古族自治县" + }, + { + "code": "220722", + "name": "长岭县" + }, + { + "code": "220723", + "name": "乾安县" + }, + { + "code": "220771", + "name": "吉林松原经济开发区" + }, + { + "code": "220781", + "name": "扶余市" + } + ] + }, + { + "code": "2208", + "name": "白城市", + "children": [ + { + "code": "220802", + "name": "洮北区" + }, + { + "code": "220821", + "name": "镇赉县" + }, + { + "code": "220822", + "name": "通榆县" + }, + { + "code": "220871", + "name": "吉林白城经济开发区" + }, + { + "code": "220881", + "name": "洮南市" + }, + { + "code": "220882", + "name": "大安市" + } + ] + }, + { + "code": "2224", + "name": "延边朝鲜族自治州", + "children": [ + { + "code": "222401", + "name": "延吉市" + }, + { + "code": "222402", + "name": "图们市" + }, + { + "code": "222403", + "name": "敦化市" + }, + { + "code": "222404", + "name": "珲春市" + }, + { + "code": "222405", + "name": "龙井市" + }, + { + "code": "222406", + "name": "和龙市" + }, + { + "code": "222424", + "name": "汪清县" + }, + { + "code": "222426", + "name": "安图县" + } + ] + } + ] + }, + { + "code": "23", + "name": "黑龙江省", + "children": [ + { + "code": "2301", + "name": "哈尔滨市", + "children": [ + { + "code": "230102", + "name": "道里区" + }, + { + "code": "230103", + "name": "南岗区" + }, + { + "code": "230104", + "name": "道外区" + }, + { + "code": "230108", + "name": "平房区" + }, + { + "code": "230109", + "name": "松北区" + }, + { + "code": "230110", + "name": "香坊区" + }, + { + "code": "230111", + "name": "呼兰区" + }, + { + "code": "230112", + "name": "阿城区" + }, + { + "code": "230113", + "name": "双城区" + }, + { + "code": "230123", + "name": "依兰县" + }, + { + "code": "230124", + "name": "方正县" + }, + { + "code": "230125", + "name": "宾县" + }, + { + "code": "230126", + "name": "巴彦县" + }, + { + "code": "230127", + "name": "木兰县" + }, + { + "code": "230128", + "name": "通河县" + }, + { + "code": "230129", + "name": "延寿县" + }, + { + "code": "230183", + "name": "尚志市" + }, + { + "code": "230184", + "name": "五常市" + } + ] + }, + { + "code": "2302", + "name": "齐齐哈尔市", + "children": [ + { + "code": "230202", + "name": "龙沙区" + }, + { + "code": "230203", + "name": "建华区" + }, + { + "code": "230204", + "name": "铁锋区" + }, + { + "code": "230205", + "name": "昂昂溪区" + }, + { + "code": "230206", + "name": "富拉尔基区" + }, + { + "code": "230207", + "name": "碾子山区" + }, + { + "code": "230208", + "name": "梅里斯达斡尔族区" + }, + { + "code": "230221", + "name": "龙江县" + }, + { + "code": "230223", + "name": "依安县" + }, + { + "code": "230224", + "name": "泰来县" + }, + { + "code": "230225", + "name": "甘南县" + }, + { + "code": "230227", + "name": "富裕县" + }, + { + "code": "230229", + "name": "克山县" + }, + { + "code": "230230", + "name": "克东县" + }, + { + "code": "230231", + "name": "拜泉县" + }, + { + "code": "230281", + "name": "讷河市" + } + ] + }, + { + "code": "2303", + "name": "鸡西市", + "children": [ + { + "code": "230302", + "name": "鸡冠区" + }, + { + "code": "230303", + "name": "恒山区" + }, + { + "code": "230304", + "name": "滴道区" + }, + { + "code": "230305", + "name": "梨树区" + }, + { + "code": "230306", + "name": "城子河区" + }, + { + "code": "230307", + "name": "麻山区" + }, + { + "code": "230321", + "name": "鸡东县" + }, + { + "code": "230381", + "name": "虎林市" + }, + { + "code": "230382", + "name": "密山市" + } + ] + }, + { + "code": "2304", + "name": "鹤岗市", + "children": [ + { + "code": "230402", + "name": "向阳区" + }, + { + "code": "230403", + "name": "工农区" + }, + { + "code": "230404", + "name": "南山区" + }, + { + "code": "230405", + "name": "兴安区" + }, + { + "code": "230406", + "name": "东山区" + }, + { + "code": "230407", + "name": "兴山区" + }, + { + "code": "230421", + "name": "萝北县" + }, + { + "code": "230422", + "name": "绥滨县" + } + ] + }, + { + "code": "2305", + "name": "双鸭山市", + "children": [ + { + "code": "230502", + "name": "尖山区" + }, + { + "code": "230503", + "name": "岭东区" + }, + { + "code": "230505", + "name": "四方台区" + }, + { + "code": "230506", + "name": "宝山区" + }, + { + "code": "230521", + "name": "集贤县" + }, + { + "code": "230522", + "name": "友谊县" + }, + { + "code": "230523", + "name": "宝清县" + }, + { + "code": "230524", + "name": "饶河县" + } + ] + }, + { + "code": "2306", + "name": "大庆市", + "children": [ + { + "code": "230602", + "name": "萨尔图区" + }, + { + "code": "230603", + "name": "龙凤区" + }, + { + "code": "230604", + "name": "让胡路区" + }, + { + "code": "230605", + "name": "红岗区" + }, + { + "code": "230606", + "name": "大同区" + }, + { + "code": "230621", + "name": "肇州县" + }, + { + "code": "230622", + "name": "肇源县" + }, + { + "code": "230623", + "name": "林甸县" + }, + { + "code": "230624", + "name": "杜尔伯特蒙古族自治县" + }, + { + "code": "230671", + "name": "大庆高新技术产业开发区" + } + ] + }, + { + "code": "2307", + "name": "伊春市", + "children": [ + { + "code": "230717", + "name": "伊美区" + }, + { + "code": "230718", + "name": "乌翠区" + }, + { + "code": "230719", + "name": "友好区" + }, + { + "code": "230722", + "name": "嘉荫县" + }, + { + "code": "230723", + "name": "汤旺县" + }, + { + "code": "230724", + "name": "丰林县" + }, + { + "code": "230725", + "name": "大箐山县" + }, + { + "code": "230726", + "name": "南岔县" + }, + { + "code": "230751", + "name": "金林区" + }, + { + "code": "230781", + "name": "铁力市" + } + ] + }, + { + "code": "2308", + "name": "佳木斯市", + "children": [ + { + "code": "230803", + "name": "向阳区" + }, + { + "code": "230804", + "name": "前进区" + }, + { + "code": "230805", + "name": "东风区" + }, + { + "code": "230811", + "name": "郊区" + }, + { + "code": "230822", + "name": "桦南县" + }, + { + "code": "230826", + "name": "桦川县" + }, + { + "code": "230828", + "name": "汤原县" + }, + { + "code": "230881", + "name": "同江市" + }, + { + "code": "230882", + "name": "富锦市" + }, + { + "code": "230883", + "name": "抚远市" + } + ] + }, + { + "code": "2309", + "name": "七台河市", + "children": [ + { + "code": "230902", + "name": "新兴区" + }, + { + "code": "230903", + "name": "桃山区" + }, + { + "code": "230904", + "name": "茄子河区" + }, + { + "code": "230921", + "name": "勃利县" + } + ] + }, + { + "code": "2310", + "name": "牡丹江市", + "children": [ + { + "code": "231002", + "name": "东安区" + }, + { + "code": "231003", + "name": "阳明区" + }, + { + "code": "231004", + "name": "爱民区" + }, + { + "code": "231005", + "name": "西安区" + }, + { + "code": "231025", + "name": "林口县" + }, + { + "code": "231081", + "name": "绥芬河市" + }, + { + "code": "231083", + "name": "海林市" + }, + { + "code": "231084", + "name": "宁安市" + }, + { + "code": "231085", + "name": "穆棱市" + }, + { + "code": "231086", + "name": "东宁市" + } + ] + }, + { + "code": "2311", + "name": "黑河市", + "children": [ + { + "code": "231102", + "name": "爱辉区" + }, + { + "code": "231123", + "name": "逊克县" + }, + { + "code": "231124", + "name": "孙吴县" + }, + { + "code": "231181", + "name": "北安市" + }, + { + "code": "231182", + "name": "五大连池市" + }, + { + "code": "231183", + "name": "嫩江市" + } + ] + }, + { + "code": "2312", + "name": "绥化市", + "children": [ + { + "code": "231202", + "name": "北林区" + }, + { + "code": "231221", + "name": "望奎县" + }, + { + "code": "231222", + "name": "兰西县" + }, + { + "code": "231223", + "name": "青冈县" + }, + { + "code": "231224", + "name": "庆安县" + }, + { + "code": "231225", + "name": "明水县" + }, + { + "code": "231226", + "name": "绥棱县" + }, + { + "code": "231281", + "name": "安达市" + }, + { + "code": "231282", + "name": "肇东市" + }, + { + "code": "231283", + "name": "海伦市" + } + ] + }, + { + "code": "2327", + "name": "大兴安岭地区", + "children": [ + { + "code": "232701", + "name": "漠河市" + }, + { + "code": "232721", + "name": "呼玛县" + }, + { + "code": "232722", + "name": "塔河县" + }, + { + "code": "232761", + "name": "加格达奇区" + }, + { + "code": "232762", + "name": "松岭区" + }, + { + "code": "232763", + "name": "新林区" + }, + { + "code": "232764", + "name": "呼中区" + } + ] + } + ] + }, + { + "code": "31", + "name": "上海市", + "children": [ + { + "code": "3101", + "name": "市辖区", + "children": [ + { + "code": "310101", + "name": "黄浦区" + }, + { + "code": "310104", + "name": "徐汇区" + }, + { + "code": "310105", + "name": "长宁区" + }, + { + "code": "310106", + "name": "静安区" + }, + { + "code": "310107", + "name": "普陀区" + }, + { + "code": "310109", + "name": "虹口区" + }, + { + "code": "310110", + "name": "杨浦区" + }, + { + "code": "310112", + "name": "闵行区" + }, + { + "code": "310113", + "name": "宝山区" + }, + { + "code": "310114", + "name": "嘉定区" + }, + { + "code": "310115", + "name": "浦东新区" + }, + { + "code": "310116", + "name": "金山区" + }, + { + "code": "310117", + "name": "松江区" + }, + { + "code": "310118", + "name": "青浦区" + }, + { + "code": "310120", + "name": "奉贤区" + }, + { + "code": "310151", + "name": "崇明区" + } + ] + } + ] + }, + { + "code": "32", + "name": "江苏省", + "children": [ + { + "code": "3201", + "name": "南京市", + "children": [ + { + "code": "320102", + "name": "玄武区" + }, + { + "code": "320104", + "name": "秦淮区" + }, + { + "code": "320105", + "name": "建邺区" + }, + { + "code": "320106", + "name": "鼓楼区" + }, + { + "code": "320111", + "name": "浦口区" + }, + { + "code": "320113", + "name": "栖霞区" + }, + { + "code": "320114", + "name": "雨花台区" + }, + { + "code": "320115", + "name": "江宁区" + }, + { + "code": "320116", + "name": "六合区" + }, + { + "code": "320117", + "name": "溧水区" + }, + { + "code": "320118", + "name": "高淳区" + } + ] + }, + { + "code": "3202", + "name": "无锡市", + "children": [ + { + "code": "320205", + "name": "锡山区" + }, + { + "code": "320206", + "name": "惠山区" + }, + { + "code": "320211", + "name": "滨湖区" + }, + { + "code": "320213", + "name": "梁溪区" + }, + { + "code": "320214", + "name": "新吴区" + }, + { + "code": "320281", + "name": "江阴市" + }, + { + "code": "320282", + "name": "宜兴市" + } + ] + }, + { + "code": "3203", + "name": "徐州市", + "children": [ + { + "code": "320302", + "name": "鼓楼区" + }, + { + "code": "320303", + "name": "云龙区" + }, + { + "code": "320305", + "name": "贾汪区" + }, + { + "code": "320311", + "name": "泉山区" + }, + { + "code": "320312", + "name": "铜山区" + }, + { + "code": "320321", + "name": "丰县" + }, + { + "code": "320322", + "name": "沛县" + }, + { + "code": "320324", + "name": "睢宁县" + }, + { + "code": "320371", + "name": "徐州经济技术开发区" + }, + { + "code": "320381", + "name": "新沂市" + }, + { + "code": "320382", + "name": "邳州市" + } + ] + }, + { + "code": "3204", + "name": "常州市", + "children": [ + { + "code": "320402", + "name": "天宁区" + }, + { + "code": "320404", + "name": "钟楼区" + }, + { + "code": "320411", + "name": "新北区" + }, + { + "code": "320412", + "name": "武进区" + }, + { + "code": "320413", + "name": "金坛区" + }, + { + "code": "320481", + "name": "溧阳市" + } + ] + }, + { + "code": "3205", + "name": "苏州市", + "children": [ + { + "code": "320505", + "name": "虎丘区" + }, + { + "code": "320506", + "name": "吴中区" + }, + { + "code": "320507", + "name": "相城区" + }, + { + "code": "320508", + "name": "姑苏区" + }, + { + "code": "320509", + "name": "吴江区" + }, + { + "code": "320576", + "name": "苏州工业园区" + }, + { + "code": "320581", + "name": "常熟市" + }, + { + "code": "320582", + "name": "张家港市" + }, + { + "code": "320583", + "name": "昆山市" + }, + { + "code": "320585", + "name": "太仓市" + } + ] + }, + { + "code": "3206", + "name": "南通市", + "children": [ + { + "code": "320612", + "name": "通州区" + }, + { + "code": "320613", + "name": "崇川区" + }, + { + "code": "320614", + "name": "海门区" + }, + { + "code": "320623", + "name": "如东县" + }, + { + "code": "320671", + "name": "南通经济技术开发区" + }, + { + "code": "320681", + "name": "启东市" + }, + { + "code": "320682", + "name": "如皋市" + }, + { + "code": "320685", + "name": "海安市" + } + ] + }, + { + "code": "3207", + "name": "连云港市", + "children": [ + { + "code": "320703", + "name": "连云区" + }, + { + "code": "320706", + "name": "海州区" + }, + { + "code": "320707", + "name": "赣榆区" + }, + { + "code": "320722", + "name": "东海县" + }, + { + "code": "320723", + "name": "灌云县" + }, + { + "code": "320724", + "name": "灌南县" + }, + { + "code": "320771", + "name": "连云港经济技术开发区" + } + ] + }, + { + "code": "3208", + "name": "淮安市", + "children": [ + { + "code": "320803", + "name": "淮安区" + }, + { + "code": "320804", + "name": "淮阴区" + }, + { + "code": "320812", + "name": "清江浦区" + }, + { + "code": "320813", + "name": "洪泽区" + }, + { + "code": "320826", + "name": "涟水县" + }, + { + "code": "320830", + "name": "盱眙县" + }, + { + "code": "320831", + "name": "金湖县" + }, + { + "code": "320871", + "name": "淮安经济技术开发区" + } + ] + }, + { + "code": "3209", + "name": "盐城市", + "children": [ + { + "code": "320902", + "name": "亭湖区" + }, + { + "code": "320903", + "name": "盐都区" + }, + { + "code": "320904", + "name": "大丰区" + }, + { + "code": "320921", + "name": "响水县" + }, + { + "code": "320922", + "name": "滨海县" + }, + { + "code": "320923", + "name": "阜宁县" + }, + { + "code": "320924", + "name": "射阳县" + }, + { + "code": "320925", + "name": "建湖县" + }, + { + "code": "320971", + "name": "盐城经济技术开发区" + }, + { + "code": "320981", + "name": "东台市" + } + ] + }, + { + "code": "3210", + "name": "扬州市", + "children": [ + { + "code": "321002", + "name": "广陵区" + }, + { + "code": "321003", + "name": "邗江区" + }, + { + "code": "321012", + "name": "江都区" + }, + { + "code": "321023", + "name": "宝应县" + }, + { + "code": "321071", + "name": "扬州经济技术开发区" + }, + { + "code": "321081", + "name": "仪征市" + }, + { + "code": "321084", + "name": "高邮市" + } + ] + }, + { + "code": "3211", + "name": "镇江市", + "children": [ + { + "code": "321102", + "name": "京口区" + }, + { + "code": "321111", + "name": "润州区" + }, + { + "code": "321112", + "name": "丹徒区" + }, + { + "code": "321171", + "name": "镇江新区" + }, + { + "code": "321181", + "name": "丹阳市" + }, + { + "code": "321182", + "name": "扬中市" + }, + { + "code": "321183", + "name": "句容市" + } + ] + }, + { + "code": "3212", + "name": "泰州市", + "children": [ + { + "code": "321202", + "name": "海陵区" + }, + { + "code": "321203", + "name": "高港区" + }, + { + "code": "321204", + "name": "姜堰区" + }, + { + "code": "321281", + "name": "兴化市" + }, + { + "code": "321282", + "name": "靖江市" + }, + { + "code": "321283", + "name": "泰兴市" + } + ] + }, + { + "code": "3213", + "name": "宿迁市", + "children": [ + { + "code": "321302", + "name": "宿城区" + }, + { + "code": "321311", + "name": "宿豫区" + }, + { + "code": "321322", + "name": "沭阳县" + }, + { + "code": "321323", + "name": "泗阳县" + }, + { + "code": "321324", + "name": "泗洪县" + }, + { + "code": "321371", + "name": "宿迁经济技术开发区" + } + ] + } + ] + }, + { + "code": "33", + "name": "浙江省", + "children": [ + { + "code": "3301", + "name": "杭州市", + "children": [ + { + "code": "330102", + "name": "上城区" + }, + { + "code": "330105", + "name": "拱墅区" + }, + { + "code": "330106", + "name": "西湖区" + }, + { + "code": "330108", + "name": "滨江区" + }, + { + "code": "330109", + "name": "萧山区" + }, + { + "code": "330110", + "name": "余杭区" + }, + { + "code": "330111", + "name": "富阳区" + }, + { + "code": "330112", + "name": "临安区" + }, + { + "code": "330113", + "name": "临平区" + }, + { + "code": "330114", + "name": "钱塘区" + }, + { + "code": "330122", + "name": "桐庐县" + }, + { + "code": "330127", + "name": "淳安县" + }, + { + "code": "330182", + "name": "建德市" + } + ] + }, + { + "code": "3302", + "name": "宁波市", + "children": [ + { + "code": "330203", + "name": "海曙区" + }, + { + "code": "330205", + "name": "江北区" + }, + { + "code": "330206", + "name": "北仑区" + }, + { + "code": "330211", + "name": "镇海区" + }, + { + "code": "330212", + "name": "鄞州区" + }, + { + "code": "330213", + "name": "奉化区" + }, + { + "code": "330225", + "name": "象山县" + }, + { + "code": "330226", + "name": "宁海县" + }, + { + "code": "330281", + "name": "余姚市" + }, + { + "code": "330282", + "name": "慈溪市" + } + ] + }, + { + "code": "3303", + "name": "温州市", + "children": [ + { + "code": "330302", + "name": "鹿城区" + }, + { + "code": "330303", + "name": "龙湾区" + }, + { + "code": "330304", + "name": "瓯海区" + }, + { + "code": "330305", + "name": "洞头区" + }, + { + "code": "330324", + "name": "永嘉县" + }, + { + "code": "330326", + "name": "平阳县" + }, + { + "code": "330327", + "name": "苍南县" + }, + { + "code": "330328", + "name": "文成县" + }, + { + "code": "330329", + "name": "泰顺县" + }, + { + "code": "330381", + "name": "瑞安市" + }, + { + "code": "330382", + "name": "乐清市" + }, + { + "code": "330383", + "name": "龙港市" + } + ] + }, + { + "code": "3304", + "name": "嘉兴市", + "children": [ + { + "code": "330402", + "name": "南湖区" + }, + { + "code": "330411", + "name": "秀洲区" + }, + { + "code": "330421", + "name": "嘉善县" + }, + { + "code": "330424", + "name": "海盐县" + }, + { + "code": "330481", + "name": "海宁市" + }, + { + "code": "330482", + "name": "平湖市" + }, + { + "code": "330483", + "name": "桐乡市" + } + ] + }, + { + "code": "3305", + "name": "湖州市", + "children": [ + { + "code": "330502", + "name": "吴兴区" + }, + { + "code": "330503", + "name": "南浔区" + }, + { + "code": "330521", + "name": "德清县" + }, + { + "code": "330522", + "name": "长兴县" + }, + { + "code": "330523", + "name": "安吉县" + } + ] + }, + { + "code": "3306", + "name": "绍兴市", + "children": [ + { + "code": "330602", + "name": "越城区" + }, + { + "code": "330603", + "name": "柯桥区" + }, + { + "code": "330604", + "name": "上虞区" + }, + { + "code": "330624", + "name": "新昌县" + }, + { + "code": "330681", + "name": "诸暨市" + }, + { + "code": "330683", + "name": "嵊州市" + } + ] + }, + { + "code": "3307", + "name": "金华市", + "children": [ + { + "code": "330702", + "name": "婺城区" + }, + { + "code": "330703", + "name": "金东区" + }, + { + "code": "330723", + "name": "武义县" + }, + { + "code": "330726", + "name": "浦江县" + }, + { + "code": "330727", + "name": "磐安县" + }, + { + "code": "330781", + "name": "兰溪市" + }, + { + "code": "330782", + "name": "义乌市" + }, + { + "code": "330783", + "name": "东阳市" + }, + { + "code": "330784", + "name": "永康市" + } + ] + }, + { + "code": "3308", + "name": "衢州市", + "children": [ + { + "code": "330802", + "name": "柯城区" + }, + { + "code": "330803", + "name": "衢江区" + }, + { + "code": "330822", + "name": "常山县" + }, + { + "code": "330824", + "name": "开化县" + }, + { + "code": "330825", + "name": "龙游县" + }, + { + "code": "330881", + "name": "江山市" + } + ] + }, + { + "code": "3309", + "name": "舟山市", + "children": [ + { + "code": "330902", + "name": "定海区" + }, + { + "code": "330903", + "name": "普陀区" + }, + { + "code": "330921", + "name": "岱山县" + }, + { + "code": "330922", + "name": "嵊泗县" + } + ] + }, + { + "code": "3310", + "name": "台州市", + "children": [ + { + "code": "331002", + "name": "椒江区" + }, + { + "code": "331003", + "name": "黄岩区" + }, + { + "code": "331004", + "name": "路桥区" + }, + { + "code": "331022", + "name": "三门县" + }, + { + "code": "331023", + "name": "天台县" + }, + { + "code": "331024", + "name": "仙居县" + }, + { + "code": "331081", + "name": "温岭市" + }, + { + "code": "331082", + "name": "临海市" + }, + { + "code": "331083", + "name": "玉环市" + } + ] + }, + { + "code": "3311", + "name": "丽水市", + "children": [ + { + "code": "331102", + "name": "莲都区" + }, + { + "code": "331121", + "name": "青田县" + }, + { + "code": "331122", + "name": "缙云县" + }, + { + "code": "331123", + "name": "遂昌县" + }, + { + "code": "331124", + "name": "松阳县" + }, + { + "code": "331125", + "name": "云和县" + }, + { + "code": "331126", + "name": "庆元县" + }, + { + "code": "331127", + "name": "景宁畲族自治县" + }, + { + "code": "331181", + "name": "龙泉市" + } + ] + } + ] + }, + { + "code": "34", + "name": "安徽省", + "children": [ + { + "code": "3401", + "name": "合肥市", + "children": [ + { + "code": "340102", + "name": "瑶海区" + }, + { + "code": "340103", + "name": "庐阳区" + }, + { + "code": "340104", + "name": "蜀山区" + }, + { + "code": "340111", + "name": "包河区" + }, + { + "code": "340121", + "name": "长丰县" + }, + { + "code": "340122", + "name": "肥东县" + }, + { + "code": "340123", + "name": "肥西县" + }, + { + "code": "340124", + "name": "庐江县" + }, + { + "code": "340176", + "name": "合肥高新技术产业开发区" + }, + { + "code": "340177", + "name": "合肥经济技术开发区" + }, + { + "code": "340178", + "name": "合肥新站高新技术产业开发区" + }, + { + "code": "340181", + "name": "巢湖市" + } + ] + }, + { + "code": "3402", + "name": "芜湖市", + "children": [ + { + "code": "340202", + "name": "镜湖区" + }, + { + "code": "340207", + "name": "鸠江区" + }, + { + "code": "340209", + "name": "弋江区" + }, + { + "code": "340210", + "name": "湾沚区" + }, + { + "code": "340212", + "name": "繁昌区" + }, + { + "code": "340223", + "name": "南陵县" + }, + { + "code": "340271", + "name": "芜湖经济技术开发区" + }, + { + "code": "340272", + "name": "安徽芜湖三山经济开发区" + }, + { + "code": "340281", + "name": "无为市" + } + ] + }, + { + "code": "3403", + "name": "蚌埠市", + "children": [ + { + "code": "340302", + "name": "龙子湖区" + }, + { + "code": "340303", + "name": "蚌山区" + }, + { + "code": "340304", + "name": "禹会区" + }, + { + "code": "340311", + "name": "淮上区" + }, + { + "code": "340321", + "name": "怀远县" + }, + { + "code": "340322", + "name": "五河县" + }, + { + "code": "340323", + "name": "固镇县" + }, + { + "code": "340371", + "name": "蚌埠市高新技术开发区" + }, + { + "code": "340372", + "name": "蚌埠市经济开发区" + } + ] + }, + { + "code": "3404", + "name": "淮南市", + "children": [ + { + "code": "340402", + "name": "大通区" + }, + { + "code": "340403", + "name": "田家庵区" + }, + { + "code": "340404", + "name": "谢家集区" + }, + { + "code": "340405", + "name": "八公山区" + }, + { + "code": "340406", + "name": "潘集区" + }, + { + "code": "340421", + "name": "凤台县" + }, + { + "code": "340422", + "name": "寿县" + } + ] + }, + { + "code": "3405", + "name": "马鞍山市", + "children": [ + { + "code": "340503", + "name": "花山区" + }, + { + "code": "340504", + "name": "雨山区" + }, + { + "code": "340506", + "name": "博望区" + }, + { + "code": "340521", + "name": "当涂县" + }, + { + "code": "340522", + "name": "含山县" + }, + { + "code": "340523", + "name": "和县" + } + ] + }, + { + "code": "3406", + "name": "淮北市", + "children": [ + { + "code": "340602", + "name": "杜集区" + }, + { + "code": "340603", + "name": "相山区" + }, + { + "code": "340604", + "name": "烈山区" + }, + { + "code": "340621", + "name": "濉溪县" + } + ] + }, + { + "code": "3407", + "name": "铜陵市", + "children": [ + { + "code": "340705", + "name": "铜官区" + }, + { + "code": "340706", + "name": "义安区" + }, + { + "code": "340711", + "name": "郊区" + }, + { + "code": "340722", + "name": "枞阳县" + } + ] + }, + { + "code": "3408", + "name": "安庆市", + "children": [ + { + "code": "340802", + "name": "迎江区" + }, + { + "code": "340803", + "name": "大观区" + }, + { + "code": "340811", + "name": "宜秀区" + }, + { + "code": "340822", + "name": "怀宁县" + }, + { + "code": "340825", + "name": "太湖县" + }, + { + "code": "340826", + "name": "宿松县" + }, + { + "code": "340827", + "name": "望江县" + }, + { + "code": "340828", + "name": "岳西县" + }, + { + "code": "340871", + "name": "安徽安庆经济开发区" + }, + { + "code": "340881", + "name": "桐城市" + }, + { + "code": "340882", + "name": "潜山市" + } + ] + }, + { + "code": "3410", + "name": "黄山市", + "children": [ + { + "code": "341002", + "name": "屯溪区" + }, + { + "code": "341003", + "name": "黄山区" + }, + { + "code": "341004", + "name": "徽州区" + }, + { + "code": "341021", + "name": "歙县" + }, + { + "code": "341022", + "name": "休宁县" + }, + { + "code": "341023", + "name": "黟县" + }, + { + "code": "341024", + "name": "祁门县" + } + ] + }, + { + "code": "3411", + "name": "滁州市", + "children": [ + { + "code": "341102", + "name": "琅琊区" + }, + { + "code": "341103", + "name": "南谯区" + }, + { + "code": "341122", + "name": "来安县" + }, + { + "code": "341124", + "name": "全椒县" + }, + { + "code": "341125", + "name": "定远县" + }, + { + "code": "341126", + "name": "凤阳县" + }, + { + "code": "341171", + "name": "中新苏滁高新技术产业开发区" + }, + { + "code": "341172", + "name": "滁州经济技术开发区" + }, + { + "code": "341181", + "name": "天长市" + }, + { + "code": "341182", + "name": "明光市" + } + ] + }, + { + "code": "3412", + "name": "阜阳市", + "children": [ + { + "code": "341202", + "name": "颍州区" + }, + { + "code": "341203", + "name": "颍东区" + }, + { + "code": "341204", + "name": "颍泉区" + }, + { + "code": "341221", + "name": "临泉县" + }, + { + "code": "341222", + "name": "太和县" + }, + { + "code": "341225", + "name": "阜南县" + }, + { + "code": "341226", + "name": "颍上县" + }, + { + "code": "341271", + "name": "阜阳合肥现代产业园区" + }, + { + "code": "341272", + "name": "阜阳经济技术开发区" + }, + { + "code": "341282", + "name": "界首市" + } + ] + }, + { + "code": "3413", + "name": "宿州市", + "children": [ + { + "code": "341302", + "name": "埇桥区" + }, + { + "code": "341321", + "name": "砀山县" + }, + { + "code": "341322", + "name": "萧县" + }, + { + "code": "341323", + "name": "灵璧县" + }, + { + "code": "341324", + "name": "泗县" + }, + { + "code": "341371", + "name": "宿州马鞍山现代产业园区" + }, + { + "code": "341372", + "name": "宿州经济技术开发区" + } + ] + }, + { + "code": "3415", + "name": "六安市", + "children": [ + { + "code": "341502", + "name": "金安区" + }, + { + "code": "341503", + "name": "裕安区" + }, + { + "code": "341504", + "name": "叶集区" + }, + { + "code": "341522", + "name": "霍邱县" + }, + { + "code": "341523", + "name": "舒城县" + }, + { + "code": "341524", + "name": "金寨县" + }, + { + "code": "341525", + "name": "霍山县" + } + ] + }, + { + "code": "3416", + "name": "亳州市", + "children": [ + { + "code": "341602", + "name": "谯城区" + }, + { + "code": "341621", + "name": "涡阳县" + }, + { + "code": "341622", + "name": "蒙城县" + }, + { + "code": "341623", + "name": "利辛县" + } + ] + }, + { + "code": "3417", + "name": "池州市", + "children": [ + { + "code": "341702", + "name": "贵池区" + }, + { + "code": "341721", + "name": "东至县" + }, + { + "code": "341722", + "name": "石台县" + }, + { + "code": "341723", + "name": "青阳县" + } + ] + }, + { + "code": "3418", + "name": "宣城市", + "children": [ + { + "code": "341802", + "name": "宣州区" + }, + { + "code": "341821", + "name": "郎溪县" + }, + { + "code": "341823", + "name": "泾县" + }, + { + "code": "341824", + "name": "绩溪县" + }, + { + "code": "341825", + "name": "旌德县" + }, + { + "code": "341871", + "name": "宣城市经济开发区" + }, + { + "code": "341881", + "name": "宁国市" + }, + { + "code": "341882", + "name": "广德市" + } + ] + } + ] + }, + { + "code": "35", + "name": "福建省", + "children": [ + { + "code": "3501", + "name": "福州市", + "children": [ + { + "code": "350102", + "name": "鼓楼区" + }, + { + "code": "350103", + "name": "台江区" + }, + { + "code": "350104", + "name": "仓山区" + }, + { + "code": "350105", + "name": "马尾区" + }, + { + "code": "350111", + "name": "晋安区" + }, + { + "code": "350112", + "name": "长乐区" + }, + { + "code": "350121", + "name": "闽侯县" + }, + { + "code": "350122", + "name": "连江县" + }, + { + "code": "350123", + "name": "罗源县" + }, + { + "code": "350124", + "name": "闽清县" + }, + { + "code": "350125", + "name": "永泰县" + }, + { + "code": "350128", + "name": "平潭县" + }, + { + "code": "350181", + "name": "福清市" + } + ] + }, + { + "code": "3502", + "name": "厦门市", + "children": [ + { + "code": "350203", + "name": "思明区" + }, + { + "code": "350205", + "name": "海沧区" + }, + { + "code": "350206", + "name": "湖里区" + }, + { + "code": "350211", + "name": "集美区" + }, + { + "code": "350212", + "name": "同安区" + }, + { + "code": "350213", + "name": "翔安区" + } + ] + }, + { + "code": "3503", + "name": "莆田市", + "children": [ + { + "code": "350302", + "name": "城厢区" + }, + { + "code": "350303", + "name": "涵江区" + }, + { + "code": "350304", + "name": "荔城区" + }, + { + "code": "350305", + "name": "秀屿区" + }, + { + "code": "350322", + "name": "仙游县" + } + ] + }, + { + "code": "3504", + "name": "三明市", + "children": [ + { + "code": "350404", + "name": "三元区" + }, + { + "code": "350405", + "name": "沙县区" + }, + { + "code": "350421", + "name": "明溪县" + }, + { + "code": "350423", + "name": "清流县" + }, + { + "code": "350424", + "name": "宁化县" + }, + { + "code": "350425", + "name": "大田县" + }, + { + "code": "350426", + "name": "尤溪县" + }, + { + "code": "350428", + "name": "将乐县" + }, + { + "code": "350429", + "name": "泰宁县" + }, + { + "code": "350430", + "name": "建宁县" + }, + { + "code": "350481", + "name": "永安市" + } + ] + }, + { + "code": "3505", + "name": "泉州市", + "children": [ + { + "code": "350502", + "name": "鲤城区" + }, + { + "code": "350503", + "name": "丰泽区" + }, + { + "code": "350504", + "name": "洛江区" + }, + { + "code": "350505", + "name": "泉港区" + }, + { + "code": "350521", + "name": "惠安县" + }, + { + "code": "350524", + "name": "安溪县" + }, + { + "code": "350525", + "name": "永春县" + }, + { + "code": "350526", + "name": "德化县" + }, + { + "code": "350527", + "name": "金门县" + }, + { + "code": "350581", + "name": "石狮市" + }, + { + "code": "350582", + "name": "晋江市" + }, + { + "code": "350583", + "name": "南安市" + } + ] + }, + { + "code": "3506", + "name": "漳州市", + "children": [ + { + "code": "350602", + "name": "芗城区" + }, + { + "code": "350603", + "name": "龙文区" + }, + { + "code": "350604", + "name": "龙海区" + }, + { + "code": "350605", + "name": "长泰区" + }, + { + "code": "350622", + "name": "云霄县" + }, + { + "code": "350623", + "name": "漳浦县" + }, + { + "code": "350624", + "name": "诏安县" + }, + { + "code": "350626", + "name": "东山县" + }, + { + "code": "350627", + "name": "南靖县" + }, + { + "code": "350628", + "name": "平和县" + }, + { + "code": "350629", + "name": "华安县" + } + ] + }, + { + "code": "3507", + "name": "南平市", + "children": [ + { + "code": "350702", + "name": "延平区" + }, + { + "code": "350703", + "name": "建阳区" + }, + { + "code": "350721", + "name": "顺昌县" + }, + { + "code": "350722", + "name": "浦城县" + }, + { + "code": "350723", + "name": "光泽县" + }, + { + "code": "350724", + "name": "松溪县" + }, + { + "code": "350725", + "name": "政和县" + }, + { + "code": "350781", + "name": "邵武市" + }, + { + "code": "350782", + "name": "武夷山市" + }, + { + "code": "350783", + "name": "建瓯市" + } + ] + }, + { + "code": "3508", + "name": "龙岩市", + "children": [ + { + "code": "350802", + "name": "新罗区" + }, + { + "code": "350803", + "name": "永定区" + }, + { + "code": "350821", + "name": "长汀县" + }, + { + "code": "350823", + "name": "上杭县" + }, + { + "code": "350824", + "name": "武平县" + }, + { + "code": "350825", + "name": "连城县" + }, + { + "code": "350881", + "name": "漳平市" + } + ] + }, + { + "code": "3509", + "name": "宁德市", + "children": [ + { + "code": "350902", + "name": "蕉城区" + }, + { + "code": "350921", + "name": "霞浦县" + }, + { + "code": "350922", + "name": "古田县" + }, + { + "code": "350923", + "name": "屏南县" + }, + { + "code": "350924", + "name": "寿宁县" + }, + { + "code": "350925", + "name": "周宁县" + }, + { + "code": "350926", + "name": "柘荣县" + }, + { + "code": "350981", + "name": "福安市" + }, + { + "code": "350982", + "name": "福鼎市" + } + ] + } + ] + }, + { + "code": "36", + "name": "江西省", + "children": [ + { + "code": "3601", + "name": "南昌市", + "children": [ + { + "code": "360102", + "name": "东湖区" + }, + { + "code": "360103", + "name": "西湖区" + }, + { + "code": "360104", + "name": "青云谱区" + }, + { + "code": "360111", + "name": "青山湖区" + }, + { + "code": "360112", + "name": "新建区" + }, + { + "code": "360113", + "name": "红谷滩区" + }, + { + "code": "360121", + "name": "南昌县" + }, + { + "code": "360123", + "name": "安义县" + }, + { + "code": "360124", + "name": "进贤县" + } + ] + }, + { + "code": "3602", + "name": "景德镇市", + "children": [ + { + "code": "360202", + "name": "昌江区" + }, + { + "code": "360203", + "name": "珠山区" + }, + { + "code": "360222", + "name": "浮梁县" + }, + { + "code": "360281", + "name": "乐平市" + } + ] + }, + { + "code": "3603", + "name": "萍乡市", + "children": [ + { + "code": "360302", + "name": "安源区" + }, + { + "code": "360313", + "name": "湘东区" + }, + { + "code": "360321", + "name": "莲花县" + }, + { + "code": "360322", + "name": "上栗县" + }, + { + "code": "360323", + "name": "芦溪县" + } + ] + }, + { + "code": "3604", + "name": "九江市", + "children": [ + { + "code": "360402", + "name": "濂溪区" + }, + { + "code": "360403", + "name": "浔阳区" + }, + { + "code": "360404", + "name": "柴桑区" + }, + { + "code": "360423", + "name": "武宁县" + }, + { + "code": "360424", + "name": "修水县" + }, + { + "code": "360425", + "name": "永修县" + }, + { + "code": "360426", + "name": "德安县" + }, + { + "code": "360428", + "name": "都昌县" + }, + { + "code": "360429", + "name": "湖口县" + }, + { + "code": "360430", + "name": "彭泽县" + }, + { + "code": "360481", + "name": "瑞昌市" + }, + { + "code": "360482", + "name": "共青城市" + }, + { + "code": "360483", + "name": "庐山市" + } + ] + }, + { + "code": "3605", + "name": "新余市", + "children": [ + { + "code": "360502", + "name": "渝水区" + }, + { + "code": "360521", + "name": "分宜县" + } + ] + }, + { + "code": "3606", + "name": "鹰潭市", + "children": [ + { + "code": "360602", + "name": "月湖区" + }, + { + "code": "360603", + "name": "余江区" + }, + { + "code": "360681", + "name": "贵溪市" + } + ] + }, + { + "code": "3607", + "name": "赣州市", + "children": [ + { + "code": "360702", + "name": "章贡区" + }, + { + "code": "360703", + "name": "南康区" + }, + { + "code": "360704", + "name": "赣县区" + }, + { + "code": "360722", + "name": "信丰县" + }, + { + "code": "360723", + "name": "大余县" + }, + { + "code": "360724", + "name": "上犹县" + }, + { + "code": "360725", + "name": "崇义县" + }, + { + "code": "360726", + "name": "安远县" + }, + { + "code": "360728", + "name": "定南县" + }, + { + "code": "360729", + "name": "全南县" + }, + { + "code": "360730", + "name": "宁都县" + }, + { + "code": "360731", + "name": "于都县" + }, + { + "code": "360732", + "name": "兴国县" + }, + { + "code": "360733", + "name": "会昌县" + }, + { + "code": "360734", + "name": "寻乌县" + }, + { + "code": "360735", + "name": "石城县" + }, + { + "code": "360781", + "name": "瑞金市" + }, + { + "code": "360783", + "name": "龙南市" + } + ] + }, + { + "code": "3608", + "name": "吉安市", + "children": [ + { + "code": "360802", + "name": "吉州区" + }, + { + "code": "360803", + "name": "青原区" + }, + { + "code": "360821", + "name": "吉安县" + }, + { + "code": "360822", + "name": "吉水县" + }, + { + "code": "360823", + "name": "峡江县" + }, + { + "code": "360824", + "name": "新干县" + }, + { + "code": "360825", + "name": "永丰县" + }, + { + "code": "360826", + "name": "泰和县" + }, + { + "code": "360827", + "name": "遂川县" + }, + { + "code": "360828", + "name": "万安县" + }, + { + "code": "360829", + "name": "安福县" + }, + { + "code": "360830", + "name": "永新县" + }, + { + "code": "360881", + "name": "井冈山市" + } + ] + }, + { + "code": "3609", + "name": "宜春市", + "children": [ + { + "code": "360902", + "name": "袁州区" + }, + { + "code": "360921", + "name": "奉新县" + }, + { + "code": "360922", + "name": "万载县" + }, + { + "code": "360923", + "name": "上高县" + }, + { + "code": "360924", + "name": "宜丰县" + }, + { + "code": "360925", + "name": "靖安县" + }, + { + "code": "360926", + "name": "铜鼓县" + }, + { + "code": "360981", + "name": "丰城市" + }, + { + "code": "360982", + "name": "樟树市" + }, + { + "code": "360983", + "name": "高安市" + } + ] + }, + { + "code": "3610", + "name": "抚州市", + "children": [ + { + "code": "361002", + "name": "临川区" + }, + { + "code": "361003", + "name": "东乡区" + }, + { + "code": "361021", + "name": "南城县" + }, + { + "code": "361022", + "name": "黎川县" + }, + { + "code": "361023", + "name": "南丰县" + }, + { + "code": "361024", + "name": "崇仁县" + }, + { + "code": "361025", + "name": "乐安县" + }, + { + "code": "361026", + "name": "宜黄县" + }, + { + "code": "361027", + "name": "金溪县" + }, + { + "code": "361028", + "name": "资溪县" + }, + { + "code": "361030", + "name": "广昌县" + } + ] + }, + { + "code": "3611", + "name": "上饶市", + "children": [ + { + "code": "361102", + "name": "信州区" + }, + { + "code": "361103", + "name": "广丰区" + }, + { + "code": "361104", + "name": "广信区" + }, + { + "code": "361123", + "name": "玉山县" + }, + { + "code": "361124", + "name": "铅山县" + }, + { + "code": "361125", + "name": "横峰县" + }, + { + "code": "361126", + "name": "弋阳县" + }, + { + "code": "361127", + "name": "余干县" + }, + { + "code": "361128", + "name": "鄱阳县" + }, + { + "code": "361129", + "name": "万年县" + }, + { + "code": "361130", + "name": "婺源县" + }, + { + "code": "361181", + "name": "德兴市" + } + ] + } + ] + }, + { + "code": "37", + "name": "山东省", + "children": [ + { + "code": "3701", + "name": "济南市", + "children": [ + { + "code": "370102", + "name": "历下区" + }, + { + "code": "370103", + "name": "市中区" + }, + { + "code": "370104", + "name": "槐荫区" + }, + { + "code": "370105", + "name": "天桥区" + }, + { + "code": "370112", + "name": "历城区" + }, + { + "code": "370113", + "name": "长清区" + }, + { + "code": "370114", + "name": "章丘区" + }, + { + "code": "370115", + "name": "济阳区" + }, + { + "code": "370116", + "name": "莱芜区" + }, + { + "code": "370117", + "name": "钢城区" + }, + { + "code": "370124", + "name": "平阴县" + }, + { + "code": "370126", + "name": "商河县" + }, + { + "code": "370176", + "name": "济南高新技术产业开发区" + } + ] + }, + { + "code": "3702", + "name": "青岛市", + "children": [ + { + "code": "370202", + "name": "市南区" + }, + { + "code": "370203", + "name": "市北区" + }, + { + "code": "370211", + "name": "黄岛区" + }, + { + "code": "370212", + "name": "崂山区" + }, + { + "code": "370213", + "name": "李沧区" + }, + { + "code": "370214", + "name": "城阳区" + }, + { + "code": "370215", + "name": "即墨区" + }, + { + "code": "370281", + "name": "胶州市" + }, + { + "code": "370283", + "name": "平度市" + }, + { + "code": "370285", + "name": "莱西市" + } + ] + }, + { + "code": "3703", + "name": "淄博市", + "children": [ + { + "code": "370302", + "name": "淄川区" + }, + { + "code": "370303", + "name": "张店区" + }, + { + "code": "370304", + "name": "博山区" + }, + { + "code": "370305", + "name": "临淄区" + }, + { + "code": "370306", + "name": "周村区" + }, + { + "code": "370321", + "name": "桓台县" + }, + { + "code": "370322", + "name": "高青县" + }, + { + "code": "370323", + "name": "沂源县" + } + ] + }, + { + "code": "3704", + "name": "枣庄市", + "children": [ + { + "code": "370402", + "name": "市中区" + }, + { + "code": "370403", + "name": "薛城区" + }, + { + "code": "370404", + "name": "峄城区" + }, + { + "code": "370405", + "name": "台儿庄区" + }, + { + "code": "370406", + "name": "山亭区" + }, + { + "code": "370481", + "name": "滕州市" + } + ] + }, + { + "code": "3705", + "name": "东营市", + "children": [ + { + "code": "370502", + "name": "东营区" + }, + { + "code": "370503", + "name": "河口区" + }, + { + "code": "370505", + "name": "垦利区" + }, + { + "code": "370522", + "name": "利津县" + }, + { + "code": "370523", + "name": "广饶县" + }, + { + "code": "370571", + "name": "东营经济技术开发区" + }, + { + "code": "370572", + "name": "东营港经济开发区" + } + ] + }, + { + "code": "3706", + "name": "烟台市", + "children": [ + { + "code": "370602", + "name": "芝罘区" + }, + { + "code": "370611", + "name": "福山区" + }, + { + "code": "370612", + "name": "牟平区" + }, + { + "code": "370613", + "name": "莱山区" + }, + { + "code": "370614", + "name": "蓬莱区" + }, + { + "code": "370671", + "name": "烟台高新技术产业开发区" + }, + { + "code": "370676", + "name": "烟台经济技术开发区" + }, + { + "code": "370681", + "name": "龙口市" + }, + { + "code": "370682", + "name": "莱阳市" + }, + { + "code": "370683", + "name": "莱州市" + }, + { + "code": "370685", + "name": "招远市" + }, + { + "code": "370686", + "name": "栖霞市" + }, + { + "code": "370687", + "name": "海阳市" + } + ] + }, + { + "code": "3707", + "name": "潍坊市", + "children": [ + { + "code": "370702", + "name": "潍城区" + }, + { + "code": "370703", + "name": "寒亭区" + }, + { + "code": "370704", + "name": "坊子区" + }, + { + "code": "370705", + "name": "奎文区" + }, + { + "code": "370724", + "name": "临朐县" + }, + { + "code": "370725", + "name": "昌乐县" + }, + { + "code": "370772", + "name": "潍坊滨海经济技术开发区" + }, + { + "code": "370781", + "name": "青州市" + }, + { + "code": "370782", + "name": "诸城市" + }, + { + "code": "370783", + "name": "寿光市" + }, + { + "code": "370784", + "name": "安丘市" + }, + { + "code": "370785", + "name": "高密市" + }, + { + "code": "370786", + "name": "昌邑市" + } + ] + }, + { + "code": "3708", + "name": "济宁市", + "children": [ + { + "code": "370811", + "name": "任城区" + }, + { + "code": "370812", + "name": "兖州区" + }, + { + "code": "370826", + "name": "微山县" + }, + { + "code": "370827", + "name": "鱼台县" + }, + { + "code": "370828", + "name": "金乡县" + }, + { + "code": "370829", + "name": "嘉祥县" + }, + { + "code": "370830", + "name": "汶上县" + }, + { + "code": "370831", + "name": "泗水县" + }, + { + "code": "370832", + "name": "梁山县" + }, + { + "code": "370871", + "name": "济宁高新技术产业开发区" + }, + { + "code": "370881", + "name": "曲阜市" + }, + { + "code": "370883", + "name": "邹城市" + } + ] + }, + { + "code": "3709", + "name": "泰安市", + "children": [ + { + "code": "370902", + "name": "泰山区" + }, + { + "code": "370911", + "name": "岱岳区" + }, + { + "code": "370921", + "name": "宁阳县" + }, + { + "code": "370923", + "name": "东平县" + }, + { + "code": "370982", + "name": "新泰市" + }, + { + "code": "370983", + "name": "肥城市" + } + ] + }, + { + "code": "3710", + "name": "威海市", + "children": [ + { + "code": "371002", + "name": "环翠区" + }, + { + "code": "371003", + "name": "文登区" + }, + { + "code": "371071", + "name": "威海火炬高技术产业开发区" + }, + { + "code": "371072", + "name": "威海经济技术开发区" + }, + { + "code": "371073", + "name": "威海临港经济技术开发区" + }, + { + "code": "371082", + "name": "荣成市" + }, + { + "code": "371083", + "name": "乳山市" + } + ] + }, + { + "code": "3711", + "name": "日照市", + "children": [ + { + "code": "371102", + "name": "东港区" + }, + { + "code": "371103", + "name": "岚山区" + }, + { + "code": "371121", + "name": "五莲县" + }, + { + "code": "371122", + "name": "莒县" + }, + { + "code": "371171", + "name": "日照经济技术开发区" + } + ] + }, + { + "code": "3713", + "name": "临沂市", + "children": [ + { + "code": "371302", + "name": "兰山区" + }, + { + "code": "371311", + "name": "罗庄区" + }, + { + "code": "371312", + "name": "河东区" + }, + { + "code": "371321", + "name": "沂南县" + }, + { + "code": "371322", + "name": "郯城县" + }, + { + "code": "371323", + "name": "沂水县" + }, + { + "code": "371324", + "name": "兰陵县" + }, + { + "code": "371325", + "name": "费县" + }, + { + "code": "371326", + "name": "平邑县" + }, + { + "code": "371327", + "name": "莒南县" + }, + { + "code": "371328", + "name": "蒙阴县" + }, + { + "code": "371329", + "name": "临沭县" + }, + { + "code": "371371", + "name": "临沂高新技术产业开发区" + } + ] + }, + { + "code": "3714", + "name": "德州市", + "children": [ + { + "code": "371402", + "name": "德城区" + }, + { + "code": "371403", + "name": "陵城区" + }, + { + "code": "371422", + "name": "宁津县" + }, + { + "code": "371423", + "name": "庆云县" + }, + { + "code": "371424", + "name": "临邑县" + }, + { + "code": "371425", + "name": "齐河县" + }, + { + "code": "371426", + "name": "平原县" + }, + { + "code": "371427", + "name": "夏津县" + }, + { + "code": "371428", + "name": "武城县" + }, + { + "code": "371471", + "name": "德州天衢新区" + }, + { + "code": "371481", + "name": "乐陵市" + }, + { + "code": "371482", + "name": "禹城市" + } + ] + }, + { + "code": "3715", + "name": "聊城市", + "children": [ + { + "code": "371502", + "name": "东昌府区" + }, + { + "code": "371503", + "name": "茌平区" + }, + { + "code": "371521", + "name": "阳谷县" + }, + { + "code": "371522", + "name": "莘县" + }, + { + "code": "371524", + "name": "东阿县" + }, + { + "code": "371525", + "name": "冠县" + }, + { + "code": "371526", + "name": "高唐县" + }, + { + "code": "371581", + "name": "临清市" + } + ] + }, + { + "code": "3716", + "name": "滨州市", + "children": [ + { + "code": "371602", + "name": "滨城区" + }, + { + "code": "371603", + "name": "沾化区" + }, + { + "code": "371621", + "name": "惠民县" + }, + { + "code": "371622", + "name": "阳信县" + }, + { + "code": "371623", + "name": "无棣县" + }, + { + "code": "371625", + "name": "博兴县" + }, + { + "code": "371681", + "name": "邹平市" + } + ] + }, + { + "code": "3717", + "name": "菏泽市", + "children": [ + { + "code": "371702", + "name": "牡丹区" + }, + { + "code": "371703", + "name": "定陶区" + }, + { + "code": "371721", + "name": "曹县" + }, + { + "code": "371722", + "name": "单县" + }, + { + "code": "371723", + "name": "成武县" + }, + { + "code": "371724", + "name": "巨野县" + }, + { + "code": "371725", + "name": "郓城县" + }, + { + "code": "371726", + "name": "鄄城县" + }, + { + "code": "371728", + "name": "东明县" + }, + { + "code": "371771", + "name": "菏泽经济技术开发区" + }, + { + "code": "371772", + "name": "菏泽高新技术开发区" + } + ] + } + ] + }, + { + "code": "41", + "name": "河南省", + "children": [ + { + "code": "4101", + "name": "郑州市", + "children": [ + { + "code": "410102", + "name": "中原区" + }, + { + "code": "410103", + "name": "二七区" + }, + { + "code": "410104", + "name": "管城回族区" + }, + { + "code": "410105", + "name": "金水区" + }, + { + "code": "410106", + "name": "上街区" + }, + { + "code": "410108", + "name": "惠济区" + }, + { + "code": "410122", + "name": "中牟县" + }, + { + "code": "410171", + "name": "郑州经济技术开发区" + }, + { + "code": "410172", + "name": "郑州高新技术产业开发区" + }, + { + "code": "410173", + "name": "郑州航空港经济综合实验区" + }, + { + "code": "410181", + "name": "巩义市" + }, + { + "code": "410182", + "name": "荥阳市" + }, + { + "code": "410183", + "name": "新密市" + }, + { + "code": "410184", + "name": "新郑市" + }, + { + "code": "410185", + "name": "登封市" + } + ] + }, + { + "code": "4102", + "name": "开封市", + "children": [ + { + "code": "410202", + "name": "龙亭区" + }, + { + "code": "410203", + "name": "顺河回族区" + }, + { + "code": "410204", + "name": "鼓楼区" + }, + { + "code": "410205", + "name": "禹王台区" + }, + { + "code": "410212", + "name": "祥符区" + }, + { + "code": "410221", + "name": "杞县" + }, + { + "code": "410222", + "name": "通许县" + }, + { + "code": "410223", + "name": "尉氏县" + }, + { + "code": "410225", + "name": "兰考县" + } + ] + }, + { + "code": "4103", + "name": "洛阳市", + "children": [ + { + "code": "410302", + "name": "老城区" + }, + { + "code": "410303", + "name": "西工区" + }, + { + "code": "410304", + "name": "瀍河回族区" + }, + { + "code": "410305", + "name": "涧西区" + }, + { + "code": "410307", + "name": "偃师区" + }, + { + "code": "410308", + "name": "孟津区" + }, + { + "code": "410311", + "name": "洛龙区" + }, + { + "code": "410323", + "name": "新安县" + }, + { + "code": "410324", + "name": "栾川县" + }, + { + "code": "410325", + "name": "嵩县" + }, + { + "code": "410326", + "name": "汝阳县" + }, + { + "code": "410327", + "name": "宜阳县" + }, + { + "code": "410328", + "name": "洛宁县" + }, + { + "code": "410329", + "name": "伊川县" + }, + { + "code": "410371", + "name": "洛阳高新技术产业开发区" + } + ] + }, + { + "code": "4104", + "name": "平顶山市", + "children": [ + { + "code": "410402", + "name": "新华区" + }, + { + "code": "410403", + "name": "卫东区" + }, + { + "code": "410404", + "name": "石龙区" + }, + { + "code": "410411", + "name": "湛河区" + }, + { + "code": "410421", + "name": "宝丰县" + }, + { + "code": "410422", + "name": "叶县" + }, + { + "code": "410423", + "name": "鲁山县" + }, + { + "code": "410425", + "name": "郏县" + }, + { + "code": "410471", + "name": "平顶山高新技术产业开发区" + }, + { + "code": "410472", + "name": "平顶山市城乡一体化示范区" + }, + { + "code": "410481", + "name": "舞钢市" + }, + { + "code": "410482", + "name": "汝州市" + } + ] + }, + { + "code": "4105", + "name": "安阳市", + "children": [ + { + "code": "410502", + "name": "文峰区" + }, + { + "code": "410503", + "name": "北关区" + }, + { + "code": "410505", + "name": "殷都区" + }, + { + "code": "410506", + "name": "龙安区" + }, + { + "code": "410522", + "name": "安阳县" + }, + { + "code": "410523", + "name": "汤阴县" + }, + { + "code": "410526", + "name": "滑县" + }, + { + "code": "410527", + "name": "内黄县" + }, + { + "code": "410571", + "name": "安阳高新技术产业开发区" + }, + { + "code": "410581", + "name": "林州市" + } + ] + }, + { + "code": "4106", + "name": "鹤壁市", + "children": [ + { + "code": "410602", + "name": "鹤山区" + }, + { + "code": "410603", + "name": "山城区" + }, + { + "code": "410611", + "name": "淇滨区" + }, + { + "code": "410621", + "name": "浚县" + }, + { + "code": "410622", + "name": "淇县" + }, + { + "code": "410671", + "name": "鹤壁经济技术开发区" + } + ] + }, + { + "code": "4107", + "name": "新乡市", + "children": [ + { + "code": "410702", + "name": "红旗区" + }, + { + "code": "410703", + "name": "卫滨区" + }, + { + "code": "410704", + "name": "凤泉区" + }, + { + "code": "410711", + "name": "牧野区" + }, + { + "code": "410721", + "name": "新乡县" + }, + { + "code": "410724", + "name": "获嘉县" + }, + { + "code": "410725", + "name": "原阳县" + }, + { + "code": "410726", + "name": "延津县" + }, + { + "code": "410727", + "name": "封丘县" + }, + { + "code": "410771", + "name": "新乡高新技术产业开发区" + }, + { + "code": "410772", + "name": "新乡经济技术开发区" + }, + { + "code": "410773", + "name": "新乡市平原城乡一体化示范区" + }, + { + "code": "410781", + "name": "卫辉市" + }, + { + "code": "410782", + "name": "辉县市" + }, + { + "code": "410783", + "name": "长垣市" + } + ] + }, + { + "code": "4108", + "name": "焦作市", + "children": [ + { + "code": "410802", + "name": "解放区" + }, + { + "code": "410803", + "name": "中站区" + }, + { + "code": "410804", + "name": "马村区" + }, + { + "code": "410811", + "name": "山阳区" + }, + { + "code": "410821", + "name": "修武县" + }, + { + "code": "410822", + "name": "博爱县" + }, + { + "code": "410823", + "name": "武陟县" + }, + { + "code": "410825", + "name": "温县" + }, + { + "code": "410871", + "name": "焦作城乡一体化示范区" + }, + { + "code": "410882", + "name": "沁阳市" + }, + { + "code": "410883", + "name": "孟州市" + } + ] + }, + { + "code": "4109", + "name": "濮阳市", + "children": [ + { + "code": "410902", + "name": "华龙区" + }, + { + "code": "410922", + "name": "清丰县" + }, + { + "code": "410923", + "name": "南乐县" + }, + { + "code": "410926", + "name": "范县" + }, + { + "code": "410927", + "name": "台前县" + }, + { + "code": "410928", + "name": "濮阳县" + }, + { + "code": "410971", + "name": "河南濮阳工业园区" + }, + { + "code": "410972", + "name": "濮阳经济技术开发区" + } + ] + }, + { + "code": "4110", + "name": "许昌市", + "children": [ + { + "code": "411002", + "name": "魏都区" + }, + { + "code": "411003", + "name": "建安区" + }, + { + "code": "411024", + "name": "鄢陵县" + }, + { + "code": "411025", + "name": "襄城县" + }, + { + "code": "411071", + "name": "许昌经济技术开发区" + }, + { + "code": "411081", + "name": "禹州市" + }, + { + "code": "411082", + "name": "长葛市" + } + ] + }, + { + "code": "4111", + "name": "漯河市", + "children": [ + { + "code": "411102", + "name": "源汇区" + }, + { + "code": "411103", + "name": "郾城区" + }, + { + "code": "411104", + "name": "召陵区" + }, + { + "code": "411121", + "name": "舞阳县" + }, + { + "code": "411122", + "name": "临颍县" + }, + { + "code": "411171", + "name": "漯河经济技术开发区" + } + ] + }, + { + "code": "4112", + "name": "三门峡市", + "children": [ + { + "code": "411202", + "name": "湖滨区" + }, + { + "code": "411203", + "name": "陕州区" + }, + { + "code": "411221", + "name": "渑池县" + }, + { + "code": "411224", + "name": "卢氏县" + }, + { + "code": "411271", + "name": "河南三门峡经济开发区" + }, + { + "code": "411281", + "name": "义马市" + }, + { + "code": "411282", + "name": "灵宝市" + } + ] + }, + { + "code": "4113", + "name": "南阳市", + "children": [ + { + "code": "411302", + "name": "宛城区" + }, + { + "code": "411303", + "name": "卧龙区" + }, + { + "code": "411321", + "name": "南召县" + }, + { + "code": "411322", + "name": "方城县" + }, + { + "code": "411323", + "name": "西峡县" + }, + { + "code": "411324", + "name": "镇平县" + }, + { + "code": "411325", + "name": "内乡县" + }, + { + "code": "411326", + "name": "淅川县" + }, + { + "code": "411327", + "name": "社旗县" + }, + { + "code": "411328", + "name": "唐河县" + }, + { + "code": "411329", + "name": "新野县" + }, + { + "code": "411330", + "name": "桐柏县" + }, + { + "code": "411371", + "name": "南阳高新技术产业开发区" + }, + { + "code": "411372", + "name": "南阳市城乡一体化示范区" + }, + { + "code": "411381", + "name": "邓州市" + } + ] + }, + { + "code": "4114", + "name": "商丘市", + "children": [ + { + "code": "411402", + "name": "梁园区" + }, + { + "code": "411403", + "name": "睢阳区" + }, + { + "code": "411421", + "name": "民权县" + }, + { + "code": "411422", + "name": "睢县" + }, + { + "code": "411423", + "name": "宁陵县" + }, + { + "code": "411424", + "name": "柘城县" + }, + { + "code": "411425", + "name": "虞城县" + }, + { + "code": "411426", + "name": "夏邑县" + }, + { + "code": "411471", + "name": "豫东综合物流产业聚集区" + }, + { + "code": "411472", + "name": "河南商丘经济开发区" + }, + { + "code": "411481", + "name": "永城市" + } + ] + }, + { + "code": "4115", + "name": "信阳市", + "children": [ + { + "code": "411502", + "name": "浉河区" + }, + { + "code": "411503", + "name": "平桥区" + }, + { + "code": "411521", + "name": "罗山县" + }, + { + "code": "411522", + "name": "光山县" + }, + { + "code": "411523", + "name": "新县" + }, + { + "code": "411524", + "name": "商城县" + }, + { + "code": "411525", + "name": "固始县" + }, + { + "code": "411526", + "name": "潢川县" + }, + { + "code": "411527", + "name": "淮滨县" + }, + { + "code": "411528", + "name": "息县" + }, + { + "code": "411571", + "name": "信阳高新技术产业开发区" + } + ] + }, + { + "code": "4116", + "name": "周口市", + "children": [ + { + "code": "411602", + "name": "川汇区" + }, + { + "code": "411603", + "name": "淮阳区" + }, + { + "code": "411621", + "name": "扶沟县" + }, + { + "code": "411622", + "name": "西华县" + }, + { + "code": "411623", + "name": "商水县" + }, + { + "code": "411624", + "name": "沈丘县" + }, + { + "code": "411625", + "name": "郸城县" + }, + { + "code": "411627", + "name": "太康县" + }, + { + "code": "411628", + "name": "鹿邑县" + }, + { + "code": "411671", + "name": "周口临港开发区" + }, + { + "code": "411681", + "name": "项城市" + } + ] + }, + { + "code": "4117", + "name": "驻马店市", + "children": [ + { + "code": "411702", + "name": "驿城区" + }, + { + "code": "411721", + "name": "西平县" + }, + { + "code": "411722", + "name": "上蔡县" + }, + { + "code": "411723", + "name": "平舆县" + }, + { + "code": "411724", + "name": "正阳县" + }, + { + "code": "411725", + "name": "确山县" + }, + { + "code": "411726", + "name": "泌阳县" + }, + { + "code": "411727", + "name": "汝南县" + }, + { + "code": "411728", + "name": "遂平县" + }, + { + "code": "411729", + "name": "新蔡县" + }, + { + "code": "411771", + "name": "河南驻马店经济开发区" + } + ] + }, + { + "code": "4190", + "name": "省直辖县级行政区划", + "children": [ + { + "code": "419001", + "name": "济源市" + } + ] + } + ] + }, + { + "code": "42", + "name": "湖北省", + "children": [ + { + "code": "4201", + "name": "武汉市", + "children": [ + { + "code": "420102", + "name": "江岸区" + }, + { + "code": "420103", + "name": "江汉区" + }, + { + "code": "420104", + "name": "硚口区" + }, + { + "code": "420105", + "name": "汉阳区" + }, + { + "code": "420106", + "name": "武昌区" + }, + { + "code": "420107", + "name": "青山区" + }, + { + "code": "420111", + "name": "洪山区" + }, + { + "code": "420112", + "name": "东西湖区" + }, + { + "code": "420113", + "name": "汉南区" + }, + { + "code": "420114", + "name": "蔡甸区" + }, + { + "code": "420115", + "name": "江夏区" + }, + { + "code": "420116", + "name": "黄陂区" + }, + { + "code": "420117", + "name": "新洲区" + } + ] + }, + { + "code": "4202", + "name": "黄石市", + "children": [ + { + "code": "420202", + "name": "黄石港区" + }, + { + "code": "420203", + "name": "西塞山区" + }, + { + "code": "420204", + "name": "下陆区" + }, + { + "code": "420205", + "name": "铁山区" + }, + { + "code": "420222", + "name": "阳新县" + }, + { + "code": "420281", + "name": "大冶市" + } + ] + }, + { + "code": "4203", + "name": "十堰市", + "children": [ + { + "code": "420302", + "name": "茅箭区" + }, + { + "code": "420303", + "name": "张湾区" + }, + { + "code": "420304", + "name": "郧阳区" + }, + { + "code": "420322", + "name": "郧西县" + }, + { + "code": "420323", + "name": "竹山县" + }, + { + "code": "420324", + "name": "竹溪县" + }, + { + "code": "420325", + "name": "房县" + }, + { + "code": "420381", + "name": "丹江口市" + } + ] + }, + { + "code": "4205", + "name": "宜昌市", + "children": [ + { + "code": "420502", + "name": "西陵区" + }, + { + "code": "420503", + "name": "伍家岗区" + }, + { + "code": "420504", + "name": "点军区" + }, + { + "code": "420505", + "name": "猇亭区" + }, + { + "code": "420506", + "name": "夷陵区" + }, + { + "code": "420525", + "name": "远安县" + }, + { + "code": "420526", + "name": "兴山县" + }, + { + "code": "420527", + "name": "秭归县" + }, + { + "code": "420528", + "name": "长阳土家族自治县" + }, + { + "code": "420529", + "name": "五峰土家族自治县" + }, + { + "code": "420581", + "name": "宜都市" + }, + { + "code": "420582", + "name": "当阳市" + }, + { + "code": "420583", + "name": "枝江市" + } + ] + }, + { + "code": "4206", + "name": "襄阳市", + "children": [ + { + "code": "420602", + "name": "襄城区" + }, + { + "code": "420606", + "name": "樊城区" + }, + { + "code": "420607", + "name": "襄州区" + }, + { + "code": "420624", + "name": "南漳县" + }, + { + "code": "420625", + "name": "谷城县" + }, + { + "code": "420626", + "name": "保康县" + }, + { + "code": "420682", + "name": "老河口市" + }, + { + "code": "420683", + "name": "枣阳市" + }, + { + "code": "420684", + "name": "宜城市" + } + ] + }, + { + "code": "4207", + "name": "鄂州市", + "children": [ + { + "code": "420702", + "name": "梁子湖区" + }, + { + "code": "420703", + "name": "华容区" + }, + { + "code": "420704", + "name": "鄂城区" + } + ] + }, + { + "code": "4208", + "name": "荆门市", + "children": [ + { + "code": "420802", + "name": "东宝区" + }, + { + "code": "420804", + "name": "掇刀区" + }, + { + "code": "420822", + "name": "沙洋县" + }, + { + "code": "420881", + "name": "钟祥市" + }, + { + "code": "420882", + "name": "京山市" + } + ] + }, + { + "code": "4209", + "name": "孝感市", + "children": [ + { + "code": "420902", + "name": "孝南区" + }, + { + "code": "420921", + "name": "孝昌县" + }, + { + "code": "420922", + "name": "大悟县" + }, + { + "code": "420923", + "name": "云梦县" + }, + { + "code": "420981", + "name": "应城市" + }, + { + "code": "420982", + "name": "安陆市" + }, + { + "code": "420984", + "name": "汉川市" + } + ] + }, + { + "code": "4210", + "name": "荆州市", + "children": [ + { + "code": "421002", + "name": "沙市区" + }, + { + "code": "421003", + "name": "荆州区" + }, + { + "code": "421022", + "name": "公安县" + }, + { + "code": "421024", + "name": "江陵县" + }, + { + "code": "421071", + "name": "荆州经济技术开发区" + }, + { + "code": "421081", + "name": "石首市" + }, + { + "code": "421083", + "name": "洪湖市" + }, + { + "code": "421087", + "name": "松滋市" + }, + { + "code": "421088", + "name": "监利市" + } + ] + }, + { + "code": "4211", + "name": "黄冈市", + "children": [ + { + "code": "421102", + "name": "黄州区" + }, + { + "code": "421121", + "name": "团风县" + }, + { + "code": "421122", + "name": "红安县" + }, + { + "code": "421123", + "name": "罗田县" + }, + { + "code": "421124", + "name": "英山县" + }, + { + "code": "421125", + "name": "浠水县" + }, + { + "code": "421126", + "name": "蕲春县" + }, + { + "code": "421127", + "name": "黄梅县" + }, + { + "code": "421171", + "name": "龙感湖管理区" + }, + { + "code": "421181", + "name": "麻城市" + }, + { + "code": "421182", + "name": "武穴市" + } + ] + }, + { + "code": "4212", + "name": "咸宁市", + "children": [ + { + "code": "421202", + "name": "咸安区" + }, + { + "code": "421221", + "name": "嘉鱼县" + }, + { + "code": "421222", + "name": "通城县" + }, + { + "code": "421223", + "name": "崇阳县" + }, + { + "code": "421224", + "name": "通山县" + }, + { + "code": "421281", + "name": "赤壁市" + } + ] + }, + { + "code": "4213", + "name": "随州市", + "children": [ + { + "code": "421303", + "name": "曾都区" + }, + { + "code": "421321", + "name": "随县" + }, + { + "code": "421381", + "name": "广水市" + } + ] + }, + { + "code": "4228", + "name": "恩施土家族苗族自治州", + "children": [ + { + "code": "422801", + "name": "恩施市" + }, + { + "code": "422802", + "name": "利川市" + }, + { + "code": "422822", + "name": "建始县" + }, + { + "code": "422823", + "name": "巴东县" + }, + { + "code": "422825", + "name": "宣恩县" + }, + { + "code": "422826", + "name": "咸丰县" + }, + { + "code": "422827", + "name": "来凤县" + }, + { + "code": "422828", + "name": "鹤峰县" + } + ] + }, + { + "code": "4290", + "name": "省直辖县级行政区划", + "children": [ + { + "code": "429004", + "name": "仙桃市" + }, + { + "code": "429005", + "name": "潜江市" + }, + { + "code": "429006", + "name": "天门市" + }, + { + "code": "429021", + "name": "神农架林区" + } + ] + } + ] + }, + { + "code": "43", + "name": "湖南省", + "children": [ + { + "code": "4301", + "name": "长沙市", + "children": [ + { + "code": "430102", + "name": "芙蓉区" + }, + { + "code": "430103", + "name": "天心区" + }, + { + "code": "430104", + "name": "岳麓区" + }, + { + "code": "430105", + "name": "开福区" + }, + { + "code": "430111", + "name": "雨花区" + }, + { + "code": "430112", + "name": "望城区" + }, + { + "code": "430121", + "name": "长沙县" + }, + { + "code": "430181", + "name": "浏阳市" + }, + { + "code": "430182", + "name": "宁乡市" + } + ] + }, + { + "code": "4302", + "name": "株洲市", + "children": [ + { + "code": "430202", + "name": "荷塘区" + }, + { + "code": "430203", + "name": "芦淞区" + }, + { + "code": "430204", + "name": "石峰区" + }, + { + "code": "430211", + "name": "天元区" + }, + { + "code": "430212", + "name": "渌口区" + }, + { + "code": "430223", + "name": "攸县" + }, + { + "code": "430224", + "name": "茶陵县" + }, + { + "code": "430225", + "name": "炎陵县" + }, + { + "code": "430281", + "name": "醴陵市" + } + ] + }, + { + "code": "4303", + "name": "湘潭市", + "children": [ + { + "code": "430302", + "name": "雨湖区" + }, + { + "code": "430304", + "name": "岳塘区" + }, + { + "code": "430321", + "name": "湘潭县" + }, + { + "code": "430371", + "name": "湖南湘潭高新技术产业园区" + }, + { + "code": "430372", + "name": "湘潭昭山示范区" + }, + { + "code": "430373", + "name": "湘潭九华示范区" + }, + { + "code": "430381", + "name": "湘乡市" + }, + { + "code": "430382", + "name": "韶山市" + } + ] + }, + { + "code": "4304", + "name": "衡阳市", + "children": [ + { + "code": "430405", + "name": "珠晖区" + }, + { + "code": "430406", + "name": "雁峰区" + }, + { + "code": "430407", + "name": "石鼓区" + }, + { + "code": "430408", + "name": "蒸湘区" + }, + { + "code": "430412", + "name": "南岳区" + }, + { + "code": "430421", + "name": "衡阳县" + }, + { + "code": "430422", + "name": "衡南县" + }, + { + "code": "430423", + "name": "衡山县" + }, + { + "code": "430424", + "name": "衡东县" + }, + { + "code": "430426", + "name": "祁东县" + }, + { + "code": "430473", + "name": "湖南衡阳松木经济开发区" + }, + { + "code": "430476", + "name": "湖南衡阳高新技术产业园区" + }, + { + "code": "430481", + "name": "耒阳市" + }, + { + "code": "430482", + "name": "常宁市" + } + ] + }, + { + "code": "4305", + "name": "邵阳市", + "children": [ + { + "code": "430502", + "name": "双清区" + }, + { + "code": "430503", + "name": "大祥区" + }, + { + "code": "430511", + "name": "北塔区" + }, + { + "code": "430522", + "name": "新邵县" + }, + { + "code": "430523", + "name": "邵阳县" + }, + { + "code": "430524", + "name": "隆回县" + }, + { + "code": "430525", + "name": "洞口县" + }, + { + "code": "430527", + "name": "绥宁县" + }, + { + "code": "430528", + "name": "新宁县" + }, + { + "code": "430529", + "name": "城步苗族自治县" + }, + { + "code": "430581", + "name": "武冈市" + }, + { + "code": "430582", + "name": "邵东市" + } + ] + }, + { + "code": "4306", + "name": "岳阳市", + "children": [ + { + "code": "430602", + "name": "岳阳楼区" + }, + { + "code": "430603", + "name": "云溪区" + }, + { + "code": "430611", + "name": "君山区" + }, + { + "code": "430621", + "name": "岳阳县" + }, + { + "code": "430623", + "name": "华容县" + }, + { + "code": "430624", + "name": "湘阴县" + }, + { + "code": "430626", + "name": "平江县" + }, + { + "code": "430671", + "name": "岳阳市屈原管理区" + }, + { + "code": "430681", + "name": "汨罗市" + }, + { + "code": "430682", + "name": "临湘市" + } + ] + }, + { + "code": "4307", + "name": "常德市", + "children": [ + { + "code": "430702", + "name": "武陵区" + }, + { + "code": "430703", + "name": "鼎城区" + }, + { + "code": "430721", + "name": "安乡县" + }, + { + "code": "430722", + "name": "汉寿县" + }, + { + "code": "430723", + "name": "澧县" + }, + { + "code": "430724", + "name": "临澧县" + }, + { + "code": "430725", + "name": "桃源县" + }, + { + "code": "430726", + "name": "石门县" + }, + { + "code": "430771", + "name": "常德市西洞庭管理区" + }, + { + "code": "430781", + "name": "津市市" + } + ] + }, + { + "code": "4308", + "name": "张家界市", + "children": [ + { + "code": "430802", + "name": "永定区" + }, + { + "code": "430811", + "name": "武陵源区" + }, + { + "code": "430821", + "name": "慈利县" + }, + { + "code": "430822", + "name": "桑植县" + } + ] + }, + { + "code": "4309", + "name": "益阳市", + "children": [ + { + "code": "430902", + "name": "资阳区" + }, + { + "code": "430903", + "name": "赫山区" + }, + { + "code": "430921", + "name": "南县" + }, + { + "code": "430922", + "name": "桃江县" + }, + { + "code": "430923", + "name": "安化县" + }, + { + "code": "430971", + "name": "益阳市大通湖管理区" + }, + { + "code": "430972", + "name": "湖南益阳高新技术产业园区" + }, + { + "code": "430981", + "name": "沅江市" + } + ] + }, + { + "code": "4310", + "name": "郴州市", + "children": [ + { + "code": "431002", + "name": "北湖区" + }, + { + "code": "431003", + "name": "苏仙区" + }, + { + "code": "431021", + "name": "桂阳县" + }, + { + "code": "431022", + "name": "宜章县" + }, + { + "code": "431023", + "name": "永兴县" + }, + { + "code": "431024", + "name": "嘉禾县" + }, + { + "code": "431025", + "name": "临武县" + }, + { + "code": "431026", + "name": "汝城县" + }, + { + "code": "431027", + "name": "桂东县" + }, + { + "code": "431028", + "name": "安仁县" + }, + { + "code": "431081", + "name": "资兴市" + } + ] + }, + { + "code": "4311", + "name": "永州市", + "children": [ + { + "code": "431102", + "name": "零陵区" + }, + { + "code": "431103", + "name": "冷水滩区" + }, + { + "code": "431122", + "name": "东安县" + }, + { + "code": "431123", + "name": "双牌县" + }, + { + "code": "431124", + "name": "道县" + }, + { + "code": "431125", + "name": "江永县" + }, + { + "code": "431126", + "name": "宁远县" + }, + { + "code": "431127", + "name": "蓝山县" + }, + { + "code": "431128", + "name": "新田县" + }, + { + "code": "431129", + "name": "江华瑶族自治县" + }, + { + "code": "431171", + "name": "永州经济技术开发区" + }, + { + "code": "431173", + "name": "永州市回龙圩管理区" + }, + { + "code": "431181", + "name": "祁阳市" + } + ] + }, + { + "code": "4312", + "name": "怀化市", + "children": [ + { + "code": "431202", + "name": "鹤城区" + }, + { + "code": "431221", + "name": "中方县" + }, + { + "code": "431222", + "name": "沅陵县" + }, + { + "code": "431223", + "name": "辰溪县" + }, + { + "code": "431224", + "name": "溆浦县" + }, + { + "code": "431225", + "name": "会同县" + }, + { + "code": "431226", + "name": "麻阳苗族自治县" + }, + { + "code": "431227", + "name": "新晃侗族自治县" + }, + { + "code": "431228", + "name": "芷江侗族自治县" + }, + { + "code": "431229", + "name": "靖州苗族侗族自治县" + }, + { + "code": "431230", + "name": "通道侗族自治县" + }, + { + "code": "431271", + "name": "怀化市洪江管理区" + }, + { + "code": "431281", + "name": "洪江市" + } + ] + }, + { + "code": "4313", + "name": "娄底市", + "children": [ + { + "code": "431302", + "name": "娄星区" + }, + { + "code": "431321", + "name": "双峰县" + }, + { + "code": "431322", + "name": "新化县" + }, + { + "code": "431381", + "name": "冷水江市" + }, + { + "code": "431382", + "name": "涟源市" + } + ] + }, + { + "code": "4331", + "name": "湘西土家族苗族自治州", + "children": [ + { + "code": "433101", + "name": "吉首市" + }, + { + "code": "433122", + "name": "泸溪县" + }, + { + "code": "433123", + "name": "凤凰县" + }, + { + "code": "433124", + "name": "花垣县" + }, + { + "code": "433125", + "name": "保靖县" + }, + { + "code": "433126", + "name": "古丈县" + }, + { + "code": "433127", + "name": "永顺县" + }, + { + "code": "433130", + "name": "龙山县" + } + ] + } + ] + }, + { + "code": "44", + "name": "广东省", + "children": [ + { + "code": "4401", + "name": "广州市", + "children": [ + { + "code": "440103", + "name": "荔湾区" + }, + { + "code": "440104", + "name": "越秀区" + }, + { + "code": "440105", + "name": "海珠区" + }, + { + "code": "440106", + "name": "天河区" + }, + { + "code": "440111", + "name": "白云区" + }, + { + "code": "440112", + "name": "黄埔区" + }, + { + "code": "440113", + "name": "番禺区" + }, + { + "code": "440114", + "name": "花都区" + }, + { + "code": "440115", + "name": "南沙区" + }, + { + "code": "440117", + "name": "从化区" + }, + { + "code": "440118", + "name": "增城区" + } + ] + }, + { + "code": "4402", + "name": "韶关市", + "children": [ + { + "code": "440203", + "name": "武江区" + }, + { + "code": "440204", + "name": "浈江区" + }, + { + "code": "440205", + "name": "曲江区" + }, + { + "code": "440222", + "name": "始兴县" + }, + { + "code": "440224", + "name": "仁化县" + }, + { + "code": "440229", + "name": "翁源县" + }, + { + "code": "440232", + "name": "乳源瑶族自治县" + }, + { + "code": "440233", + "name": "新丰县" + }, + { + "code": "440281", + "name": "乐昌市" + }, + { + "code": "440282", + "name": "南雄市" + } + ] + }, + { + "code": "4403", + "name": "深圳市", + "children": [ + { + "code": "440303", + "name": "罗湖区" + }, + { + "code": "440304", + "name": "福田区" + }, + { + "code": "440305", + "name": "南山区" + }, + { + "code": "440306", + "name": "宝安区" + }, + { + "code": "440307", + "name": "龙岗区" + }, + { + "code": "440308", + "name": "盐田区" + }, + { + "code": "440309", + "name": "龙华区" + }, + { + "code": "440310", + "name": "坪山区" + }, + { + "code": "440311", + "name": "光明区" + } + ] + }, + { + "code": "4404", + "name": "珠海市", + "children": [ + { + "code": "440402", + "name": "香洲区" + }, + { + "code": "440403", + "name": "斗门区" + }, + { + "code": "440404", + "name": "金湾区" + } + ] + }, + { + "code": "4405", + "name": "汕头市", + "children": [ + { + "code": "440507", + "name": "龙湖区" + }, + { + "code": "440511", + "name": "金平区" + }, + { + "code": "440512", + "name": "濠江区" + }, + { + "code": "440513", + "name": "潮阳区" + }, + { + "code": "440514", + "name": "潮南区" + }, + { + "code": "440515", + "name": "澄海区" + }, + { + "code": "440523", + "name": "南澳县" + } + ] + }, + { + "code": "4406", + "name": "佛山市", + "children": [ + { + "code": "440604", + "name": "禅城区" + }, + { + "code": "440605", + "name": "南海区" + }, + { + "code": "440606", + "name": "顺德区" + }, + { + "code": "440607", + "name": "三水区" + }, + { + "code": "440608", + "name": "高明区" + } + ] + }, + { + "code": "4407", + "name": "江门市", + "children": [ + { + "code": "440703", + "name": "蓬江区" + }, + { + "code": "440704", + "name": "江海区" + }, + { + "code": "440705", + "name": "新会区" + }, + { + "code": "440781", + "name": "台山市" + }, + { + "code": "440783", + "name": "开平市" + }, + { + "code": "440784", + "name": "鹤山市" + }, + { + "code": "440785", + "name": "恩平市" + } + ] + }, + { + "code": "4408", + "name": "湛江市", + "children": [ + { + "code": "440802", + "name": "赤坎区" + }, + { + "code": "440803", + "name": "霞山区" + }, + { + "code": "440804", + "name": "坡头区" + }, + { + "code": "440811", + "name": "麻章区" + }, + { + "code": "440823", + "name": "遂溪县" + }, + { + "code": "440825", + "name": "徐闻县" + }, + { + "code": "440881", + "name": "廉江市" + }, + { + "code": "440882", + "name": "雷州市" + }, + { + "code": "440883", + "name": "吴川市" + } + ] + }, + { + "code": "4409", + "name": "茂名市", + "children": [ + { + "code": "440902", + "name": "茂南区" + }, + { + "code": "440904", + "name": "电白区" + }, + { + "code": "440981", + "name": "高州市" + }, + { + "code": "440982", + "name": "化州市" + }, + { + "code": "440983", + "name": "信宜市" + } + ] + }, + { + "code": "4412", + "name": "肇庆市", + "children": [ + { + "code": "441202", + "name": "端州区" + }, + { + "code": "441203", + "name": "鼎湖区" + }, + { + "code": "441204", + "name": "高要区" + }, + { + "code": "441223", + "name": "广宁县" + }, + { + "code": "441224", + "name": "怀集县" + }, + { + "code": "441225", + "name": "封开县" + }, + { + "code": "441226", + "name": "德庆县" + }, + { + "code": "441284", + "name": "四会市" + } + ] + }, + { + "code": "4413", + "name": "惠州市", + "children": [ + { + "code": "441302", + "name": "惠城区" + }, + { + "code": "441303", + "name": "惠阳区" + }, + { + "code": "441322", + "name": "博罗县" + }, + { + "code": "441323", + "name": "惠东县" + }, + { + "code": "441324", + "name": "龙门县" + } + ] + }, + { + "code": "4414", + "name": "梅州市", + "children": [ + { + "code": "441402", + "name": "梅江区" + }, + { + "code": "441403", + "name": "梅县区" + }, + { + "code": "441422", + "name": "大埔县" + }, + { + "code": "441423", + "name": "丰顺县" + }, + { + "code": "441424", + "name": "五华县" + }, + { + "code": "441426", + "name": "平远县" + }, + { + "code": "441427", + "name": "蕉岭县" + }, + { + "code": "441481", + "name": "兴宁市" + } + ] + }, + { + "code": "4415", + "name": "汕尾市", + "children": [ + { + "code": "441502", + "name": "城区" + }, + { + "code": "441521", + "name": "海丰县" + }, + { + "code": "441523", + "name": "陆河县" + }, + { + "code": "441581", + "name": "陆丰市" + } + ] + }, + { + "code": "4416", + "name": "河源市", + "children": [ + { + "code": "441602", + "name": "源城区" + }, + { + "code": "441621", + "name": "紫金县" + }, + { + "code": "441622", + "name": "龙川县" + }, + { + "code": "441623", + "name": "连平县" + }, + { + "code": "441624", + "name": "和平县" + }, + { + "code": "441625", + "name": "东源县" + } + ] + }, + { + "code": "4417", + "name": "阳江市", + "children": [ + { + "code": "441702", + "name": "江城区" + }, + { + "code": "441704", + "name": "阳东区" + }, + { + "code": "441721", + "name": "阳西县" + }, + { + "code": "441781", + "name": "阳春市" + } + ] + }, + { + "code": "4418", + "name": "清远市", + "children": [ + { + "code": "441802", + "name": "清城区" + }, + { + "code": "441803", + "name": "清新区" + }, + { + "code": "441821", + "name": "佛冈县" + }, + { + "code": "441823", + "name": "阳山县" + }, + { + "code": "441825", + "name": "连山壮族瑶族自治县" + }, + { + "code": "441826", + "name": "连南瑶族自治县" + }, + { + "code": "441881", + "name": "英德市" + }, + { + "code": "441882", + "name": "连州市" + } + ] + }, + { + "code": "4419", + "name": "东莞市", + "children": [ + { + "code": "441900003", + "name": "东城街道" + }, + { + "code": "441900004", + "name": "南城街道" + }, + { + "code": "441900005", + "name": "万江街道" + }, + { + "code": "441900006", + "name": "莞城街道" + }, + { + "code": "441900101", + "name": "石碣镇" + }, + { + "code": "441900102", + "name": "石龙镇" + }, + { + "code": "441900103", + "name": "茶山镇" + }, + { + "code": "441900104", + "name": "石排镇" + }, + { + "code": "441900105", + "name": "企石镇" + }, + { + "code": "441900106", + "name": "横沥镇" + }, + { + "code": "441900107", + "name": "桥头镇" + }, + { + "code": "441900108", + "name": "谢岗镇" + }, + { + "code": "441900109", + "name": "东坑镇" + }, + { + "code": "441900110", + "name": "常平镇" + }, + { + "code": "441900111", + "name": "寮步镇" + }, + { + "code": "441900112", + "name": "樟木头镇" + }, + { + "code": "441900113", + "name": "大朗镇" + }, + { + "code": "441900114", + "name": "黄江镇" + }, + { + "code": "441900115", + "name": "清溪镇" + }, + { + "code": "441900116", + "name": "塘厦镇" + }, + { + "code": "441900117", + "name": "凤岗镇" + }, + { + "code": "441900118", + "name": "大岭山镇" + }, + { + "code": "441900119", + "name": "长安镇" + }, + { + "code": "441900121", + "name": "虎门镇" + }, + { + "code": "441900122", + "name": "厚街镇" + }, + { + "code": "441900123", + "name": "沙田镇" + }, + { + "code": "441900124", + "name": "道滘镇" + }, + { + "code": "441900125", + "name": "洪梅镇" + }, + { + "code": "441900126", + "name": "麻涌镇" + }, + { + "code": "441900127", + "name": "望牛墩镇" + }, + { + "code": "441900128", + "name": "中堂镇" + }, + { + "code": "441900129", + "name": "高埗镇" + }, + { + "code": "441900401", + "name": "松山湖" + }, + { + "code": "441900402", + "name": "东莞港" + }, + { + "code": "441900403", + "name": "东莞生态园" + }, + { + "code": "441900404", + "name": "东莞滨海湾新区" + } + ] + }, + { + "code": "4420", + "name": "中山市", + "children": [ + { + "code": "442000001", + "name": "石岐街道" + }, + { + "code": "442000002", + "name": "东区街道" + }, + { + "code": "442000003", + "name": "中山港街道" + }, + { + "code": "442000004", + "name": "西区街道" + }, + { + "code": "442000005", + "name": "南区街道" + }, + { + "code": "442000006", + "name": "五桂山街道" + }, + { + "code": "442000007", + "name": "民众街道" + }, + { + "code": "442000008", + "name": "南朗街道" + }, + { + "code": "442000101", + "name": "黄圃镇" + }, + { + "code": "442000103", + "name": "东凤镇" + }, + { + "code": "442000105", + "name": "古镇镇" + }, + { + "code": "442000106", + "name": "沙溪镇" + }, + { + "code": "442000107", + "name": "坦洲镇" + }, + { + "code": "442000108", + "name": "港口镇" + }, + { + "code": "442000109", + "name": "三角镇" + }, + { + "code": "442000110", + "name": "横栏镇" + }, + { + "code": "442000111", + "name": "南头镇" + }, + { + "code": "442000112", + "name": "阜沙镇" + }, + { + "code": "442000114", + "name": "三乡镇" + }, + { + "code": "442000115", + "name": "板芙镇" + }, + { + "code": "442000116", + "name": "大涌镇" + }, + { + "code": "442000117", + "name": "神湾镇" + }, + { + "code": "442000118", + "name": "小榄镇" + } + ] + }, + { + "code": "4451", + "name": "潮州市", + "children": [ + { + "code": "445102", + "name": "湘桥区" + }, + { + "code": "445103", + "name": "潮安区" + }, + { + "code": "445122", + "name": "饶平县" + } + ] + }, + { + "code": "4452", + "name": "揭阳市", + "children": [ + { + "code": "445202", + "name": "榕城区" + }, + { + "code": "445203", + "name": "揭东区" + }, + { + "code": "445222", + "name": "揭西县" + }, + { + "code": "445224", + "name": "惠来县" + }, + { + "code": "445281", + "name": "普宁市" + } + ] + }, + { + "code": "4453", + "name": "云浮市", + "children": [ + { + "code": "445302", + "name": "云城区" + }, + { + "code": "445303", + "name": "云安区" + }, + { + "code": "445321", + "name": "新兴县" + }, + { + "code": "445322", + "name": "郁南县" + }, + { + "code": "445381", + "name": "罗定市" + } + ] + } + ] + }, + { + "code": "45", + "name": "广西壮族自治区", + "children": [ + { + "code": "4501", + "name": "南宁市", + "children": [ + { + "code": "450102", + "name": "兴宁区" + }, + { + "code": "450103", + "name": "青秀区" + }, + { + "code": "450105", + "name": "江南区" + }, + { + "code": "450107", + "name": "西乡塘区" + }, + { + "code": "450108", + "name": "良庆区" + }, + { + "code": "450109", + "name": "邕宁区" + }, + { + "code": "450110", + "name": "武鸣区" + }, + { + "code": "450123", + "name": "隆安县" + }, + { + "code": "450124", + "name": "马山县" + }, + { + "code": "450125", + "name": "上林县" + }, + { + "code": "450126", + "name": "宾阳县" + }, + { + "code": "450181", + "name": "横州市" + } + ] + }, + { + "code": "4502", + "name": "柳州市", + "children": [ + { + "code": "450202", + "name": "城中区" + }, + { + "code": "450203", + "name": "鱼峰区" + }, + { + "code": "450204", + "name": "柳南区" + }, + { + "code": "450205", + "name": "柳北区" + }, + { + "code": "450206", + "name": "柳江区" + }, + { + "code": "450222", + "name": "柳城县" + }, + { + "code": "450223", + "name": "鹿寨县" + }, + { + "code": "450224", + "name": "融安县" + }, + { + "code": "450225", + "name": "融水苗族自治县" + }, + { + "code": "450226", + "name": "三江侗族自治县" + } + ] + }, + { + "code": "4503", + "name": "桂林市", + "children": [ + { + "code": "450302", + "name": "秀峰区" + }, + { + "code": "450303", + "name": "叠彩区" + }, + { + "code": "450304", + "name": "象山区" + }, + { + "code": "450305", + "name": "七星区" + }, + { + "code": "450311", + "name": "雁山区" + }, + { + "code": "450312", + "name": "临桂区" + }, + { + "code": "450321", + "name": "阳朔县" + }, + { + "code": "450323", + "name": "灵川县" + }, + { + "code": "450324", + "name": "全州县" + }, + { + "code": "450325", + "name": "兴安县" + }, + { + "code": "450326", + "name": "永福县" + }, + { + "code": "450327", + "name": "灌阳县" + }, + { + "code": "450328", + "name": "龙胜各族自治县" + }, + { + "code": "450329", + "name": "资源县" + }, + { + "code": "450330", + "name": "平乐县" + }, + { + "code": "450332", + "name": "恭城瑶族自治县" + }, + { + "code": "450381", + "name": "荔浦市" + } + ] + }, + { + "code": "4504", + "name": "梧州市", + "children": [ + { + "code": "450403", + "name": "万秀区" + }, + { + "code": "450405", + "name": "长洲区" + }, + { + "code": "450406", + "name": "龙圩区" + }, + { + "code": "450421", + "name": "苍梧县" + }, + { + "code": "450422", + "name": "藤县" + }, + { + "code": "450423", + "name": "蒙山县" + }, + { + "code": "450481", + "name": "岑溪市" + } + ] + }, + { + "code": "4505", + "name": "北海市", + "children": [ + { + "code": "450502", + "name": "海城区" + }, + { + "code": "450503", + "name": "银海区" + }, + { + "code": "450512", + "name": "铁山港区" + }, + { + "code": "450521", + "name": "合浦县" + } + ] + }, + { + "code": "4506", + "name": "防城港市", + "children": [ + { + "code": "450602", + "name": "港口区" + }, + { + "code": "450603", + "name": "防城区" + }, + { + "code": "450621", + "name": "上思县" + }, + { + "code": "450681", + "name": "东兴市" + } + ] + }, + { + "code": "4507", + "name": "钦州市", + "children": [ + { + "code": "450702", + "name": "钦南区" + }, + { + "code": "450703", + "name": "钦北区" + }, + { + "code": "450721", + "name": "灵山县" + }, + { + "code": "450722", + "name": "浦北县" + } + ] + }, + { + "code": "4508", + "name": "贵港市", + "children": [ + { + "code": "450802", + "name": "港北区" + }, + { + "code": "450803", + "name": "港南区" + }, + { + "code": "450804", + "name": "覃塘区" + }, + { + "code": "450821", + "name": "平南县" + }, + { + "code": "450881", + "name": "桂平市" + } + ] + }, + { + "code": "4509", + "name": "玉林市", + "children": [ + { + "code": "450902", + "name": "玉州区" + }, + { + "code": "450903", + "name": "福绵区" + }, + { + "code": "450921", + "name": "容县" + }, + { + "code": "450922", + "name": "陆川县" + }, + { + "code": "450923", + "name": "博白县" + }, + { + "code": "450924", + "name": "兴业县" + }, + { + "code": "450981", + "name": "北流市" + } + ] + }, + { + "code": "4510", + "name": "百色市", + "children": [ + { + "code": "451002", + "name": "右江区" + }, + { + "code": "451003", + "name": "田阳区" + }, + { + "code": "451022", + "name": "田东县" + }, + { + "code": "451024", + "name": "德保县" + }, + { + "code": "451026", + "name": "那坡县" + }, + { + "code": "451027", + "name": "凌云县" + }, + { + "code": "451028", + "name": "乐业县" + }, + { + "code": "451029", + "name": "田林县" + }, + { + "code": "451030", + "name": "西林县" + }, + { + "code": "451031", + "name": "隆林各族自治县" + }, + { + "code": "451081", + "name": "靖西市" + }, + { + "code": "451082", + "name": "平果市" + } + ] + }, + { + "code": "4511", + "name": "贺州市", + "children": [ + { + "code": "451102", + "name": "八步区" + }, + { + "code": "451103", + "name": "平桂区" + }, + { + "code": "451121", + "name": "昭平县" + }, + { + "code": "451122", + "name": "钟山县" + }, + { + "code": "451123", + "name": "富川瑶族自治县" + } + ] + }, + { + "code": "4512", + "name": "河池市", + "children": [ + { + "code": "451202", + "name": "金城江区" + }, + { + "code": "451203", + "name": "宜州区" + }, + { + "code": "451221", + "name": "南丹县" + }, + { + "code": "451222", + "name": "天峨县" + }, + { + "code": "451223", + "name": "凤山县" + }, + { + "code": "451224", + "name": "东兰县" + }, + { + "code": "451225", + "name": "罗城仫佬族自治县" + }, + { + "code": "451226", + "name": "环江毛南族自治县" + }, + { + "code": "451227", + "name": "巴马瑶族自治县" + }, + { + "code": "451228", + "name": "都安瑶族自治县" + }, + { + "code": "451229", + "name": "大化瑶族自治县" + } + ] + }, + { + "code": "4513", + "name": "来宾市", + "children": [ + { + "code": "451302", + "name": "兴宾区" + }, + { + "code": "451321", + "name": "忻城县" + }, + { + "code": "451322", + "name": "象州县" + }, + { + "code": "451323", + "name": "武宣县" + }, + { + "code": "451324", + "name": "金秀瑶族自治县" + }, + { + "code": "451381", + "name": "合山市" + } + ] + }, + { + "code": "4514", + "name": "崇左市", + "children": [ + { + "code": "451402", + "name": "江州区" + }, + { + "code": "451421", + "name": "扶绥县" + }, + { + "code": "451422", + "name": "宁明县" + }, + { + "code": "451423", + "name": "龙州县" + }, + { + "code": "451424", + "name": "大新县" + }, + { + "code": "451425", + "name": "天等县" + }, + { + "code": "451481", + "name": "凭祥市" + } + ] + } + ] + }, + { + "code": "46", + "name": "海南省", + "children": [ + { + "code": "4601", + "name": "海口市", + "children": [ + { + "code": "460105", + "name": "秀英区" + }, + { + "code": "460106", + "name": "龙华区" + }, + { + "code": "460107", + "name": "琼山区" + }, + { + "code": "460108", + "name": "美兰区" + } + ] + }, + { + "code": "4602", + "name": "三亚市", + "children": [ + { + "code": "460202", + "name": "海棠区" + }, + { + "code": "460203", + "name": "吉阳区" + }, + { + "code": "460204", + "name": "天涯区" + }, + { + "code": "460205", + "name": "崖州区" + } + ] + }, + { + "code": "4603", + "name": "三沙市", + "children": [ + { + "code": "460321", + "name": "西沙群岛" + }, + { + "code": "460322", + "name": "南沙群岛" + }, + { + "code": "460323", + "name": "中沙群岛的岛礁及其海域" + } + ] + }, + { + "code": "4604", + "name": "儋州市", + "children": [ + { + "code": "460400100", + "name": "那大镇" + }, + { + "code": "460400101", + "name": "和庆镇" + }, + { + "code": "460400102", + "name": "南丰镇" + }, + { + "code": "460400103", + "name": "大成镇" + }, + { + "code": "460400104", + "name": "雅星镇" + }, + { + "code": "460400105", + "name": "兰洋镇" + }, + { + "code": "460400106", + "name": "光村镇" + }, + { + "code": "460400107", + "name": "木棠镇" + }, + { + "code": "460400108", + "name": "海头镇" + }, + { + "code": "460400109", + "name": "峨蔓镇" + }, + { + "code": "460400111", + "name": "王五镇" + }, + { + "code": "460400112", + "name": "白马井镇" + }, + { + "code": "460400113", + "name": "中和镇" + }, + { + "code": "460400114", + "name": "排浦镇" + }, + { + "code": "460400115", + "name": "东成镇" + }, + { + "code": "460400116", + "name": "新州镇" + }, + { + "code": "460400499", + "name": "洋浦经济开发区" + }, + { + "code": "460400500", + "name": "华南热作学院" + } + ] + }, + { + "code": "4690", + "name": "省直辖县级行政区划", + "children": [ + { + "code": "469001", + "name": "五指山市" + }, + { + "code": "469002", + "name": "琼海市" + }, + { + "code": "469005", + "name": "文昌市" + }, + { + "code": "469006", + "name": "万宁市" + }, + { + "code": "469007", + "name": "东方市" + }, + { + "code": "469021", + "name": "定安县" + }, + { + "code": "469022", + "name": "屯昌县" + }, + { + "code": "469023", + "name": "澄迈县" + }, + { + "code": "469024", + "name": "临高县" + }, + { + "code": "469025", + "name": "白沙黎族自治县" + }, + { + "code": "469026", + "name": "昌江黎族自治县" + }, + { + "code": "469027", + "name": "乐东黎族自治县" + }, + { + "code": "469028", + "name": "陵水黎族自治县" + }, + { + "code": "469029", + "name": "保亭黎族苗族自治县" + }, + { + "code": "469030", + "name": "琼中黎族苗族自治县" + } + ] + } + ] + }, + { + "code": "50", + "name": "重庆市", + "children": [ + { + "code": "5001", + "name": "市辖区", + "children": [ + { + "code": "500101", + "name": "万州区" + }, + { + "code": "500102", + "name": "涪陵区" + }, + { + "code": "500103", + "name": "渝中区" + }, + { + "code": "500104", + "name": "大渡口区" + }, + { + "code": "500105", + "name": "江北区" + }, + { + "code": "500106", + "name": "沙坪坝区" + }, + { + "code": "500107", + "name": "九龙坡区" + }, + { + "code": "500108", + "name": "南岸区" + }, + { + "code": "500109", + "name": "北碚区" + }, + { + "code": "500110", + "name": "綦江区" + }, + { + "code": "500111", + "name": "大足区" + }, + { + "code": "500112", + "name": "渝北区" + }, + { + "code": "500113", + "name": "巴南区" + }, + { + "code": "500114", + "name": "黔江区" + }, + { + "code": "500115", + "name": "长寿区" + }, + { + "code": "500116", + "name": "江津区" + }, + { + "code": "500117", + "name": "合川区" + }, + { + "code": "500118", + "name": "永川区" + }, + { + "code": "500119", + "name": "南川区" + }, + { + "code": "500120", + "name": "璧山区" + }, + { + "code": "500151", + "name": "铜梁区" + }, + { + "code": "500152", + "name": "潼南区" + }, + { + "code": "500153", + "name": "荣昌区" + }, + { + "code": "500154", + "name": "开州区" + }, + { + "code": "500155", + "name": "梁平区" + }, + { + "code": "500156", + "name": "武隆区" + } + ] + }, + { + "code": "5002", + "name": "县", + "children": [ + { + "code": "500229", + "name": "城口县" + }, + { + "code": "500230", + "name": "丰都县" + }, + { + "code": "500231", + "name": "垫江县" + }, + { + "code": "500233", + "name": "忠县" + }, + { + "code": "500235", + "name": "云阳县" + }, + { + "code": "500236", + "name": "奉节县" + }, + { + "code": "500237", + "name": "巫山县" + }, + { + "code": "500238", + "name": "巫溪县" + }, + { + "code": "500240", + "name": "石柱土家族自治县" + }, + { + "code": "500241", + "name": "秀山土家族苗族自治县" + }, + { + "code": "500242", + "name": "酉阳土家族苗族自治县" + }, + { + "code": "500243", + "name": "彭水苗族土家族自治县" + } + ] + } + ] + }, + { + "code": "51", + "name": "四川省", + "children": [ + { + "code": "5101", + "name": "成都市", + "children": [ + { + "code": "510104", + "name": "锦江区" + }, + { + "code": "510105", + "name": "青羊区" + }, + { + "code": "510106", + "name": "金牛区" + }, + { + "code": "510107", + "name": "武侯区" + }, + { + "code": "510108", + "name": "成华区" + }, + { + "code": "510112", + "name": "龙泉驿区" + }, + { + "code": "510113", + "name": "青白江区" + }, + { + "code": "510114", + "name": "新都区" + }, + { + "code": "510115", + "name": "温江区" + }, + { + "code": "510116", + "name": "双流区" + }, + { + "code": "510117", + "name": "郫都区" + }, + { + "code": "510118", + "name": "新津区" + }, + { + "code": "510121", + "name": "金堂县" + }, + { + "code": "510129", + "name": "大邑县" + }, + { + "code": "510131", + "name": "蒲江县" + }, + { + "code": "510181", + "name": "都江堰市" + }, + { + "code": "510182", + "name": "彭州市" + }, + { + "code": "510183", + "name": "邛崃市" + }, + { + "code": "510184", + "name": "崇州市" + }, + { + "code": "510185", + "name": "简阳市" + } + ] + }, + { + "code": "5103", + "name": "自贡市", + "children": [ + { + "code": "510302", + "name": "自流井区" + }, + { + "code": "510303", + "name": "贡井区" + }, + { + "code": "510304", + "name": "大安区" + }, + { + "code": "510311", + "name": "沿滩区" + }, + { + "code": "510321", + "name": "荣县" + }, + { + "code": "510322", + "name": "富顺县" + } + ] + }, + { + "code": "5104", + "name": "攀枝花市", + "children": [ + { + "code": "510402", + "name": "东区" + }, + { + "code": "510403", + "name": "西区" + }, + { + "code": "510411", + "name": "仁和区" + }, + { + "code": "510421", + "name": "米易县" + }, + { + "code": "510422", + "name": "盐边县" + } + ] + }, + { + "code": "5105", + "name": "泸州市", + "children": [ + { + "code": "510502", + "name": "江阳区" + }, + { + "code": "510503", + "name": "纳溪区" + }, + { + "code": "510504", + "name": "龙马潭区" + }, + { + "code": "510521", + "name": "泸县" + }, + { + "code": "510522", + "name": "合江县" + }, + { + "code": "510524", + "name": "叙永县" + }, + { + "code": "510525", + "name": "古蔺县" + } + ] + }, + { + "code": "5106", + "name": "德阳市", + "children": [ + { + "code": "510603", + "name": "旌阳区" + }, + { + "code": "510604", + "name": "罗江区" + }, + { + "code": "510623", + "name": "中江县" + }, + { + "code": "510681", + "name": "广汉市" + }, + { + "code": "510682", + "name": "什邡市" + }, + { + "code": "510683", + "name": "绵竹市" + } + ] + }, + { + "code": "5107", + "name": "绵阳市", + "children": [ + { + "code": "510703", + "name": "涪城区" + }, + { + "code": "510704", + "name": "游仙区" + }, + { + "code": "510705", + "name": "安州区" + }, + { + "code": "510722", + "name": "三台县" + }, + { + "code": "510723", + "name": "盐亭县" + }, + { + "code": "510725", + "name": "梓潼县" + }, + { + "code": "510726", + "name": "北川羌族自治县" + }, + { + "code": "510727", + "name": "平武县" + }, + { + "code": "510781", + "name": "江油市" + } + ] + }, + { + "code": "5108", + "name": "广元市", + "children": [ + { + "code": "510802", + "name": "利州区" + }, + { + "code": "510811", + "name": "昭化区" + }, + { + "code": "510812", + "name": "朝天区" + }, + { + "code": "510821", + "name": "旺苍县" + }, + { + "code": "510822", + "name": "青川县" + }, + { + "code": "510823", + "name": "剑阁县" + }, + { + "code": "510824", + "name": "苍溪县" + } + ] + }, + { + "code": "5109", + "name": "遂宁市", + "children": [ + { + "code": "510903", + "name": "船山区" + }, + { + "code": "510904", + "name": "安居区" + }, + { + "code": "510921", + "name": "蓬溪县" + }, + { + "code": "510923", + "name": "大英县" + }, + { + "code": "510981", + "name": "射洪市" + } + ] + }, + { + "code": "5110", + "name": "内江市", + "children": [ + { + "code": "511002", + "name": "市中区" + }, + { + "code": "511011", + "name": "东兴区" + }, + { + "code": "511024", + "name": "威远县" + }, + { + "code": "511025", + "name": "资中县" + }, + { + "code": "511083", + "name": "隆昌市" + } + ] + }, + { + "code": "5111", + "name": "乐山市", + "children": [ + { + "code": "511102", + "name": "市中区" + }, + { + "code": "511111", + "name": "沙湾区" + }, + { + "code": "511112", + "name": "五通桥区" + }, + { + "code": "511113", + "name": "金口河区" + }, + { + "code": "511123", + "name": "犍为县" + }, + { + "code": "511124", + "name": "井研县" + }, + { + "code": "511126", + "name": "夹江县" + }, + { + "code": "511129", + "name": "沐川县" + }, + { + "code": "511132", + "name": "峨边彝族自治县" + }, + { + "code": "511133", + "name": "马边彝族自治县" + }, + { + "code": "511181", + "name": "峨眉山市" + } + ] + }, + { + "code": "5113", + "name": "南充市", + "children": [ + { + "code": "511302", + "name": "顺庆区" + }, + { + "code": "511303", + "name": "高坪区" + }, + { + "code": "511304", + "name": "嘉陵区" + }, + { + "code": "511321", + "name": "南部县" + }, + { + "code": "511322", + "name": "营山县" + }, + { + "code": "511323", + "name": "蓬安县" + }, + { + "code": "511324", + "name": "仪陇县" + }, + { + "code": "511325", + "name": "西充县" + }, + { + "code": "511381", + "name": "阆中市" + } + ] + }, + { + "code": "5114", + "name": "眉山市", + "children": [ + { + "code": "511402", + "name": "东坡区" + }, + { + "code": "511403", + "name": "彭山区" + }, + { + "code": "511421", + "name": "仁寿县" + }, + { + "code": "511423", + "name": "洪雅县" + }, + { + "code": "511424", + "name": "丹棱县" + }, + { + "code": "511425", + "name": "青神县" + } + ] + }, + { + "code": "5115", + "name": "宜宾市", + "children": [ + { + "code": "511502", + "name": "翠屏区" + }, + { + "code": "511503", + "name": "南溪区" + }, + { + "code": "511504", + "name": "叙州区" + }, + { + "code": "511523", + "name": "江安县" + }, + { + "code": "511524", + "name": "长宁县" + }, + { + "code": "511525", + "name": "高县" + }, + { + "code": "511526", + "name": "珙县" + }, + { + "code": "511527", + "name": "筠连县" + }, + { + "code": "511528", + "name": "兴文县" + }, + { + "code": "511529", + "name": "屏山县" + } + ] + }, + { + "code": "5116", + "name": "广安市", + "children": [ + { + "code": "511602", + "name": "广安区" + }, + { + "code": "511603", + "name": "前锋区" + }, + { + "code": "511621", + "name": "岳池县" + }, + { + "code": "511622", + "name": "武胜县" + }, + { + "code": "511623", + "name": "邻水县" + }, + { + "code": "511681", + "name": "华蓥市" + } + ] + }, + { + "code": "5117", + "name": "达州市", + "children": [ + { + "code": "511702", + "name": "通川区" + }, + { + "code": "511703", + "name": "达川区" + }, + { + "code": "511722", + "name": "宣汉县" + }, + { + "code": "511723", + "name": "开江县" + }, + { + "code": "511724", + "name": "大竹县" + }, + { + "code": "511725", + "name": "渠县" + }, + { + "code": "511781", + "name": "万源市" + } + ] + }, + { + "code": "5118", + "name": "雅安市", + "children": [ + { + "code": "511802", + "name": "雨城区" + }, + { + "code": "511803", + "name": "名山区" + }, + { + "code": "511822", + "name": "荥经县" + }, + { + "code": "511823", + "name": "汉源县" + }, + { + "code": "511824", + "name": "石棉县" + }, + { + "code": "511825", + "name": "天全县" + }, + { + "code": "511826", + "name": "芦山县" + }, + { + "code": "511827", + "name": "宝兴县" + } + ] + }, + { + "code": "5119", + "name": "巴中市", + "children": [ + { + "code": "511902", + "name": "巴州区" + }, + { + "code": "511903", + "name": "恩阳区" + }, + { + "code": "511921", + "name": "通江县" + }, + { + "code": "511922", + "name": "南江县" + }, + { + "code": "511923", + "name": "平昌县" + } + ] + }, + { + "code": "5120", + "name": "资阳市", + "children": [ + { + "code": "512002", + "name": "雁江区" + }, + { + "code": "512021", + "name": "安岳县" + }, + { + "code": "512022", + "name": "乐至县" + } + ] + }, + { + "code": "5132", + "name": "阿坝藏族羌族自治州", + "children": [ + { + "code": "513201", + "name": "马尔康市" + }, + { + "code": "513221", + "name": "汶川县" + }, + { + "code": "513222", + "name": "理县" + }, + { + "code": "513223", + "name": "茂县" + }, + { + "code": "513224", + "name": "松潘县" + }, + { + "code": "513225", + "name": "九寨沟县" + }, + { + "code": "513226", + "name": "金川县" + }, + { + "code": "513227", + "name": "小金县" + }, + { + "code": "513228", + "name": "黑水县" + }, + { + "code": "513230", + "name": "壤塘县" + }, + { + "code": "513231", + "name": "阿坝县" + }, + { + "code": "513232", + "name": "若尔盖县" + }, + { + "code": "513233", + "name": "红原县" + } + ] + }, + { + "code": "5133", + "name": "甘孜藏族自治州", + "children": [ + { + "code": "513301", + "name": "康定市" + }, + { + "code": "513322", + "name": "泸定县" + }, + { + "code": "513323", + "name": "丹巴县" + }, + { + "code": "513324", + "name": "九龙县" + }, + { + "code": "513325", + "name": "雅江县" + }, + { + "code": "513326", + "name": "道孚县" + }, + { + "code": "513327", + "name": "炉霍县" + }, + { + "code": "513328", + "name": "甘孜县" + }, + { + "code": "513329", + "name": "新龙县" + }, + { + "code": "513330", + "name": "德格县" + }, + { + "code": "513331", + "name": "白玉县" + }, + { + "code": "513332", + "name": "石渠县" + }, + { + "code": "513333", + "name": "色达县" + }, + { + "code": "513334", + "name": "理塘县" + }, + { + "code": "513335", + "name": "巴塘县" + }, + { + "code": "513336", + "name": "乡城县" + }, + { + "code": "513337", + "name": "稻城县" + }, + { + "code": "513338", + "name": "得荣县" + } + ] + }, + { + "code": "5134", + "name": "凉山彝族自治州", + "children": [ + { + "code": "513401", + "name": "西昌市" + }, + { + "code": "513402", + "name": "会理市" + }, + { + "code": "513422", + "name": "木里藏族自治县" + }, + { + "code": "513423", + "name": "盐源县" + }, + { + "code": "513424", + "name": "德昌县" + }, + { + "code": "513426", + "name": "会东县" + }, + { + "code": "513427", + "name": "宁南县" + }, + { + "code": "513428", + "name": "普格县" + }, + { + "code": "513429", + "name": "布拖县" + }, + { + "code": "513430", + "name": "金阳县" + }, + { + "code": "513431", + "name": "昭觉县" + }, + { + "code": "513432", + "name": "喜德县" + }, + { + "code": "513433", + "name": "冕宁县" + }, + { + "code": "513434", + "name": "越西县" + }, + { + "code": "513435", + "name": "甘洛县" + }, + { + "code": "513436", + "name": "美姑县" + }, + { + "code": "513437", + "name": "雷波县" + } + ] + } + ] + }, + { + "code": "52", + "name": "贵州省", + "children": [ + { + "code": "5201", + "name": "贵阳市", + "children": [ + { + "code": "520102", + "name": "南明区" + }, + { + "code": "520103", + "name": "云岩区" + }, + { + "code": "520111", + "name": "花溪区" + }, + { + "code": "520112", + "name": "乌当区" + }, + { + "code": "520113", + "name": "白云区" + }, + { + "code": "520115", + "name": "观山湖区" + }, + { + "code": "520121", + "name": "开阳县" + }, + { + "code": "520122", + "name": "息烽县" + }, + { + "code": "520123", + "name": "修文县" + }, + { + "code": "520181", + "name": "清镇市" + } + ] + }, + { + "code": "5202", + "name": "六盘水市", + "children": [ + { + "code": "520201", + "name": "钟山区" + }, + { + "code": "520203", + "name": "六枝特区" + }, + { + "code": "520204", + "name": "水城区" + }, + { + "code": "520281", + "name": "盘州市" + } + ] + }, + { + "code": "5203", + "name": "遵义市", + "children": [ + { + "code": "520302", + "name": "红花岗区" + }, + { + "code": "520303", + "name": "汇川区" + }, + { + "code": "520304", + "name": "播州区" + }, + { + "code": "520322", + "name": "桐梓县" + }, + { + "code": "520323", + "name": "绥阳县" + }, + { + "code": "520324", + "name": "正安县" + }, + { + "code": "520325", + "name": "道真仡佬族苗族自治县" + }, + { + "code": "520326", + "name": "务川仡佬族苗族自治县" + }, + { + "code": "520327", + "name": "凤冈县" + }, + { + "code": "520328", + "name": "湄潭县" + }, + { + "code": "520329", + "name": "余庆县" + }, + { + "code": "520330", + "name": "习水县" + }, + { + "code": "520381", + "name": "赤水市" + }, + { + "code": "520382", + "name": "仁怀市" + } + ] + }, + { + "code": "5204", + "name": "安顺市", + "children": [ + { + "code": "520402", + "name": "西秀区" + }, + { + "code": "520403", + "name": "平坝区" + }, + { + "code": "520422", + "name": "普定县" + }, + { + "code": "520423", + "name": "镇宁布依族苗族自治县" + }, + { + "code": "520424", + "name": "关岭布依族苗族自治县" + }, + { + "code": "520425", + "name": "紫云苗族布依族自治县" + } + ] + }, + { + "code": "5205", + "name": "毕节市", + "children": [ + { + "code": "520502", + "name": "七星关区" + }, + { + "code": "520521", + "name": "大方县" + }, + { + "code": "520523", + "name": "金沙县" + }, + { + "code": "520524", + "name": "织金县" + }, + { + "code": "520525", + "name": "纳雍县" + }, + { + "code": "520526", + "name": "威宁彝族回族苗族自治县" + }, + { + "code": "520527", + "name": "赫章县" + }, + { + "code": "520581", + "name": "黔西市" + } + ] + }, + { + "code": "5206", + "name": "铜仁市", + "children": [ + { + "code": "520602", + "name": "碧江区" + }, + { + "code": "520603", + "name": "万山区" + }, + { + "code": "520621", + "name": "江口县" + }, + { + "code": "520622", + "name": "玉屏侗族自治县" + }, + { + "code": "520623", + "name": "石阡县" + }, + { + "code": "520624", + "name": "思南县" + }, + { + "code": "520625", + "name": "印江土家族苗族自治县" + }, + { + "code": "520626", + "name": "德江县" + }, + { + "code": "520627", + "name": "沿河土家族自治县" + }, + { + "code": "520628", + "name": "松桃苗族自治县" + } + ] + }, + { + "code": "5223", + "name": "黔西南布依族苗族自治州", + "children": [ + { + "code": "522301", + "name": "兴义市" + }, + { + "code": "522302", + "name": "兴仁市" + }, + { + "code": "522323", + "name": "普安县" + }, + { + "code": "522324", + "name": "晴隆县" + }, + { + "code": "522325", + "name": "贞丰县" + }, + { + "code": "522326", + "name": "望谟县" + }, + { + "code": "522327", + "name": "册亨县" + }, + { + "code": "522328", + "name": "安龙县" + } + ] + }, + { + "code": "5226", + "name": "黔东南苗族侗族自治州", + "children": [ + { + "code": "522601", + "name": "凯里市" + }, + { + "code": "522622", + "name": "黄平县" + }, + { + "code": "522623", + "name": "施秉县" + }, + { + "code": "522624", + "name": "三穗县" + }, + { + "code": "522625", + "name": "镇远县" + }, + { + "code": "522626", + "name": "岑巩县" + }, + { + "code": "522627", + "name": "天柱县" + }, + { + "code": "522628", + "name": "锦屏县" + }, + { + "code": "522629", + "name": "剑河县" + }, + { + "code": "522630", + "name": "台江县" + }, + { + "code": "522631", + "name": "黎平县" + }, + { + "code": "522632", + "name": "榕江县" + }, + { + "code": "522633", + "name": "从江县" + }, + { + "code": "522634", + "name": "雷山县" + }, + { + "code": "522635", + "name": "麻江县" + }, + { + "code": "522636", + "name": "丹寨县" + } + ] + }, + { + "code": "5227", + "name": "黔南布依族苗族自治州", + "children": [ + { + "code": "522701", + "name": "都匀市" + }, + { + "code": "522702", + "name": "福泉市" + }, + { + "code": "522722", + "name": "荔波县" + }, + { + "code": "522723", + "name": "贵定县" + }, + { + "code": "522725", + "name": "瓮安县" + }, + { + "code": "522726", + "name": "独山县" + }, + { + "code": "522727", + "name": "平塘县" + }, + { + "code": "522728", + "name": "罗甸县" + }, + { + "code": "522729", + "name": "长顺县" + }, + { + "code": "522730", + "name": "龙里县" + }, + { + "code": "522731", + "name": "惠水县" + }, + { + "code": "522732", + "name": "三都水族自治县" + } + ] + } + ] + }, + { + "code": "53", + "name": "云南省", + "children": [ + { + "code": "5301", + "name": "昆明市", + "children": [ + { + "code": "530102", + "name": "五华区" + }, + { + "code": "530103", + "name": "盘龙区" + }, + { + "code": "530111", + "name": "官渡区" + }, + { + "code": "530112", + "name": "西山区" + }, + { + "code": "530113", + "name": "东川区" + }, + { + "code": "530114", + "name": "呈贡区" + }, + { + "code": "530115", + "name": "晋宁区" + }, + { + "code": "530124", + "name": "富民县" + }, + { + "code": "530125", + "name": "宜良县" + }, + { + "code": "530126", + "name": "石林彝族自治县" + }, + { + "code": "530127", + "name": "嵩明县" + }, + { + "code": "530128", + "name": "禄劝彝族苗族自治县" + }, + { + "code": "530129", + "name": "寻甸回族彝族自治县" + }, + { + "code": "530181", + "name": "安宁市" + } + ] + }, + { + "code": "5303", + "name": "曲靖市", + "children": [ + { + "code": "530302", + "name": "麒麟区" + }, + { + "code": "530303", + "name": "沾益区" + }, + { + "code": "530304", + "name": "马龙区" + }, + { + "code": "530322", + "name": "陆良县" + }, + { + "code": "530323", + "name": "师宗县" + }, + { + "code": "530324", + "name": "罗平县" + }, + { + "code": "530325", + "name": "富源县" + }, + { + "code": "530326", + "name": "会泽县" + }, + { + "code": "530381", + "name": "宣威市" + } + ] + }, + { + "code": "5304", + "name": "玉溪市", + "children": [ + { + "code": "530402", + "name": "红塔区" + }, + { + "code": "530403", + "name": "江川区" + }, + { + "code": "530423", + "name": "通海县" + }, + { + "code": "530424", + "name": "华宁县" + }, + { + "code": "530425", + "name": "易门县" + }, + { + "code": "530426", + "name": "峨山彝族自治县" + }, + { + "code": "530427", + "name": "新平彝族傣族自治县" + }, + { + "code": "530428", + "name": "元江哈尼族彝族傣族自治县" + }, + { + "code": "530481", + "name": "澄江市" + } + ] + }, + { + "code": "5305", + "name": "保山市", + "children": [ + { + "code": "530502", + "name": "隆阳区" + }, + { + "code": "530521", + "name": "施甸县" + }, + { + "code": "530523", + "name": "龙陵县" + }, + { + "code": "530524", + "name": "昌宁县" + }, + { + "code": "530581", + "name": "腾冲市" + } + ] + }, + { + "code": "5306", + "name": "昭通市", + "children": [ + { + "code": "530602", + "name": "昭阳区" + }, + { + "code": "530621", + "name": "鲁甸县" + }, + { + "code": "530622", + "name": "巧家县" + }, + { + "code": "530623", + "name": "盐津县" + }, + { + "code": "530624", + "name": "大关县" + }, + { + "code": "530625", + "name": "永善县" + }, + { + "code": "530626", + "name": "绥江县" + }, + { + "code": "530627", + "name": "镇雄县" + }, + { + "code": "530628", + "name": "彝良县" + }, + { + "code": "530629", + "name": "威信县" + }, + { + "code": "530681", + "name": "水富市" + } + ] + }, + { + "code": "5307", + "name": "丽江市", + "children": [ + { + "code": "530702", + "name": "古城区" + }, + { + "code": "530721", + "name": "玉龙纳西族自治县" + }, + { + "code": "530722", + "name": "永胜县" + }, + { + "code": "530723", + "name": "华坪县" + }, + { + "code": "530724", + "name": "宁蒗彝族自治县" + } + ] + }, + { + "code": "5308", + "name": "普洱市", + "children": [ + { + "code": "530802", + "name": "思茅区" + }, + { + "code": "530821", + "name": "宁洱哈尼族彝族自治县" + }, + { + "code": "530822", + "name": "墨江哈尼族自治县" + }, + { + "code": "530823", + "name": "景东彝族自治县" + }, + { + "code": "530824", + "name": "景谷傣族彝族自治县" + }, + { + "code": "530825", + "name": "镇沅彝族哈尼族拉祜族自治县" + }, + { + "code": "530826", + "name": "江城哈尼族彝族自治县" + }, + { + "code": "530827", + "name": "孟连傣族拉祜族佤族自治县" + }, + { + "code": "530828", + "name": "澜沧拉祜族自治县" + }, + { + "code": "530829", + "name": "西盟佤族自治县" + } + ] + }, + { + "code": "5309", + "name": "临沧市", + "children": [ + { + "code": "530902", + "name": "临翔区" + }, + { + "code": "530921", + "name": "凤庆县" + }, + { + "code": "530922", + "name": "云县" + }, + { + "code": "530923", + "name": "永德县" + }, + { + "code": "530924", + "name": "镇康县" + }, + { + "code": "530925", + "name": "双江拉祜族佤族布朗族傣族自治县" + }, + { + "code": "530926", + "name": "耿马傣族佤族自治县" + }, + { + "code": "530927", + "name": "沧源佤族自治县" + } + ] + }, + { + "code": "5323", + "name": "楚雄彝族自治州", + "children": [ + { + "code": "532301", + "name": "楚雄市" + }, + { + "code": "532302", + "name": "禄丰市" + }, + { + "code": "532322", + "name": "双柏县" + }, + { + "code": "532323", + "name": "牟定县" + }, + { + "code": "532324", + "name": "南华县" + }, + { + "code": "532325", + "name": "姚安县" + }, + { + "code": "532326", + "name": "大姚县" + }, + { + "code": "532327", + "name": "永仁县" + }, + { + "code": "532328", + "name": "元谋县" + }, + { + "code": "532329", + "name": "武定县" + } + ] + }, + { + "code": "5325", + "name": "红河哈尼族彝族自治州", + "children": [ + { + "code": "532501", + "name": "个旧市" + }, + { + "code": "532502", + "name": "开远市" + }, + { + "code": "532503", + "name": "蒙自市" + }, + { + "code": "532504", + "name": "弥勒市" + }, + { + "code": "532523", + "name": "屏边苗族自治县" + }, + { + "code": "532524", + "name": "建水县" + }, + { + "code": "532525", + "name": "石屏县" + }, + { + "code": "532527", + "name": "泸西县" + }, + { + "code": "532528", + "name": "元阳县" + }, + { + "code": "532529", + "name": "红河县" + }, + { + "code": "532530", + "name": "金平苗族瑶族傣族自治县" + }, + { + "code": "532531", + "name": "绿春县" + }, + { + "code": "532532", + "name": "河口瑶族自治县" + } + ] + }, + { + "code": "5326", + "name": "文山壮族苗族自治州", + "children": [ + { + "code": "532601", + "name": "文山市" + }, + { + "code": "532622", + "name": "砚山县" + }, + { + "code": "532623", + "name": "西畴县" + }, + { + "code": "532624", + "name": "麻栗坡县" + }, + { + "code": "532625", + "name": "马关县" + }, + { + "code": "532626", + "name": "丘北县" + }, + { + "code": "532627", + "name": "广南县" + }, + { + "code": "532628", + "name": "富宁县" + } + ] + }, + { + "code": "5328", + "name": "西双版纳傣族自治州", + "children": [ + { + "code": "532801", + "name": "景洪市" + }, + { + "code": "532822", + "name": "勐海县" + }, + { + "code": "532823", + "name": "勐腊县" + } + ] + }, + { + "code": "5329", + "name": "大理白族自治州", + "children": [ + { + "code": "532901", + "name": "大理市" + }, + { + "code": "532922", + "name": "漾濞彝族自治县" + }, + { + "code": "532923", + "name": "祥云县" + }, + { + "code": "532924", + "name": "宾川县" + }, + { + "code": "532925", + "name": "弥渡县" + }, + { + "code": "532926", + "name": "南涧彝族自治县" + }, + { + "code": "532927", + "name": "巍山彝族回族自治县" + }, + { + "code": "532928", + "name": "永平县" + }, + { + "code": "532929", + "name": "云龙县" + }, + { + "code": "532930", + "name": "洱源县" + }, + { + "code": "532931", + "name": "剑川县" + }, + { + "code": "532932", + "name": "鹤庆县" + } + ] + }, + { + "code": "5331", + "name": "德宏傣族景颇族自治州", + "children": [ + { + "code": "533102", + "name": "瑞丽市" + }, + { + "code": "533103", + "name": "芒市" + }, + { + "code": "533122", + "name": "梁河县" + }, + { + "code": "533123", + "name": "盈江县" + }, + { + "code": "533124", + "name": "陇川县" + } + ] + }, + { + "code": "5333", + "name": "怒江傈僳族自治州", + "children": [ + { + "code": "533301", + "name": "泸水市" + }, + { + "code": "533323", + "name": "福贡县" + }, + { + "code": "533324", + "name": "贡山独龙族怒族自治县" + }, + { + "code": "533325", + "name": "兰坪白族普米族自治县" + } + ] + }, + { + "code": "5334", + "name": "迪庆藏族自治州", + "children": [ + { + "code": "533401", + "name": "香格里拉市" + }, + { + "code": "533422", + "name": "德钦县" + }, + { + "code": "533423", + "name": "维西傈僳族自治县" + } + ] + } + ] + }, + { + "code": "54", + "name": "西藏自治区", + "children": [ + { + "code": "5401", + "name": "拉萨市", + "children": [ + { + "code": "540102", + "name": "城关区" + }, + { + "code": "540103", + "name": "堆龙德庆区" + }, + { + "code": "540104", + "name": "达孜区" + }, + { + "code": "540121", + "name": "林周县" + }, + { + "code": "540122", + "name": "当雄县" + }, + { + "code": "540123", + "name": "尼木县" + }, + { + "code": "540124", + "name": "曲水县" + }, + { + "code": "540127", + "name": "墨竹工卡县" + }, + { + "code": "540171", + "name": "格尔木藏青工业园区" + }, + { + "code": "540172", + "name": "拉萨经济技术开发区" + }, + { + "code": "540173", + "name": "西藏文化旅游创意园区" + }, + { + "code": "540174", + "name": "达孜工业园区" + } + ] + }, + { + "code": "5402", + "name": "日喀则市", + "children": [ + { + "code": "540202", + "name": "桑珠孜区" + }, + { + "code": "540221", + "name": "南木林县" + }, + { + "code": "540222", + "name": "江孜县" + }, + { + "code": "540223", + "name": "定日县" + }, + { + "code": "540224", + "name": "萨迦县" + }, + { + "code": "540225", + "name": "拉孜县" + }, + { + "code": "540226", + "name": "昂仁县" + }, + { + "code": "540227", + "name": "谢通门县" + }, + { + "code": "540228", + "name": "白朗县" + }, + { + "code": "540229", + "name": "仁布县" + }, + { + "code": "540230", + "name": "康马县" + }, + { + "code": "540231", + "name": "定结县" + }, + { + "code": "540232", + "name": "仲巴县" + }, + { + "code": "540233", + "name": "亚东县" + }, + { + "code": "540234", + "name": "吉隆县" + }, + { + "code": "540235", + "name": "聂拉木县" + }, + { + "code": "540236", + "name": "萨嘎县" + }, + { + "code": "540237", + "name": "岗巴县" + } + ] + }, + { + "code": "5403", + "name": "昌都市", + "children": [ + { + "code": "540302", + "name": "卡若区" + }, + { + "code": "540321", + "name": "江达县" + }, + { + "code": "540322", + "name": "贡觉县" + }, + { + "code": "540323", + "name": "类乌齐县" + }, + { + "code": "540324", + "name": "丁青县" + }, + { + "code": "540325", + "name": "察雅县" + }, + { + "code": "540326", + "name": "八宿县" + }, + { + "code": "540327", + "name": "左贡县" + }, + { + "code": "540328", + "name": "芒康县" + }, + { + "code": "540329", + "name": "洛隆县" + }, + { + "code": "540330", + "name": "边坝县" + } + ] + }, + { + "code": "5404", + "name": "林芝市", + "children": [ + { + "code": "540402", + "name": "巴宜区" + }, + { + "code": "540421", + "name": "工布江达县" + }, + { + "code": "540423", + "name": "墨脱县" + }, + { + "code": "540424", + "name": "波密县" + }, + { + "code": "540425", + "name": "察隅县" + }, + { + "code": "540426", + "name": "朗县" + }, + { + "code": "540481", + "name": "米林市" + } + ] + }, + { + "code": "5405", + "name": "山南市", + "children": [ + { + "code": "540502", + "name": "乃东区" + }, + { + "code": "540521", + "name": "扎囊县" + }, + { + "code": "540522", + "name": "贡嘎县" + }, + { + "code": "540523", + "name": "桑日县" + }, + { + "code": "540524", + "name": "琼结县" + }, + { + "code": "540525", + "name": "曲松县" + }, + { + "code": "540526", + "name": "措美县" + }, + { + "code": "540527", + "name": "洛扎县" + }, + { + "code": "540528", + "name": "加查县" + }, + { + "code": "540529", + "name": "隆子县" + }, + { + "code": "540531", + "name": "浪卡子县" + }, + { + "code": "540581", + "name": "错那市" + } + ] + }, + { + "code": "5406", + "name": "那曲市", + "children": [ + { + "code": "540602", + "name": "色尼区" + }, + { + "code": "540621", + "name": "嘉黎县" + }, + { + "code": "540622", + "name": "比如县" + }, + { + "code": "540623", + "name": "聂荣县" + }, + { + "code": "540624", + "name": "安多县" + }, + { + "code": "540625", + "name": "申扎县" + }, + { + "code": "540626", + "name": "索县" + }, + { + "code": "540627", + "name": "班戈县" + }, + { + "code": "540628", + "name": "巴青县" + }, + { + "code": "540629", + "name": "尼玛县" + }, + { + "code": "540630", + "name": "双湖县" + } + ] + }, + { + "code": "5425", + "name": "阿里地区", + "children": [ + { + "code": "542521", + "name": "普兰县" + }, + { + "code": "542522", + "name": "札达县" + }, + { + "code": "542523", + "name": "噶尔县" + }, + { + "code": "542524", + "name": "日土县" + }, + { + "code": "542525", + "name": "革吉县" + }, + { + "code": "542526", + "name": "改则县" + }, + { + "code": "542527", + "name": "措勤县" + } + ] + } + ] + }, + { + "code": "61", + "name": "陕西省", + "children": [ + { + "code": "6101", + "name": "西安市", + "children": [ + { + "code": "610102", + "name": "新城区" + }, + { + "code": "610103", + "name": "碑林区" + }, + { + "code": "610104", + "name": "莲湖区" + }, + { + "code": "610111", + "name": "灞桥区" + }, + { + "code": "610112", + "name": "未央区" + }, + { + "code": "610113", + "name": "雁塔区" + }, + { + "code": "610114", + "name": "阎良区" + }, + { + "code": "610115", + "name": "临潼区" + }, + { + "code": "610116", + "name": "长安区" + }, + { + "code": "610117", + "name": "高陵区" + }, + { + "code": "610118", + "name": "鄠邑区" + }, + { + "code": "610122", + "name": "蓝田县" + }, + { + "code": "610124", + "name": "周至县" + } + ] + }, + { + "code": "6102", + "name": "铜川市", + "children": [ + { + "code": "610202", + "name": "王益区" + }, + { + "code": "610203", + "name": "印台区" + }, + { + "code": "610204", + "name": "耀州区" + }, + { + "code": "610222", + "name": "宜君县" + } + ] + }, + { + "code": "6103", + "name": "宝鸡市", + "children": [ + { + "code": "610302", + "name": "渭滨区" + }, + { + "code": "610303", + "name": "金台区" + }, + { + "code": "610304", + "name": "陈仓区" + }, + { + "code": "610305", + "name": "凤翔区" + }, + { + "code": "610323", + "name": "岐山县" + }, + { + "code": "610324", + "name": "扶风县" + }, + { + "code": "610326", + "name": "眉县" + }, + { + "code": "610327", + "name": "陇县" + }, + { + "code": "610328", + "name": "千阳县" + }, + { + "code": "610329", + "name": "麟游县" + }, + { + "code": "610330", + "name": "凤县" + }, + { + "code": "610331", + "name": "太白县" + } + ] + }, + { + "code": "6104", + "name": "咸阳市", + "children": [ + { + "code": "610402", + "name": "秦都区" + }, + { + "code": "610403", + "name": "杨陵区" + }, + { + "code": "610404", + "name": "渭城区" + }, + { + "code": "610422", + "name": "三原县" + }, + { + "code": "610423", + "name": "泾阳县" + }, + { + "code": "610424", + "name": "乾县" + }, + { + "code": "610425", + "name": "礼泉县" + }, + { + "code": "610426", + "name": "永寿县" + }, + { + "code": "610428", + "name": "长武县" + }, + { + "code": "610429", + "name": "旬邑县" + }, + { + "code": "610430", + "name": "淳化县" + }, + { + "code": "610431", + "name": "武功县" + }, + { + "code": "610481", + "name": "兴平市" + }, + { + "code": "610482", + "name": "彬州市" + } + ] + }, + { + "code": "6105", + "name": "渭南市", + "children": [ + { + "code": "610502", + "name": "临渭区" + }, + { + "code": "610503", + "name": "华州区" + }, + { + "code": "610522", + "name": "潼关县" + }, + { + "code": "610523", + "name": "大荔县" + }, + { + "code": "610524", + "name": "合阳县" + }, + { + "code": "610525", + "name": "澄城县" + }, + { + "code": "610526", + "name": "蒲城县" + }, + { + "code": "610527", + "name": "白水县" + }, + { + "code": "610528", + "name": "富平县" + }, + { + "code": "610581", + "name": "韩城市" + }, + { + "code": "610582", + "name": "华阴市" + } + ] + }, + { + "code": "6106", + "name": "延安市", + "children": [ + { + "code": "610602", + "name": "宝塔区" + }, + { + "code": "610603", + "name": "安塞区" + }, + { + "code": "610621", + "name": "延长县" + }, + { + "code": "610622", + "name": "延川县" + }, + { + "code": "610625", + "name": "志丹县" + }, + { + "code": "610626", + "name": "吴起县" + }, + { + "code": "610627", + "name": "甘泉县" + }, + { + "code": "610628", + "name": "富县" + }, + { + "code": "610629", + "name": "洛川县" + }, + { + "code": "610630", + "name": "宜川县" + }, + { + "code": "610631", + "name": "黄龙县" + }, + { + "code": "610632", + "name": "黄陵县" + }, + { + "code": "610681", + "name": "子长市" + } + ] + }, + { + "code": "6107", + "name": "汉中市", + "children": [ + { + "code": "610702", + "name": "汉台区" + }, + { + "code": "610703", + "name": "南郑区" + }, + { + "code": "610722", + "name": "城固县" + }, + { + "code": "610723", + "name": "洋县" + }, + { + "code": "610724", + "name": "西乡县" + }, + { + "code": "610725", + "name": "勉县" + }, + { + "code": "610726", + "name": "宁强县" + }, + { + "code": "610727", + "name": "略阳县" + }, + { + "code": "610728", + "name": "镇巴县" + }, + { + "code": "610729", + "name": "留坝县" + }, + { + "code": "610730", + "name": "佛坪县" + } + ] + }, + { + "code": "6108", + "name": "榆林市", + "children": [ + { + "code": "610802", + "name": "榆阳区" + }, + { + "code": "610803", + "name": "横山区" + }, + { + "code": "610822", + "name": "府谷县" + }, + { + "code": "610824", + "name": "靖边县" + }, + { + "code": "610825", + "name": "定边县" + }, + { + "code": "610826", + "name": "绥德县" + }, + { + "code": "610827", + "name": "米脂县" + }, + { + "code": "610828", + "name": "佳县" + }, + { + "code": "610829", + "name": "吴堡县" + }, + { + "code": "610830", + "name": "清涧县" + }, + { + "code": "610831", + "name": "子洲县" + }, + { + "code": "610881", + "name": "神木市" + } + ] + }, + { + "code": "6109", + "name": "安康市", + "children": [ + { + "code": "610902", + "name": "汉滨区" + }, + { + "code": "610921", + "name": "汉阴县" + }, + { + "code": "610922", + "name": "石泉县" + }, + { + "code": "610923", + "name": "宁陕县" + }, + { + "code": "610924", + "name": "紫阳县" + }, + { + "code": "610925", + "name": "岚皋县" + }, + { + "code": "610926", + "name": "平利县" + }, + { + "code": "610927", + "name": "镇坪县" + }, + { + "code": "610929", + "name": "白河县" + }, + { + "code": "610981", + "name": "旬阳市" + } + ] + }, + { + "code": "6110", + "name": "商洛市", + "children": [ + { + "code": "611002", + "name": "商州区" + }, + { + "code": "611021", + "name": "洛南县" + }, + { + "code": "611022", + "name": "丹凤县" + }, + { + "code": "611023", + "name": "商南县" + }, + { + "code": "611024", + "name": "山阳县" + }, + { + "code": "611025", + "name": "镇安县" + }, + { + "code": "611026", + "name": "柞水县" + } + ] + } + ] + }, + { + "code": "62", + "name": "甘肃省", + "children": [ + { + "code": "6201", + "name": "兰州市", + "children": [ + { + "code": "620102", + "name": "城关区" + }, + { + "code": "620103", + "name": "七里河区" + }, + { + "code": "620104", + "name": "西固区" + }, + { + "code": "620105", + "name": "安宁区" + }, + { + "code": "620111", + "name": "红古区" + }, + { + "code": "620121", + "name": "永登县" + }, + { + "code": "620122", + "name": "皋兰县" + }, + { + "code": "620123", + "name": "榆中县" + }, + { + "code": "620171", + "name": "兰州新区" + } + ] + }, + { + "code": "6202", + "name": "嘉峪关市", + "children": [ + { + "code": "620201001", + "name": "雄关街道" + }, + { + "code": "620201002", + "name": "钢城街道" + }, + { + "code": "620201100", + "name": "新城镇" + }, + { + "code": "620201101", + "name": "峪泉镇" + }, + { + "code": "620201102", + "name": "文殊镇" + } + ] + }, + { + "code": "6203", + "name": "金昌市", + "children": [ + { + "code": "620302", + "name": "金川区" + }, + { + "code": "620321", + "name": "永昌县" + } + ] + }, + { + "code": "6204", + "name": "白银市", + "children": [ + { + "code": "620402", + "name": "白银区" + }, + { + "code": "620403", + "name": "平川区" + }, + { + "code": "620421", + "name": "靖远县" + }, + { + "code": "620422", + "name": "会宁县" + }, + { + "code": "620423", + "name": "景泰县" + } + ] + }, + { + "code": "6205", + "name": "天水市", + "children": [ + { + "code": "620502", + "name": "秦州区" + }, + { + "code": "620503", + "name": "麦积区" + }, + { + "code": "620521", + "name": "清水县" + }, + { + "code": "620522", + "name": "秦安县" + }, + { + "code": "620523", + "name": "甘谷县" + }, + { + "code": "620524", + "name": "武山县" + }, + { + "code": "620525", + "name": "张家川回族自治县" + } + ] + }, + { + "code": "6206", + "name": "武威市", + "children": [ + { + "code": "620602", + "name": "凉州区" + }, + { + "code": "620621", + "name": "民勤县" + }, + { + "code": "620622", + "name": "古浪县" + }, + { + "code": "620623", + "name": "天祝藏族自治县" + } + ] + }, + { + "code": "6207", + "name": "张掖市", + "children": [ + { + "code": "620702", + "name": "甘州区" + }, + { + "code": "620721", + "name": "肃南裕固族自治县" + }, + { + "code": "620722", + "name": "民乐县" + }, + { + "code": "620723", + "name": "临泽县" + }, + { + "code": "620724", + "name": "高台县" + }, + { + "code": "620725", + "name": "山丹县" + } + ] + }, + { + "code": "6208", + "name": "平凉市", + "children": [ + { + "code": "620802", + "name": "崆峒区" + }, + { + "code": "620821", + "name": "泾川县" + }, + { + "code": "620822", + "name": "灵台县" + }, + { + "code": "620823", + "name": "崇信县" + }, + { + "code": "620825", + "name": "庄浪县" + }, + { + "code": "620826", + "name": "静宁县" + }, + { + "code": "620881", + "name": "华亭市" + } + ] + }, + { + "code": "6209", + "name": "酒泉市", + "children": [ + { + "code": "620902", + "name": "肃州区" + }, + { + "code": "620921", + "name": "金塔县" + }, + { + "code": "620922", + "name": "瓜州县" + }, + { + "code": "620923", + "name": "肃北蒙古族自治县" + }, + { + "code": "620924", + "name": "阿克塞哈萨克族自治县" + }, + { + "code": "620981", + "name": "玉门市" + }, + { + "code": "620982", + "name": "敦煌市" + } + ] + }, + { + "code": "6210", + "name": "庆阳市", + "children": [ + { + "code": "621002", + "name": "西峰区" + }, + { + "code": "621021", + "name": "庆城县" + }, + { + "code": "621022", + "name": "环县" + }, + { + "code": "621023", + "name": "华池县" + }, + { + "code": "621024", + "name": "合水县" + }, + { + "code": "621025", + "name": "正宁县" + }, + { + "code": "621026", + "name": "宁县" + }, + { + "code": "621027", + "name": "镇原县" + } + ] + }, + { + "code": "6211", + "name": "定西市", + "children": [ + { + "code": "621102", + "name": "安定区" + }, + { + "code": "621121", + "name": "通渭县" + }, + { + "code": "621122", + "name": "陇西县" + }, + { + "code": "621123", + "name": "渭源县" + }, + { + "code": "621124", + "name": "临洮县" + }, + { + "code": "621125", + "name": "漳县" + }, + { + "code": "621126", + "name": "岷县" + } + ] + }, + { + "code": "6212", + "name": "陇南市", + "children": [ + { + "code": "621202", + "name": "武都区" + }, + { + "code": "621221", + "name": "成县" + }, + { + "code": "621222", + "name": "文县" + }, + { + "code": "621223", + "name": "宕昌县" + }, + { + "code": "621224", + "name": "康县" + }, + { + "code": "621225", + "name": "西和县" + }, + { + "code": "621226", + "name": "礼县" + }, + { + "code": "621227", + "name": "徽县" + }, + { + "code": "621228", + "name": "两当县" + } + ] + }, + { + "code": "6229", + "name": "临夏回族自治州", + "children": [ + { + "code": "622901", + "name": "临夏市" + }, + { + "code": "622921", + "name": "临夏县" + }, + { + "code": "622922", + "name": "康乐县" + }, + { + "code": "622923", + "name": "永靖县" + }, + { + "code": "622924", + "name": "广河县" + }, + { + "code": "622925", + "name": "和政县" + }, + { + "code": "622926", + "name": "东乡族自治县" + }, + { + "code": "622927", + "name": "积石山保安族东乡族撒拉族自治县" + } + ] + }, + { + "code": "6230", + "name": "甘南藏族自治州", + "children": [ + { + "code": "623001", + "name": "合作市" + }, + { + "code": "623021", + "name": "临潭县" + }, + { + "code": "623022", + "name": "卓尼县" + }, + { + "code": "623023", + "name": "舟曲县" + }, + { + "code": "623024", + "name": "迭部县" + }, + { + "code": "623025", + "name": "玛曲县" + }, + { + "code": "623026", + "name": "碌曲县" + }, + { + "code": "623027", + "name": "夏河县" + } + ] + } + ] + }, + { + "code": "63", + "name": "青海省", + "children": [ + { + "code": "6301", + "name": "西宁市", + "children": [ + { + "code": "630102", + "name": "城东区" + }, + { + "code": "630103", + "name": "城中区" + }, + { + "code": "630104", + "name": "城西区" + }, + { + "code": "630105", + "name": "城北区" + }, + { + "code": "630106", + "name": "湟中区" + }, + { + "code": "630121", + "name": "大通回族土族自治县" + }, + { + "code": "630123", + "name": "湟源县" + } + ] + }, + { + "code": "6302", + "name": "海东市", + "children": [ + { + "code": "630202", + "name": "乐都区" + }, + { + "code": "630203", + "name": "平安区" + }, + { + "code": "630222", + "name": "民和回族土族自治县" + }, + { + "code": "630223", + "name": "互助土族自治县" + }, + { + "code": "630224", + "name": "化隆回族自治县" + }, + { + "code": "630225", + "name": "循化撒拉族自治县" + } + ] + }, + { + "code": "6322", + "name": "海北藏族自治州", + "children": [ + { + "code": "632221", + "name": "门源回族自治县" + }, + { + "code": "632222", + "name": "祁连县" + }, + { + "code": "632223", + "name": "海晏县" + }, + { + "code": "632224", + "name": "刚察县" + } + ] + }, + { + "code": "6323", + "name": "黄南藏族自治州", + "children": [ + { + "code": "632301", + "name": "同仁市" + }, + { + "code": "632322", + "name": "尖扎县" + }, + { + "code": "632323", + "name": "泽库县" + }, + { + "code": "632324", + "name": "河南蒙古族自治县" + } + ] + }, + { + "code": "6325", + "name": "海南藏族自治州", + "children": [ + { + "code": "632521", + "name": "共和县" + }, + { + "code": "632522", + "name": "同德县" + }, + { + "code": "632523", + "name": "贵德县" + }, + { + "code": "632524", + "name": "兴海县" + }, + { + "code": "632525", + "name": "贵南县" + } + ] + }, + { + "code": "6326", + "name": "果洛藏族自治州", + "children": [ + { + "code": "632621", + "name": "玛沁县" + }, + { + "code": "632622", + "name": "班玛县" + }, + { + "code": "632623", + "name": "甘德县" + }, + { + "code": "632624", + "name": "达日县" + }, + { + "code": "632625", + "name": "久治县" + }, + { + "code": "632626", + "name": "玛多县" + } + ] + }, + { + "code": "6327", + "name": "玉树藏族自治州", + "children": [ + { + "code": "632701", + "name": "玉树市" + }, + { + "code": "632722", + "name": "杂多县" + }, + { + "code": "632723", + "name": "称多县" + }, + { + "code": "632724", + "name": "治多县" + }, + { + "code": "632725", + "name": "囊谦县" + }, + { + "code": "632726", + "name": "曲麻莱县" + } + ] + }, + { + "code": "6328", + "name": "海西蒙古族藏族自治州", + "children": [ + { + "code": "632801", + "name": "格尔木市" + }, + { + "code": "632802", + "name": "德令哈市" + }, + { + "code": "632803", + "name": "茫崖市" + }, + { + "code": "632821", + "name": "乌兰县" + }, + { + "code": "632822", + "name": "都兰县" + }, + { + "code": "632823", + "name": "天峻县" + }, + { + "code": "632857", + "name": "大柴旦行政委员会" + } + ] + } + ] + }, + { + "code": "64", + "name": "宁夏回族自治区", + "children": [ + { + "code": "6401", + "name": "银川市", + "children": [ + { + "code": "640104", + "name": "兴庆区" + }, + { + "code": "640105", + "name": "西夏区" + }, + { + "code": "640106", + "name": "金凤区" + }, + { + "code": "640121", + "name": "永宁县" + }, + { + "code": "640122", + "name": "贺兰县" + }, + { + "code": "640181", + "name": "灵武市" + } + ] + }, + { + "code": "6402", + "name": "石嘴山市", + "children": [ + { + "code": "640202", + "name": "大武口区" + }, + { + "code": "640205", + "name": "惠农区" + }, + { + "code": "640221", + "name": "平罗县" + } + ] + }, + { + "code": "6403", + "name": "吴忠市", + "children": [ + { + "code": "640302", + "name": "利通区" + }, + { + "code": "640303", + "name": "红寺堡区" + }, + { + "code": "640323", + "name": "盐池县" + }, + { + "code": "640324", + "name": "同心县" + }, + { + "code": "640381", + "name": "青铜峡市" + } + ] + }, + { + "code": "6404", + "name": "固原市", + "children": [ + { + "code": "640402", + "name": "原州区" + }, + { + "code": "640422", + "name": "西吉县" + }, + { + "code": "640423", + "name": "隆德县" + }, + { + "code": "640424", + "name": "泾源县" + }, + { + "code": "640425", + "name": "彭阳县" + } + ] + }, + { + "code": "6405", + "name": "中卫市", + "children": [ + { + "code": "640502", + "name": "沙坡头区" + }, + { + "code": "640521", + "name": "中宁县" + }, + { + "code": "640522", + "name": "海原县" + } + ] + } + ] + }, + { + "code": "65", + "name": "新疆维吾尔自治区", + "children": [ + { + "code": "6501", + "name": "乌鲁木齐市", + "children": [ + { + "code": "650102", + "name": "天山区" + }, + { + "code": "650103", + "name": "沙依巴克区" + }, + { + "code": "650104", + "name": "新市区" + }, + { + "code": "650105", + "name": "水磨沟区" + }, + { + "code": "650106", + "name": "头屯河区" + }, + { + "code": "650107", + "name": "达坂城区" + }, + { + "code": "650109", + "name": "米东区" + }, + { + "code": "650121", + "name": "乌鲁木齐县" + } + ] + }, + { + "code": "6502", + "name": "克拉玛依市", + "children": [ + { + "code": "650202", + "name": "独山子区" + }, + { + "code": "650203", + "name": "克拉玛依区" + }, + { + "code": "650204", + "name": "白碱滩区" + }, + { + "code": "650205", + "name": "乌尔禾区" + } + ] + }, + { + "code": "6504", + "name": "吐鲁番市", + "children": [ + { + "code": "650402", + "name": "高昌区" + }, + { + "code": "650421", + "name": "鄯善县" + }, + { + "code": "650422", + "name": "托克逊县" + } + ] + }, + { + "code": "6505", + "name": "哈密市", + "children": [ + { + "code": "650502", + "name": "伊州区" + }, + { + "code": "650521", + "name": "巴里坤哈萨克自治县" + }, + { + "code": "650522", + "name": "伊吾县" + } + ] + }, + { + "code": "6523", + "name": "昌吉回族自治州", + "children": [ + { + "code": "652301", + "name": "昌吉市" + }, + { + "code": "652302", + "name": "阜康市" + }, + { + "code": "652323", + "name": "呼图壁县" + }, + { + "code": "652324", + "name": "玛纳斯县" + }, + { + "code": "652325", + "name": "奇台县" + }, + { + "code": "652327", + "name": "吉木萨尔县" + }, + { + "code": "652328", + "name": "木垒哈萨克自治县" + } + ] + }, + { + "code": "6527", + "name": "博尔塔拉蒙古自治州", + "children": [ + { + "code": "652701", + "name": "博乐市" + }, + { + "code": "652702", + "name": "阿拉山口市" + }, + { + "code": "652722", + "name": "精河县" + }, + { + "code": "652723", + "name": "温泉县" + } + ] + }, + { + "code": "6528", + "name": "巴音郭楞蒙古自治州", + "children": [ + { + "code": "652801", + "name": "库尔勒市" + }, + { + "code": "652822", + "name": "轮台县" + }, + { + "code": "652823", + "name": "尉犁县" + }, + { + "code": "652824", + "name": "若羌县" + }, + { + "code": "652825", + "name": "且末县" + }, + { + "code": "652826", + "name": "焉耆回族自治县" + }, + { + "code": "652827", + "name": "和静县" + }, + { + "code": "652828", + "name": "和硕县" + }, + { + "code": "652829", + "name": "博湖县" + } + ] + }, + { + "code": "6529", + "name": "阿克苏地区", + "children": [ + { + "code": "652901", + "name": "阿克苏市" + }, + { + "code": "652902", + "name": "库车市" + }, + { + "code": "652922", + "name": "温宿县" + }, + { + "code": "652924", + "name": "沙雅县" + }, + { + "code": "652925", + "name": "新和县" + }, + { + "code": "652926", + "name": "拜城县" + }, + { + "code": "652927", + "name": "乌什县" + }, + { + "code": "652928", + "name": "阿瓦提县" + }, + { + "code": "652929", + "name": "柯坪县" + } + ] + }, + { + "code": "6530", + "name": "克孜勒苏柯尔克孜自治州", + "children": [ + { + "code": "653001", + "name": "阿图什市" + }, + { + "code": "653022", + "name": "阿克陶县" + }, + { + "code": "653023", + "name": "阿合奇县" + }, + { + "code": "653024", + "name": "乌恰县" + } + ] + }, + { + "code": "6531", + "name": "喀什地区", + "children": [ + { + "code": "653101", + "name": "喀什市" + }, + { + "code": "653121", + "name": "疏附县" + }, + { + "code": "653122", + "name": "疏勒县" + }, + { + "code": "653123", + "name": "英吉沙县" + }, + { + "code": "653124", + "name": "泽普县" + }, + { + "code": "653125", + "name": "莎车县" + }, + { + "code": "653126", + "name": "叶城县" + }, + { + "code": "653127", + "name": "麦盖提县" + }, + { + "code": "653128", + "name": "岳普湖县" + }, + { + "code": "653129", + "name": "伽师县" + }, + { + "code": "653130", + "name": "巴楚县" + }, + { + "code": "653131", + "name": "塔什库尔干塔吉克自治县" + } + ] + }, + { + "code": "6532", + "name": "和田地区", + "children": [ + { + "code": "653201", + "name": "和田市" + }, + { + "code": "653221", + "name": "和田县" + }, + { + "code": "653222", + "name": "墨玉县" + }, + { + "code": "653223", + "name": "皮山县" + }, + { + "code": "653224", + "name": "洛浦县" + }, + { + "code": "653225", + "name": "策勒县" + }, + { + "code": "653226", + "name": "于田县" + }, + { + "code": "653227", + "name": "民丰县" + } + ] + }, + { + "code": "6540", + "name": "伊犁哈萨克自治州", + "children": [ + { + "code": "654002", + "name": "伊宁市" + }, + { + "code": "654003", + "name": "奎屯市" + }, + { + "code": "654004", + "name": "霍尔果斯市" + }, + { + "code": "654021", + "name": "伊宁县" + }, + { + "code": "654022", + "name": "察布查尔锡伯自治县" + }, + { + "code": "654023", + "name": "霍城县" + }, + { + "code": "654024", + "name": "巩留县" + }, + { + "code": "654025", + "name": "新源县" + }, + { + "code": "654026", + "name": "昭苏县" + }, + { + "code": "654027", + "name": "特克斯县" + }, + { + "code": "654028", + "name": "尼勒克县" + } + ] + }, + { + "code": "6542", + "name": "塔城地区", + "children": [ + { + "code": "654201", + "name": "塔城市" + }, + { + "code": "654202", + "name": "乌苏市" + }, + { + "code": "654203", + "name": "沙湾市" + }, + { + "code": "654221", + "name": "额敏县" + }, + { + "code": "654224", + "name": "托里县" + }, + { + "code": "654225", + "name": "裕民县" + }, + { + "code": "654226", + "name": "和布克赛尔蒙古自治县" + } + ] + }, + { + "code": "6543", + "name": "阿勒泰地区", + "children": [ + { + "code": "654301", + "name": "阿勒泰市" + }, + { + "code": "654321", + "name": "布尔津县" + }, + { + "code": "654322", + "name": "富蕴县" + }, + { + "code": "654323", + "name": "福海县" + }, + { + "code": "654324", + "name": "哈巴河县" + }, + { + "code": "654325", + "name": "青河县" + }, + { + "code": "654326", + "name": "吉木乃县" + } + ] + }, + { + "code": "6590", + "name": "自治区直辖县级行政区划", + "children": [ + { + "code": "659001", + "name": "石河子市" + }, + { + "code": "659002", + "name": "阿拉尔市" + }, + { + "code": "659003", + "name": "图木舒克市" + }, + { + "code": "659004", + "name": "五家渠市" + }, + { + "code": "659005", + "name": "北屯市" + }, + { + "code": "659006", + "name": "铁门关市" + }, + { + "code": "659007", + "name": "双河市" + }, + { + "code": "659008", + "name": "可克达拉市" + }, + { + "code": "659009", + "name": "昆玉市" + }, + { + "code": "659010", + "name": "胡杨河市" + }, + { + "code": "659011", + "name": "新星市" + }, + { + "code": "659012", + "name": "白杨市" + } + ] + } + ] + }, + { + "code": "81", + "name": "香港特别行政区", + "children": [ + { + "code": "8101", + "name": "香港岛", + "children": [ + {"code": "810101", "name": "中西区"}, + {"code": "810102", "name": "湾仔区"}, + {"code": "810103", "name": "东区"}, + {"code": "810104", "name": "南区"} + ] + }, + { + "code": "8102", + "name": "九龙", + "children": [ + {"code": "810201", "name": "油尖旺区"}, + {"code": "810202", "name": "深水埗区"}, + {"code": "810203", "name": "九龙城区"}, + {"code": "810204", "name": "黄大仙区"}, + {"code": "810205", "name": "观塘区"} + ] + }, + { + "code": "8103", + "name": "新界", + "children": [ + {"code": "810301", "name": "北区"}, + {"code": "810302", "name": "大埔区"}, + {"code": "810303", "name": "沙田区"}, + {"code": "810304", "name": "西贡区"}, + {"code": "810305", "name": "元朗区"}, + {"code": "810306", "name": "屯门区"}, + {"code": "810307", "name": "荃湾区"}, + {"code": "810308", "name": "葵青区"}, + {"code": "810309", "name": "离岛区"} + ] + } + ] + }, + { + "code": "82", + "name": "澳门特别行政区", + "children": [ + { + "code": "8201", + "name": "澳门半岛", + "children": [ + {"code": "820101", "name": "花地玛堂区"}, + {"code": "820102", "name": "圣安多尼堂区"}, + {"code": "820103", "name": "大堂区"}, + {"code": "820104", "name": "望德堂区"}, + {"code": "820105", "name": "风顺堂区"} + ] + }, + { + "code": "8202", + "name": "离岛", + "children": [ + {"code": "820201", "name": "嘉模堂区"}, + {"code": "820202", "name": "圣方济各堂区"} + ] + } + ] + }, + { + "code": "71", + "name": "台湾省", + "children": [ + { + "code": "7101", + "name": "台北市", + "children": [ + {"code": "710101", "name": "中正区"}, + {"code": "710102", "name": "大同区"}, + {"code": "710103", "name": "中山区"}, + {"code": "710104", "name": "松山区"}, + {"code": "710105", "name": "大安区"}, + {"code": "710106", "name": "万华区"}, + {"code": "710107", "name": "信义区"}, + {"code": "710108", "name": "士林区"}, + {"code": "710109", "name": "北投区"}, + {"code": "710110", "name": "内湖区"}, + {"code": "710111", "name": "南港区"}, + {"code": "710112", "name": "文山区"} + ] + }, + { + "code": "7102", + "name": "高雄市", + "children": [ + {"code": "710201", "name": "新兴区"}, + {"code": "710202", "name": "前金区"}, + {"code": "710203", "name": "苓雅区"}, + {"code": "710204", "name": "盐埕区"}, + {"code": "710205", "name": "鼓山区"}, + {"code": "710206", "name": "旗津区"}, + {"code": "710207", "name": "前镇区"}, + {"code": "710208", "name": "三民区"}, + {"code": "710209", "name": "左营区"}, + {"code": "710210", "name": "楠梓区"} + ] + }, + { + "code": "7103", + "name": "台中市", + "children": [ + {"code": "710301", "name": "中区"}, + {"code": "710302", "name": "东区"}, + {"code": "710303", "name": "南区"}, + {"code": "710304", "name": "西区"}, + {"code": "710305", "name": "北区"}, + {"code": "710306", "name": "北屯区"}, + {"code": "710307", "name": "西屯区"}, + {"code": "710308", "name": "南屯区"} + ] + }, + { + "code": "7104", + "name": "台南市", + "children": [ + {"code": "710401", "name": "中西区"}, + {"code": "710402", "name": "东区"}, + {"code": "710403", "name": "南区"}, + {"code": "710404", "name": "北区"}, + {"code": "710405", "name": "安平区"}, + {"code": "710406", "name": "安南区"} + ] + }, + { + "code": "7105", + "name": "新北市", + "children": [ + {"code": "710501", "name": "万里区"}, + {"code": "710502", "name": "金山区"}, + {"code": "710503", "name": "板桥区"}, + {"code": "710504", "name": "汐止区"}, + {"code": "710505", "name": "深坑区"}, + {"code": "710506", "name": "石碇区"}, + {"code": "710507", "name": "瑞芳区"}, + {"code": "710508", "name": "平溪区"} + ] + }, + { + "code": "7106", + "name": "桃园市", + "children": [ + {"code": "710601", "name": "中坜区"}, + {"code": "710602", "name": "平镇区"}, + {"code": "710603", "name": "龙潭区"}, + {"code": "710604", "name": "杨梅区"}, + {"code": "710605", "name": "新屋区"}, + {"code": "710606", "name": "观音区"}, + {"code": "710607", "name": "桃园区"}, + {"code": "710608", "name": "龟山区"} + ] + } + ] + } +] \ No newline at end of file diff --git a/scripts/verify_data.js b/scripts/verify_data.js new file mode 100644 index 0000000..470682a --- /dev/null +++ b/scripts/verify_data.js @@ -0,0 +1,37 @@ +const { initDB, getDB } = require('../database'); + +async function verifyData() { + try { + await initDB(); + + // 检查省份数据 + const [provinces] = await getDB().query('SELECT code, name FROM china_regions WHERE level = 1 ORDER BY code LIMIT 10'); + console.log('省份数据样本:'); + provinces.forEach(p => console.log(` ${p.code} - ${p.name}`)); + + // 检查城市数据 + const [cities] = await getDB().query('SELECT code, name, parent_code FROM china_regions WHERE level = 2 ORDER BY code LIMIT 10'); + console.log('\n城市数据样本:'); + cities.forEach(c => console.log(` ${c.code} - ${c.name} (${c.parent_code})`)); + + // 检查区县数据 + const [districts] = await getDB().query('SELECT code, name, parent_code FROM china_regions WHERE level = 3 ORDER BY code LIMIT 10'); + console.log('\n区县数据样本:'); + districts.forEach(d => console.log(` ${d.code} - ${d.name} (${d.parent_code})`)); + + // 统计各级别数量 + const [stats] = await getDB().query('SELECT level, COUNT(*) as count FROM china_regions GROUP BY level ORDER BY level'); + console.log('\n各级别统计:'); + stats.forEach(row => { + const levelName = row.level === 1 ? '省份' : row.level === 2 ? '城市' : '区县'; + console.log(` ${levelName}(level ${row.level}): ${row.count} 个`); + }); + + } catch (error) { + console.error('验证失败:', error); + } finally { + process.exit(); + } +} + +verifyData(); \ No newline at end of file diff --git a/server.js b/server.js index f7dcc7a..adddadf 100644 --- a/server.js +++ b/server.js @@ -39,6 +39,7 @@ app.use(helmet({ app.use(cors({ origin: [ 'http://localhost:5173', + 'http://localhost:5176', 'http://localhost:5174', 'http://localhost:3001', 'https://www.zrbjr.com', @@ -59,14 +60,18 @@ app.use((req, res, next) => { res.on('finish', () => { const duration = Date.now() - start; - logger.info('HTTP Request', { - method: req.method, - url: req.originalUrl, - statusCode: res.statusCode, - duration: `${duration}ms`, - ip: req.ip, - userAgent: req.get('User-Agent') - }); + + // 只记录非正常状态码的请求日志(过滤掉200、304等正常返回) + if (res.statusCode >= 400 || res.statusCode < 200) { + logger.info('HTTP Request', { + method: req.method, + url: req.originalUrl, + statusCode: res.statusCode, + duration: `${duration}ms`, + ip: req.ip, + userAgent: req.get('User-Agent') + }); + } }); next(); @@ -225,6 +230,7 @@ app.use('/api/auth', require('./routes/auth')); app.use('/api/users', require('./routes/users')); app.use('/api/user', require('./routes/users')); // 添加单数形式的路由映射 app.use('/api/products', require('./routes/products')); +app.use('/api/specifications', require('./routes/specifications')); app.use('/api/orders', require('./routes/orders')); app.use('/api/points', require('./routes/points')); app.use('/api/captcha', require('./routes/captcha')); // 验证码路由 @@ -243,6 +249,7 @@ app.use('/api/agent-withdrawals', require('./routes/agent-withdrawals')); app.use('/api/regions', require('./routes/regions')); app.use('/api/addresses', require('./routes/addresses')); app.use('/api/address-labels', require('./routes/address-labels')); +app.use('/api/cart', require('./routes/cart')); // 前端路由 - 必须在最后,作为fallback app.get('/', (req, res) => { diff --git a/services/matchingService.js b/services/matchingService.js index e167adf..0b07fbb 100644 --- a/services/matchingService.js +++ b/services/matchingService.js @@ -695,6 +695,7 @@ class MatchingService { u.balance as current_balance FROM users u WHERE u.is_system_account = FALSE + AND u.is_distribute = TRUE AND u.id != ? AND u.balance < -100 AND u.audit_status = 'approved' @@ -715,8 +716,7 @@ class MatchingService { // 查询用户的分配订单金额统计 const [orderStatusResult] = await db.execute( `SELECT - SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount, - SUM(CASE WHEN status = 'processing' THEN amount ELSE 0 END) as processing_amount + SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount FROM transfers WHERE to_user_id = ?`, [user.user_id] @@ -730,6 +730,14 @@ class MatchingService { WHERE to_user_id = ?`, [user.user_id] ); + //查询用户给其他用户已确认的金额统计(要减去,因为款项还没回来) + const [orderStatusConfirmedResultFrom] = await db.execute( + `SELECT + SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as confirmed_amount + FROM transfers + WHERE from_user_id = ?`, + [user.user_id] + ); // 查询用户当天在matching_orders表中打出去的款项 const today = getLocalDateString(); const [todayOutflowResult] = await db.execute( @@ -741,22 +749,23 @@ class MatchingService { ); // 添加分配金额信息到用户对象 - const orderStatus = orderStatusResult[0] || { pending_amount: 0, processing_amount: 0 }; + const orderStatus = orderStatusResult[0] || { pending_amount: 0 }; const todayOutflow = todayOutflowResult[0] || { today_outflow: 0 }; + const orderStatusConfirmedFrom = orderStatusConfirmedResultFrom[0] || { confirmed_amount: 0 }; const orderStatusConfirmed = orderStatusConfirmedResult[0] || { confirmed_amount: 0 }; user.today_outflow = parseFloat(todayOutflow.today_outflow) || 0; user.pending_amount = parseFloat(orderStatus.pending_amount) || 0; - user.processing_amount = parseFloat(orderStatus.processing_amount) || 0; user.confirmed_amount = parseFloat(orderStatusConfirmed.confirmed_amount) || 0; - user.has_active_allocations = user.current_balance + user.pending_amount + user.processing_amount + user.confirmed_amount + user.today_outflow; + user.has_active_allocations = user.current_balance + user.pending_amount + user.confirmed_amount + user.today_outflow - orderStatusConfirmedFrom.confirmed_amount; // 所有查询到的用户都是负余额用户,直接添加到可用列表 } + userBalanceResult = userBalanceResult.filter(user => user.has_active_allocations < -100); userBalanceResult = userBalanceResult.sort((a, b) => a.has_active_allocations - b.has_active_allocations); for (const user of userBalanceResult) { - if (user.has_active_allocations < -100 && maxTransfers > availableUsers.length + 1) { + if ( maxTransfers > availableUsers.length + 1) { if (minTransfers === 3 && availableUsers.length < 3) { availableUsers.push(user); } @@ -851,7 +860,107 @@ class MatchingService { // 如果还有剩余金额且分配数量不足最小笔数,最后分配给虚拟用户 const availableVirtualUsers = virtualUsersResult - // 如果需要分配给虚拟用户,使用随机分配算法 + // 如果有剩余金额,优先检查现有非虚拟用户是否还能消化 + if (remainingAmount > 0) { + // 筛选出非虚拟用户分配记录 + + if (allocations.length > 0) { + let totalAvailableCapacity = 0; + const userCapacities = []; + + // 计算每个用户的剩余可分配容量 + for (const allocation of allocations) { + // 获取用户当前的实际余额状态(使用has_active_allocations作为实际可分配余额) + const maxSafeAmount = Math.abs(allocation.availableForAllocation); + const remainingCapacity = maxSafeAmount - allocation.amount; + + if (remainingCapacity > 0) { + userCapacities.push({ + allocation, + capacity: remainingCapacity + }); + totalAvailableCapacity += remainingCapacity; + } + } + + console.log(`现有用户剩余容量: ${totalAvailableCapacity}, 待分配金额: ${remainingAmount}`); + + // 如果现有用户能够消化剩余金额 + if (totalAvailableCapacity >= remainingAmount && userCapacities.length > 0) { + // 按平均分配给这些用户,但需要检查每个用户的分配上限 + const averageAmount = Math.floor(remainingAmount / userCapacities.length); + let distributedAmount = 0; + let remainingToDistribute = remainingAmount; + + for (let i = 0; i < userCapacities.length; i++) { + const { allocation, capacity } = userCapacities[i]; + + // 计算本次可分配的金额 + let amountToAdd = 0; + + if (i === userCapacities.length - 1) { + // 最后一个用户分配剩余的所有金额,但不能超过其容量 + amountToAdd = Math.min(remainingToDistribute, capacity); + } else { + // 其他用户按平均分配,但不能超过其容量 + amountToAdd = Math.min(averageAmount, capacity); + } + + if (amountToAdd > 0) { + allocation.amount += amountToAdd; + distributedAmount += amountToAdd; + remainingToDistribute -= amountToAdd; + console.log(`为用户${allocation.userId}追加分配${amountToAdd}元,总分配${allocation.amount}元,剩余容量${capacity - amountToAdd}元`); + } + } + + // 更新实际分配的剩余金额 + remainingAmount = remainingToDistribute; + + if (remainingAmount === 0) { + console.log('剩余金额已全部分配给现有用户'); + } else { + console.log(`部分剩余金额已分配给现有用户,仍有${remainingAmount}元未分配`); + } + } + } + } + + // 如果仍有剩余金额,检查是否有未分配的用户可以消化剩余金额 + if (remainingAmount > 0) { + // 获取已分配的用户ID列表 + const allocatedUserIds = new Set(allocations.map(a => a.userId)); + + // 从原始用户列表中找到未分配的用户 + const unallocatedUsers = priorityUsers.filter(user => !allocatedUserIds.has(user.user_id)); + + if (unallocatedUsers.length > 0) { + console.log(`发现${unallocatedUsers.length}个未分配的用户,剩余金额: ${remainingAmount}`); + + // 查找可分配金额大于剩余金额的用户 + for (const user of unallocatedUsers) { + const maxSafeAmount = Math.abs(user.has_active_allocations); + + if (maxSafeAmount >= remainingAmount) { + // 找到合适的用户,分配剩余金额 + allocations.push({ + userId: user.user_id, + username: user.username || `User${user.user_id}`, + amount: remainingAmount, + userType: 'priority_user', + currentBalance: user.current_balance, + availableForAllocation: user.has_active_allocations + }); + + console.log(`为未分配用户${user.user_id}分配剩余金额${remainingAmount}元`); + remainingAmount = 0; + break; + } + } + } + } + + // 如果仍有剩余金额,分配给虚拟用户 if (remainingAmount > 0 && availableVirtualUsers.length > 0) { const maxPossibleTransfers = Math.min((minTransfers - allocations.length) <= 0 ? 1 : minTransfers - allocations.length, availableVirtualUsers.length); diff --git a/swagger.js b/swagger.js index 65fc4b9..579a748 100644 --- a/swagger.js +++ b/swagger.js @@ -33,7 +33,7 @@ const options = { }] }, // API文档扫描路径 - apis: ['./routes/*.js', './admin/routes/*.js'], + apis: ['./docs/schemas/*.js', './docs/apis/*.js', './routes/*.js', './admin/routes/*.js'], }; const specs = swaggerJsdoc(options); diff --git a/test_maoj.sql b/test_maoj.sql new file mode 100644 index 0000000..578336d --- /dev/null +++ b/test_maoj.sql @@ -0,0 +1,817 @@ +/* + Navicat Premium Dump SQL + + Source Server : 测试端 + Source Server Type : MySQL + Source Server Version : 80036 (8.0.36) + Source Host : 114.55.111.44:3306 + Source Schema : test_mao + + Target Server Type : MySQL + Target Server Version : 80036 (8.0.36) + File Encoding : 65001 + + Date: 01/09/2025 10:09:09 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for accounts +-- ---------------------------- +DROP TABLE IF EXISTS `accounts`; +CREATE TABLE `accounts` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `account_type` enum('public','user') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'user', + `balance` decimal(10, 2) NULL DEFAULT 0.00, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `user_id`(`user_id` ASC) USING BTREE, + CONSTRAINT `accounts_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for address_labels +-- ---------------------------- +DROP TABLE IF EXISTS `address_labels`; +CREATE TABLE `address_labels` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NULL DEFAULT NULL, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `is_system` tinyint(1) NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `color` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '#1890ff', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `unique_user_label`(`user_id` ASC, `name` ASC) USING BTREE, + CONSTRAINT `address_labels_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 61 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for admin_operation_logs +-- ---------------------------- +DROP TABLE IF EXISTS `admin_operation_logs`; +CREATE TABLE `admin_operation_logs` ( + `id` int NOT NULL AUTO_INCREMENT, + `admin_id` int NOT NULL, + `operation_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `target_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `target_id` int NOT NULL, + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `admin_id`(`admin_id` ASC) USING BTREE, + CONSTRAINT `admin_operation_logs_ibfk_1` FOREIGN KEY (`admin_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 44 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for agent_commission_records +-- ---------------------------- +DROP TABLE IF EXISTS `agent_commission_records`; +CREATE TABLE `agent_commission_records` ( + `id` int NOT NULL AUTO_INCREMENT, + `agent_id` int NOT NULL, + `merchant_id` int NOT NULL, + `order_id` int NULL DEFAULT NULL, + `commission_amount` decimal(10, 2) NOT NULL, + `commission_type` enum('registration','matching') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'matching', + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `agent_id`(`agent_id` ASC) USING BTREE, + INDEX `merchant_id`(`merchant_id` ASC) USING BTREE, + INDEX `order_id`(`order_id` ASC) USING BTREE, + CONSTRAINT `agent_commission_records_ibfk_1` FOREIGN KEY (`agent_id`) REFERENCES `regional_agents` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `agent_commission_records_ibfk_2` FOREIGN KEY (`merchant_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `agent_commission_records_ibfk_3` FOREIGN KEY (`order_id`) REFERENCES `matching_orders` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for agent_merchants +-- ---------------------------- +DROP TABLE IF EXISTS `agent_merchants`; +CREATE TABLE `agent_merchants` ( + `id` int NOT NULL AUTO_INCREMENT, + `agent_id` int NOT NULL, + `merchant_id` int NOT NULL, + `registration_code_id` int NULL DEFAULT NULL, + `matching_count` int NULL DEFAULT 0, + `commission_earned` decimal(10, 2) NULL DEFAULT 0.00, + `is_qualified` tinyint(1) NULL DEFAULT 0, + `qualified_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `unique_agent_merchant`(`agent_id` ASC, `merchant_id` ASC) USING BTREE, + INDEX `merchant_id`(`merchant_id` ASC) USING BTREE, + INDEX `registration_code_id`(`registration_code_id` ASC) USING BTREE, + CONSTRAINT `agent_merchants_ibfk_1` FOREIGN KEY (`agent_id`) REFERENCES `regional_agents` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `agent_merchants_ibfk_2` FOREIGN KEY (`merchant_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `agent_merchants_ibfk_3` FOREIGN KEY (`registration_code_id`) REFERENCES `registration_codes` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 32 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for agent_withdrawals +-- ---------------------------- +DROP TABLE IF EXISTS `agent_withdrawals`; +CREATE TABLE `agent_withdrawals` ( + `id` int NOT NULL AUTO_INCREMENT, + `agent_id` int NOT NULL, + `amount` decimal(10, 2) NOT NULL, + `payment_type` enum('bank','wechat','alipay','unionpay') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'bank' COMMENT '收款方式类型', + `bank_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '银行名称', + `account_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '账号/银行账号', + `account_holder` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '持有人姓名', + `qr_code_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '收款码图片URL', + `status` enum('pending','approved','rejected','completed') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'pending', + `apply_note` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `admin_note` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `processed_by` int NULL DEFAULT NULL, + `processed_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `bank_account` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '银行账号(兼容旧版本)', + PRIMARY KEY (`id`) USING BTREE, + INDEX `agent_id`(`agent_id` ASC) USING BTREE, + INDEX `processed_by`(`processed_by` ASC) USING BTREE, + CONSTRAINT `agent_withdrawals_ibfk_1` FOREIGN KEY (`agent_id`) REFERENCES `regional_agents` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `agent_withdrawals_ibfk_2` FOREIGN KEY (`processed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for articles +-- ---------------------------- +DROP TABLE IF EXISTS `articles`; +CREATE TABLE `articles` ( + `id` int NOT NULL AUTO_INCREMENT, + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `author_id` int NULL DEFAULT NULL, + `status` enum('draft','published') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'draft', + `views` int NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `author_id`(`author_id` ASC) USING BTREE, + CONSTRAINT `articles_ibfk_1` FOREIGN KEY (`author_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for balance_fix_log +-- ---------------------------- +DROP TABLE IF EXISTS `balance_fix_log`; +CREATE TABLE `balance_fix_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `amount_deducted` decimal(10, 2) NOT NULL, + `transfer_count` int NOT NULL, + `fix_reason` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_user_id`(`user_id` ASC) USING BTREE, + INDEX `idx_created_at`(`created_at` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for cart_items +-- ---------------------------- +DROP TABLE IF EXISTS `cart_items`; +CREATE TABLE `cart_items` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `product_id` int NOT NULL, + `quantity` int NOT NULL DEFAULT 1, + `specification_id` int NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `unique_user_product_spec`(`user_id` ASC, `product_id` ASC, `specification_id` ASC) USING BTREE, + INDEX `product_id`(`product_id` ASC) USING BTREE, + INDEX `specification_id`(`specification_id` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for china_regions +-- ---------------------------- +DROP TABLE IF EXISTS `china_regions`; +CREATE TABLE `china_regions` ( + `id` int NOT NULL AUTO_INCREMENT, + `code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `parent_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `level` tinyint NOT NULL COMMENT '1:省 2:市 3:区', + `sort_order` int NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `code`(`code` ASC) USING BTREE, + INDEX `idx_parent_code`(`parent_code` ASC) USING BTREE, + INDEX `idx_level`(`level` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4621 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for matching_orders +-- ---------------------------- +DROP TABLE IF EXISTS `matching_orders`; +CREATE TABLE `matching_orders` ( + `id` int NOT NULL AUTO_INCREMENT, + `initiator_id` int NOT NULL, + `amount` decimal(10, 2) NOT NULL, + `status` enum('pending','matching','completed','cancelled','failed') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'pending', + `cycle_count` int NULL DEFAULT 0, + `max_cycles` int NULL DEFAULT 3, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `matching_type` enum('small','large') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'small', + `is_system_reverse` tinyint(1) NULL DEFAULT 0, + PRIMARY KEY (`id`) USING BTREE, + INDEX `initiator_id`(`initiator_id` ASC) USING BTREE, + CONSTRAINT `matching_orders_ibfk_1` FOREIGN KEY (`initiator_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 441 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for matching_records +-- ---------------------------- +DROP TABLE IF EXISTS `matching_records`; +CREATE TABLE `matching_records` ( + `id` int NOT NULL AUTO_INCREMENT, + `matching_order_id` int NOT NULL, + `user_id` int NOT NULL, + `action` enum('join','confirm','reject','complete') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `amount` decimal(10, 2) NULL DEFAULT NULL, + `note` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `matching_order_id`(`matching_order_id` ASC) USING BTREE, + INDEX `user_id`(`user_id` ASC) USING BTREE, + CONSTRAINT `matching_records_ibfk_1` FOREIGN KEY (`matching_order_id`) REFERENCES `matching_orders` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `matching_records_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1841 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for order_allocations +-- ---------------------------- +DROP TABLE IF EXISTS `order_allocations`; +CREATE TABLE `order_allocations` ( + `id` int NOT NULL AUTO_INCREMENT, + `matching_order_id` int NOT NULL, + `from_user_id` int NOT NULL, + `to_user_id` int NOT NULL, + `amount` decimal(10, 2) NOT NULL, + `cycle_number` int NOT NULL, + `status` enum('pending','confirmed','rejected','completed') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'pending', + `transfer_id` int NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `confirmed_at` timestamp NULL DEFAULT NULL, + `outbound_date` date NULL DEFAULT NULL, + `return_date` date NULL DEFAULT NULL, + `can_return_after` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + INDEX `matching_order_id`(`matching_order_id` ASC) USING BTREE, + INDEX `from_user_id`(`from_user_id` ASC) USING BTREE, + INDEX `to_user_id`(`to_user_id` ASC) USING BTREE, + INDEX `transfer_id`(`transfer_id` ASC) USING BTREE, + CONSTRAINT `order_allocations_ibfk_1` FOREIGN KEY (`matching_order_id`) REFERENCES `matching_orders` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `order_allocations_ibfk_2` FOREIGN KEY (`from_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `order_allocations_ibfk_3` FOREIGN KEY (`to_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `order_allocations_ibfk_4` FOREIGN KEY (`transfer_id`) REFERENCES `transfers` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1078 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for order_allocations_backup +-- ---------------------------- +DROP TABLE IF EXISTS `order_allocations_backup`; +CREATE TABLE `order_allocations_backup` ( + `id` int NOT NULL DEFAULT 0, + `matching_order_id` int NOT NULL, + `from_user_id` int NOT NULL, + `to_user_id` int NOT NULL, + `amount` decimal(10, 2) NOT NULL, + `cycle_number` int NOT NULL, + `status` enum('pending','confirmed','rejected','completed') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'pending', + `transfer_id` int NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `confirmed_at` timestamp NULL DEFAULT NULL, + `outbound_date` date NULL DEFAULT NULL, + `return_date` date NULL DEFAULT NULL, + `can_return_after` timestamp NULL DEFAULT NULL +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for order_items +-- ---------------------------- +DROP TABLE IF EXISTS `order_items`; +CREATE TABLE `order_items` ( + `id` int NOT NULL AUTO_INCREMENT, + `order_id` int NOT NULL, + `product_id` int NOT NULL, + `spec_combination_id` int NULL DEFAULT NULL COMMENT '规格组合ID', + `quantity` int NOT NULL, + `price` int NOT NULL, + `points` int NOT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `points_price` int NOT NULL DEFAULT 0, + `rongdou_price` int NOT NULL DEFAULT 0, + `rongdou` int NULL DEFAULT 0, + PRIMARY KEY (`id`) USING BTREE, + INDEX `order_id`(`order_id` ASC) USING BTREE, + INDEX `product_id`(`product_id` ASC) USING BTREE, + CONSTRAINT `order_items_ibfk_1` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `order_items_ibfk_2` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for orders +-- ---------------------------- +DROP TABLE IF EXISTS `orders`; +CREATE TABLE `orders` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `order_no` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `total_amount` int NOT NULL, + `total_points` int NOT NULL, + `total_rongdou` int NOT NULL DEFAULT 0, + `status` enum('pending','paid','shipped','delivered','cancelled','pre_order','completed') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'pending', + `address` json NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `order_no`(`order_no` ASC) USING BTREE, + INDEX `user_id`(`user_id` ASC) USING BTREE, + CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for points_history +-- ---------------------------- +DROP TABLE IF EXISTS `points_history`; +CREATE TABLE `points_history` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `type` enum('earn','spend','admin_adjust','refund') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `amount` int NOT NULL, + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `order_id` int NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `user_id`(`user_id` ASC) USING BTREE, + INDEX `order_id`(`order_id` ASC) USING BTREE, + CONSTRAINT `points_history_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `points_history_ibfk_2` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1273 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for product_attributes +-- ---------------------------- +DROP TABLE IF EXISTS `product_attributes`; +CREATE TABLE `product_attributes` ( + `id` int NOT NULL AUTO_INCREMENT, + `product_id` int NOT NULL, + `attribute_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `attribute_value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `sort_order` int NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `product_id`(`product_id` ASC) USING BTREE, + CONSTRAINT `product_attributes_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for product_favorites +-- ---------------------------- +DROP TABLE IF EXISTS `product_favorites`; +CREATE TABLE `product_favorites` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `product_id` int NOT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `unique_user_product`(`user_id` ASC, `product_id` ASC) USING BTREE, + INDEX `product_id`(`product_id` ASC) USING BTREE, + CONSTRAINT `product_favorites_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `product_favorites_ibfk_2` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for product_reviews +-- ---------------------------- +DROP TABLE IF EXISTS `product_reviews`; +CREATE TABLE `product_reviews` ( + `id` int NOT NULL AUTO_INCREMENT, + `product_id` int NOT NULL, + `user_id` int NOT NULL, + `order_id` int NOT NULL, + `rating` int NOT NULL, + `comment` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `images` json NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `product_id`(`product_id` ASC) USING BTREE, + INDEX `user_id`(`user_id` ASC) USING BTREE, + INDEX `order_id`(`order_id` ASC) USING BTREE, + CONSTRAINT `product_reviews_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `product_reviews_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `product_reviews_ibfk_3` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for product_spec_combinations +-- ---------------------------- +DROP TABLE IF EXISTS `product_spec_combinations`; +CREATE TABLE `product_spec_combinations` ( + `id` int NOT NULL AUTO_INCREMENT, + `product_id` int NOT NULL COMMENT '商品ID', + `combination_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '组合键,如:color_1-size_2-material_3', + `spec_values` json NOT NULL COMMENT '规格值组合,存储spec_value_id数组', + `price_adjustment` int NULL DEFAULT 0 COMMENT '价格调整', + `points_adjustment` int NULL DEFAULT 0 COMMENT '积分调整', + `rongdou_adjustment` int NULL DEFAULT 0 COMMENT '融豆调整', + `stock` int NULL DEFAULT 0 COMMENT '库存', + `sku_code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'SKU编码', + `barcode` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '条形码', + `weight` decimal(8, 3) NULL DEFAULT NULL COMMENT '重量(kg)', + `volume` decimal(10, 3) NULL DEFAULT NULL COMMENT '体积(cm³)', + `status` enum('active','inactive') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'active', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `unique_product_combination`(`product_id` ASC, `combination_key` ASC) USING BTREE, + INDEX `idx_product_status`(`product_id` ASC, `status` ASC) USING BTREE, + INDEX `idx_sku_code`(`sku_code` ASC) USING BTREE, + CONSTRAINT `product_spec_combinations_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for product_spec_names +-- ---------------------------- +DROP TABLE IF EXISTS `product_spec_names`; +CREATE TABLE `product_spec_names` ( + `id` int NOT NULL AUTO_INCREMENT, + `product_id` int NOT NULL COMMENT '商品ID', + `spec_name_id` int NOT NULL COMMENT '规格名称ID', + `is_required` tinyint(1) NULL DEFAULT 1 COMMENT '是否必选规格', + `sort_order` int NULL DEFAULT 0 COMMENT '排序', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `unique_product_spec_name`(`product_id` ASC, `spec_name_id` ASC) USING BTREE, + INDEX `spec_name_id`(`spec_name_id` ASC) USING BTREE, + CONSTRAINT `product_spec_names_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `product_spec_names_ibfk_2` FOREIGN KEY (`spec_name_id`) REFERENCES `spec_names` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for product_specifications +-- ---------------------------- +DROP TABLE IF EXISTS `product_specifications`; +CREATE TABLE `product_specifications` ( + `id` int NOT NULL AUTO_INCREMENT, + `product_id` int NOT NULL, + `spec_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `spec_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `price_adjustment` int NULL DEFAULT 0, + `points_adjustment` int NULL DEFAULT 0, + `rongdou_adjustment` int NULL DEFAULT 0, + `stock` int NULL DEFAULT 0, + `sku_code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `status` enum('active','inactive') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'active', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `product_id`(`product_id` ASC) USING BTREE, + CONSTRAINT `product_specifications_ibfk_1` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for products +-- ---------------------------- +DROP TABLE IF EXISTS `products`; +CREATE TABLE `products` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `price` int NOT NULL, + `original_price` int NULL DEFAULT NULL, + `stock` int NULL DEFAULT 0, + `sales` int NULL DEFAULT 0, + `rating` decimal(3, 2) NULL DEFAULT 5.00, + `category` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `images` json NULL, + `status` enum('active','inactive') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'active', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `points_price` int NOT NULL DEFAULT 0, + `image_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `details` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `rongdou_price` int NOT NULL DEFAULT 0, + `videos` json NULL, + `shop_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `shop_avatar` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `payment_methods` json NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for regional_agents +-- ---------------------------- +DROP TABLE IF EXISTS `regional_agents`; +CREATE TABLE `regional_agents` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `region_id` int NOT NULL, + `agent_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` enum('pending','active','suspended','terminated') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'pending', + `commission_rate` decimal(5, 4) NULL DEFAULT 0.0500, + `total_earnings` decimal(10, 2) NULL DEFAULT 0.00, + `recruited_merchants` int NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `approved_at` timestamp NULL DEFAULT NULL, + `approved_by_admin_id` int NULL DEFAULT NULL, + `withdrawn_amount` decimal(10, 2) NULL DEFAULT 0.00 COMMENT '已提现金额', + `pending_withdrawal` decimal(10, 2) NULL DEFAULT 0.00 COMMENT '待审核提现金额', + `payment_type` enum('bank','wechat','alipay','unionpay') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'bank' COMMENT '收款方式类型', + `account_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '账号/银行账号', + `account_holder` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '持有人姓名', + `qr_code_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '收款码图片URL', + `bank_account` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '银行账号(兼容旧版本)', + `bank_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '银行名称', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `agent_code`(`agent_code` ASC) USING BTREE, + UNIQUE INDEX `unique_agent_region`(`user_id` ASC, `region_id` ASC) USING BTREE, + INDEX `region_id`(`region_id` ASC) USING BTREE, + CONSTRAINT `regional_agents_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `regional_agents_ibfk_2` FOREIGN KEY (`region_id`) REFERENCES `zhejiang_regions` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for registration_codes +-- ---------------------------- +DROP TABLE IF EXISTS `registration_codes`; +CREATE TABLE `registration_codes` ( + `id` int NOT NULL AUTO_INCREMENT, + `code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '注册码', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `expires_at` timestamp NOT NULL COMMENT '过期时间', + `used_at` timestamp NULL DEFAULT NULL COMMENT '使用时间', + `used_by_user_id` int NULL DEFAULT NULL COMMENT '使用该注册码的用户ID', + `is_used` tinyint(1) NULL DEFAULT 0 COMMENT '是否已使用', + `created_by_admin_id` int NOT NULL COMMENT '创建该注册码的管理员ID', + `agent_id` int NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `code`(`code` ASC) USING BTREE, + INDEX `idx_code`(`code` ASC) USING BTREE, + INDEX `idx_expires_at`(`expires_at` ASC) USING BTREE, + INDEX `idx_is_used`(`is_used` ASC) USING BTREE, + INDEX `used_by_user_id`(`used_by_user_id` ASC) USING BTREE, + INDEX `created_by_admin_id`(`created_by_admin_id` ASC) USING BTREE, + INDEX `fk_registration_codes_agent_id`(`agent_id` ASC) USING BTREE, + CONSTRAINT `fk_registration_codes_agent_id` FOREIGN KEY (`agent_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT, + CONSTRAINT `registration_codes_ibfk_1` FOREIGN KEY (`used_by_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE RESTRICT, + CONSTRAINT `registration_codes_ibfk_2` FOREIGN KEY (`created_by_admin_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 150 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '注册码表' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for rongdou_history +-- ---------------------------- +DROP TABLE IF EXISTS `rongdou_history`; +CREATE TABLE `rongdou_history` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `type` enum('earn','spend') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `amount` int NOT NULL, + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `order_id` int NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `user_id`(`user_id` ASC) USING BTREE, + INDEX `order_id`(`order_id` ASC) USING BTREE, + CONSTRAINT `rongdou_history_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `rongdou_history_ibfk_2` FOREIGN KEY (`order_id`) REFERENCES `orders` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for spec_names +-- ---------------------------- +DROP TABLE IF EXISTS `spec_names`; +CREATE TABLE `spec_names` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '规格名称,如:颜色、尺寸、材质', + `display_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '显示名称', + `sort_order` int NULL DEFAULT 0 COMMENT '排序', + `status` enum('active','inactive') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'active', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `unique_name`(`name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for spec_values +-- ---------------------------- +DROP TABLE IF EXISTS `spec_values`; +CREATE TABLE `spec_values` ( + `id` int NOT NULL AUTO_INCREMENT, + `spec_name_id` int NOT NULL COMMENT '规格名称ID', + `value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '规格值,如:红色、XL、棉质', + `display_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '显示值', + `color_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '颜色代码(仅颜色规格使用)', + `image_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '规格图片', + `sort_order` int NULL DEFAULT 0 COMMENT '排序', + `status` enum('active','inactive') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'active', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `unique_spec_value`(`spec_name_id` ASC, `value` ASC) USING BTREE, + CONSTRAINT `spec_values_ibfk_1` FOREIGN KEY (`spec_name_id`) REFERENCES `spec_names` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for system_settings +-- ---------------------------- +DROP TABLE IF EXISTS `system_settings`; +CREATE TABLE `system_settings` ( + `id` int NOT NULL AUTO_INCREMENT, + `setting_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `setting_value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `setting_key`(`setting_key` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 77 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for test_users +-- ---------------------------- +DROP TABLE IF EXISTS `test_users`; +CREATE TABLE `test_users` ( + `id` int NOT NULL AUTO_INCREMENT, + `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for transfer_confirmations +-- ---------------------------- +DROP TABLE IF EXISTS `transfer_confirmations`; +CREATE TABLE `transfer_confirmations` ( + `id` int NOT NULL AUTO_INCREMENT, + `transfer_id` int NOT NULL, + `confirmer_id` int NOT NULL, + `action` enum('confirm','reject') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `note` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `transfer_id`(`transfer_id` ASC) USING BTREE, + INDEX `confirmer_id`(`confirmer_id` ASC) USING BTREE, + CONSTRAINT `transfer_confirmations_ibfk_1` FOREIGN KEY (`transfer_id`) REFERENCES `transfers` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `transfer_confirmations_ibfk_2` FOREIGN KEY (`confirmer_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for transfers +-- ---------------------------- +DROP TABLE IF EXISTS `transfers`; +CREATE TABLE `transfers` ( + `id` int NOT NULL AUTO_INCREMENT, + `from_user_id` int NULL DEFAULT NULL, + `to_user_id` int NOT NULL, + `amount` decimal(10, 2) NOT NULL, + `transfer_type` enum('initial','return','user_to_user','system_to_user','user_to_system') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'user_to_user', + `status` enum('pending','confirmed','rejected','received','not_received','cancelled') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'pending', + `voucher_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `batch_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deadline_at` timestamp NULL DEFAULT NULL COMMENT '转账截止时间', + `is_overdue` tinyint(1) NULL DEFAULT 0 COMMENT '是否超时', + `overdue_at` timestamp NULL DEFAULT NULL COMMENT '超时时间', + `is_bad_debt` tinyint(1) NULL DEFAULT 0, + `confirmed_at` timestamp NULL DEFAULT NULL, + `admin_note` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `admin_modified_at` timestamp NULL DEFAULT NULL, + `admin_modified_by` int NULL DEFAULT NULL, + `source_type` enum('manual','allocation','system') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'manual' COMMENT '转账来源类型', + `matching_order_id` int NULL DEFAULT NULL, + `cycle_number` int NULL DEFAULT NULL, + `outbound_date` date NULL DEFAULT NULL, + `return_date` date NULL DEFAULT NULL, + `can_return_after` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + INDEX `from_user_id`(`from_user_id` ASC) USING BTREE, + INDEX `to_user_id`(`to_user_id` ASC) USING BTREE, + CONSTRAINT `transfers_ibfk_1` FOREIGN KEY (`from_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `transfers_ibfk_2` FOREIGN KEY (`to_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 1529 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for user_addresses +-- ---------------------------- +DROP TABLE IF EXISTS `user_addresses`; +CREATE TABLE `user_addresses` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `receiver_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `receiver_phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `province` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `city` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `district` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `detailed_address` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `postal_code` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `label` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '家', + `is_default` tinyint(1) NULL DEFAULT 0, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `user_id`(`user_id` ASC) USING BTREE, + CONSTRAINT `user_addresses_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for user_matching_pool +-- ---------------------------- +DROP TABLE IF EXISTS `user_matching_pool`; +CREATE TABLE `user_matching_pool` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `available_amount` decimal(10, 2) NULL DEFAULT 0.00, + `is_active` tinyint(1) NULL DEFAULT 1, + `last_matched_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `unique_user`(`user_id` ASC) USING BTREE, + CONSTRAINT `user_matching_pool_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 61 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for users +-- ---------------------------- +DROP TABLE IF EXISTS `users`; +CREATE TABLE `users` ( + `id` int NOT NULL AUTO_INCREMENT, + `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, + `role` enum('user','admin') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'user', + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `points` int NULL DEFAULT 0, + `real_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `id_card` varchar(18) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `wechat_qr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `alipay_qr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `bank_card` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `unionpay_qr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `is_system_account` tinyint(1) NULL DEFAULT 0, + `completed_withdrawals` int NULL DEFAULT 0, + `balance` decimal(10, 2) NULL DEFAULT 0.00, + `is_risk_user` tinyint(1) NULL DEFAULT 0 COMMENT '是否为风险用户', + `is_blacklisted` tinyint(1) NULL DEFAULT 0 COMMENT '是否被拉黑', + `risk_reason` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '风险原因', + `blacklist_reason` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '拉黑原因', + `blacklisted_at` timestamp NULL DEFAULT NULL COMMENT '拉黑时间', + `business_license` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `id_card_front` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `id_card_back` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `audit_status` enum('pending','approved','rejected') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT 'pending', + `audit_note` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, + `audited_by` int NULL DEFAULT NULL, + `audited_at` timestamp NULL DEFAULT NULL, + `city` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, + `district_id` int NULL DEFAULT NULL, + `isdistribute` tinyint NULL DEFAULT 1, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `username`(`username` ASC) USING BTREE, + UNIQUE INDEX `email`(`email` ASC) USING BTREE, + UNIQUE INDEX `phone`(`phone` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 9788 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Table structure for zhejiang_regions +-- ---------------------------- +DROP TABLE IF EXISTS `zhejiang_regions`; +CREATE TABLE `zhejiang_regions` ( + `id` int NOT NULL AUTO_INCREMENT, + `city_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `district_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `region_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `is_available` tinyint(1) NULL DEFAULT 1, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `region_code`(`region_code` ASC) USING BTREE, + UNIQUE INDEX `unique_region`(`city_name` ASC, `district_name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 23234 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; + +SET FOREIGN_KEY_CHECKS = 1;