diff --git a/.env b/.env
index 39e2c22..a74c634 100644
--- a/.env
+++ b/.env
@@ -19,7 +19,7 @@ ALIYUN_SMS_TEMPLATE_CODE=SMS_324470054
# 环境配置
NODE_ENV=development
-PORT=3000
+PORT=3008
# 前端地址配置
FRONTEND_URL=https://www.zrbjr.com/frontend
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/UniappTool.xml b/.idea/UniappTool.xml
new file mode 100644
index 0000000..f7328e8
--- /dev/null
+++ b/.idea/UniappTool.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..497a8d0
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ mysql.8
+ true
+ com.mysql.cj.jdbc.Driver
+ jdbc:mysql://114.55.111.44:3306/test_mao
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/data_source_mapping.xml b/.idea/data_source_mapping.xml
new file mode 100644
index 0000000..14f154b
--- /dev/null
+++ b/.idea/data_source_mapping.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jurong_circle_shopping_black.iml b/.idea/jurong_circle_shopping_black.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/jurong_circle_shopping_black.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..42954ec
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
new file mode 100644
index 0000000..56782ca
--- /dev/null
+++ b/.idea/sqldialects.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.trae/TODO.md b/.trae/TODO.md
deleted file mode 100644
index 8ad817f..0000000
--- a/.trae/TODO.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# TODO:
-
-- [x] backend-api: 在 routes/transfers.js 中创建 GET /api/transfers/pending-allocations 接口 (priority: High)
-- [x] frontend-tabs: 在 admin/src/views/Transfers.vue 中添加待处理匹配订单标签页 (priority: High)
-- [x] frontend-display: 实现待处理匹配订单的数据显示和表格 (priority: High)
-- [x] frontend-operations: 添加待处理匹配订单的操作功能(确认、拒绝等) (priority: Medium)
-- [x] testing: 测试新功能的完整性和正确性 (priority: Medium)
diff --git a/.vercel/project.json b/.vercel/project.json
deleted file mode 100644
index d931e9c..0000000
--- a/.vercel/project.json
+++ /dev/null
@@ -1 +0,0 @@
-{"projectName":"trae_code_bv1k"}
\ No newline at end of file
diff --git a/API-DOCS-README.md b/API-DOCS-README.md
deleted file mode 100644
index a47efc1..0000000
--- a/API-DOCS-README.md
+++ /dev/null
@@ -1,123 +0,0 @@
-# API文档生成与Apifox同步指南
-
-本项目已集成Swagger用于API文档生成,并提供了与Apifox同步的工具脚本。
-
-## 1. 查看API文档
-
-启动服务器后,可以通过以下URL访问Swagger UI界面查看API文档:
-
-```
-http://localhost:3000/api-docs
-```
-
-## 2. 手动导出并导入Apifox
-
-### 2.1 导出Swagger文档
-
-运行以下命令导出Swagger文档:
-
-```bash
-node export-swagger.js
-```
-
-这将在`api-docs`目录下生成`swagger.json`文件。
-
-### 2.2 手动导入Apifox
-
-1. 打开Apifox应用
-2. 选择您的项目
-3. 点击"导入"按钮
-4. 选择"导入OpenAPI(Swagger)"
-5. 选择刚才导出的swagger.json文件
-6. 按照Apifox的导入向导完成导入
-
-## 3. 自动同步到Apifox
-
-### 3.1 安装Apifox CLI
-
-```bash
-npm install -g apifox-cli
-```
-
-### 3.2 登录Apifox CLI
-
-```bash
-apifox-cli login
-```
-
-### 3.3 配置项目ID
-
-编辑`apifox-sync.js`文件,将`YOUR_APIFOX_PROJECT_ID`替换为您的Apifox项目ID。
-
-> 获取项目ID:在Apifox网页版中打开项目,URL中包含项目ID
-
-### 3.4 运行同步脚本
-
-```bash
-node apifox-sync.js
-```
-
-## 4. 为API添加Swagger注释
-
-为了使API文档保持最新,在添加新的API接口时,请按照以下格式添加Swagger注释:
-
-```javascript
-/**
- * @swagger
- * /api/resource:
- * get:
- * summary: 接口描述
- * tags: [资源分类]
- * parameters:
- * - name: param
- * in: query
- * description: 参数描述
- * required: true
- * schema:
- * type: string
- * responses:
- * 200:
- * description: 成功响应
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: object
- */
-router.get('/resource', (req, res) => {
- // 实现代码
-});
-```
-
-## 5. 定义数据模型
-
-在路由文件顶部定义数据模型,例如:
-
-```javascript
-/**
- * @swagger
- * components:
- * schemas:
- * ModelName:
- * type: object
- * properties:
- * id:
- * type: integer
- * name:
- * type: string
- * required:
- * - name
- */
-```
-
-## 6. 自动化集成
-
-可以将API文档生成和同步集成到CI/CD流程中,在每次部署前自动更新API文档。
-
----
-
-如有任何问题,请参考[Swagger官方文档](https://swagger.io/docs/)和[Apifox官方文档](https://www.apifox.cn/help/)。
\ No newline at end of file
diff --git a/MIGRATION-GUIDE.md b/MIGRATION-GUIDE.md
deleted file mode 100644
index 9fbf739..0000000
--- a/MIGRATION-GUIDE.md
+++ /dev/null
@@ -1,186 +0,0 @@
-# 文件迁移到 MinIO 指南
-
-本指南将帮助您将现有的本地静态文件迁移到 MinIO 对象存储。
-
-## 迁移前准备
-
-### 1. 确保 MinIO 配置正确
-
-确保 `.env` 文件中的 MinIO 配置正确:
-
-```env
-MINIO_ENDPOINT=your-minio-server
-MINIO_PORT=9000
-MINIO_ACCESS_KEY=your-access-key
-MINIO_SECRET_KEY=your-secret-key
-MINIO_USE_SSL=false
-MINIO_PUBLIC_URL=https://minio.yourdomain.com
-```
-
-### 2. 确保存储桶已创建
-
-确保以下存储桶在 MinIO 中已存在:
-- `avatars` - 用户头像
-- `products` - 产品图片
-- `documents` - 文档文件
-
-### 3. 备份数据库
-
-**重要:在开始迁移前,请务必备份数据库!**
-
-```bash
-mysqldump -u username -p database_name > backup_before_migration.sql
-```
-
-## 执行迁移
-
-### 1. 运行迁移脚本
-
-```bash
-cd jurong_circle_black
-node migrate-to-minio.js
-```
-
-### 2. 监控迁移过程
-
-脚本会显示详细的迁移进度:
-- 扫描本地文件
-- 逐个上传到 MinIO
-- 更新数据库中的文件路径引用
-- 生成迁移报告
-
-## 迁移后验证
-
-### 1. 检查迁移报告
-
-查看生成的 `migration-report.json` 文件:
-
-```json
-{
- "migrationDate": "2024-01-15T10:30:00.000Z",
- "totalFiles": 150,
- "successCount": 148,
- "failedCount": 2,
- "migratedFiles": [...],
- "failedFiles": [...]
-}
-```
-
-### 2. 验证文件访问
-
-- 检查用户头像是否正常显示
-- 检查产品图片是否正常显示
-- 检查文档下载是否正常
-
-### 3. 验证数据库更新
-
-检查数据库中的文件路径是否已更新:
-
-```sql
--- 检查用户头像路径
-SELECT id, username, avatar FROM users WHERE avatar LIKE 'https://minio%' LIMIT 10;
-
--- 检查产品图片路径
-SELECT id, name, image_url FROM products WHERE image_url LIKE 'https://minio%' LIMIT 10;
-```
-
-## 文件组织结构
-
-迁移后,文件将按以下结构组织:
-
-```
-MinIO 存储桶/
-├── avatars/
-│ ├── 2024/
-│ │ ├── 01/
-│ │ │ ├── 15/
-│ │ │ │ ├── 1640995200000_a1b2c3d4.jpg
-│ │ │ │ └── 1640995300000_b2c3d4e5.png
-│ │ │ └── 16/
-│ │ └── 02/
-│ └── 2023/
-├── products/
-│ ├── 2024/
-│ │ ├── 01/
-│ │ │ ├── 15/
-│ │ │ └── 16/
-│ │ └── 02/
-│ └── 2023/
-└── documents/
- ├── 2024/
- └── 2023/
-```
-
-## 故障排除
-
-### 常见问题
-
-1. **连接 MinIO 失败**
- - 检查 MinIO 服务是否运行
- - 验证网络连接
- - 确认访问密钥正确
-
-2. **存储桶不存在**
- - 在 MinIO 控制台创建所需的存储桶
- - 确保存储桶名称与配置一致
-
-3. **权限问题**
- - 确保 MinIO 用户有读写权限
- - 检查存储桶策略设置
-
-4. **部分文件迁移失败**
- - 查看迁移报告中的失败文件列表
- - 检查文件是否损坏或被占用
- - 手动重新上传失败的文件
-
-### 回滚方案
-
-如果迁移出现问题,可以通过以下步骤回滚:
-
-1. **恢复数据库备份**
- ```bash
- mysql -u username -p database_name < backup_before_migration.sql
- ```
-
-2. **重新配置文件上传路径**
- - 修改 `routes/upload.js` 使用本地存储
- - 确保 `uploads` 目录存在且有正确权限
-
-## 迁移完成后的清理
-
-### 1. 删除本地文件(可选)
-
-**警告:只有在确认迁移成功且系统运行正常后才执行此操作!**
-
-```bash
-# 备份 uploads 目录
-mv uploads uploads_backup_$(date +%Y%m%d)
-
-# 或者直接删除(谨慎操作)
-# rm -rf uploads
-```
-
-### 2. 更新部署脚本
-
-更新生产环境的部署脚本,移除对 `uploads` 目录的依赖。
-
-### 3. 更新备份策略
-
-确保备份策略包含 MinIO 数据的备份。
-
-## 注意事项
-
-1. **迁移时间**:根据文件数量和大小,迁移可能需要较长时间
-2. **网络稳定性**:确保网络连接稳定,避免迁移中断
-3. **存储空间**:确保 MinIO 有足够的存储空间
-4. **并发限制**:脚本已添加延迟避免过快请求,如需调整可修改代码
-5. **文件路径**:迁移后的文件路径将包含日期文件夹结构
-
-## 技术支持
-
-如果在迁移过程中遇到问题,请:
-
-1. 查看控制台输出的错误信息
-2. 检查 `migration-report.json` 中的详细信息
-3. 确保 MinIO 服务正常运行
-4. 验证网络连接和权限设置
\ No newline at end of file
diff --git a/api-docs/swagger.json b/api-docs/swagger.json
deleted file mode 100644
index 066f91c..0000000
--- a/api-docs/swagger.json
+++ /dev/null
@@ -1,8639 +0,0 @@
-{
- "openapi": "3.0.0",
- "info": {
- "title": "融豆商城 API",
- "version": "1.0.0",
- "description": "融豆商城后端API文档",
- "contact": {
- "name": "技术支持",
- "email": "support@example.com"
- }
- },
- "servers": [
- {
- "url": "/api",
- "description": "API服务器"
- }
- ],
- "components": {
- "securitySchemes": {
- "bearerAuth": {
- "type": "http",
- "scheme": "bearer",
- "bearerFormat": "JWT"
- }
- },
- "schemas": {
- "Announcement": {
- "type": "object",
- "required": [
- "title",
- "content",
- "type",
- "priority"
- ],
- "properties": {
- "id": {
- "type": "integer",
- "description": "公告ID",
- "example": 1
- },
- "title": {
- "type": "string",
- "description": "公告标题",
- "example": "系统维护通知"
- },
- "content": {
- "type": "string",
- "description": "公告内容",
- "example": "系统将于今晚进行维护,预计维护时间2小时,期间可能影响部分功能使用。"
- },
- "type": {
- "type": "string",
- "description": "公告类型",
- "enum": [
- "system",
- "maintenance",
- "promotion",
- "warning"
- ],
- "example": "maintenance"
- },
- "priority": {
- "type": "string",
- "description": "优先级",
- "enum": [
- "low",
- "medium",
- "high",
- "urgent"
- ],
- "example": "high"
- },
- "status": {
- "type": "string",
- "description": "状态",
- "enum": [
- "draft",
- "published",
- "archived"
- ],
- "example": "published"
- },
- "isTop": {
- "type": "boolean",
- "description": "是否置顶",
- "example": false
- },
- "publishTime": {
- "type": "string",
- "format": "date-time",
- "description": "发布时间",
- "example": "2024-01-15T10:00:00Z"
- },
- "expireTime": {
- "type": "string",
- "format": "date-time",
- "description": "过期时间",
- "example": "2024-01-20T10:00:00Z"
- },
- "createdBy": {
- "type": "integer",
- "description": "创建者用户ID",
- "example": 1
- },
- "createdAt": {
- "type": "string",
- "format": "date-time",
- "description": "创建时间",
- "example": "2024-01-15T09:00:00Z"
- },
- "updatedAt": {
- "type": "string",
- "format": "date-time",
- "description": "更新时间",
- "example": "2024-01-15T09:30:00Z"
- },
- "creator": {
- "type": "object",
- "description": "创建者信息",
- "properties": {
- "id": {
- "type": "integer",
- "example": 1
- },
- "username": {
- "type": "string",
- "example": "admin"
- },
- "email": {
- "type": "string",
- "example": "admin@example.com"
- }
- }
- },
- "is_pinned": {
- "type": "boolean",
- "description": "是否置顶"
- },
- "publish_time": {
- "type": "string",
- "format": "date-time",
- "description": "发布时间"
- },
- "expire_time": {
- "type": "string",
- "format": "date-time",
- "description": "过期时间"
- },
- "created_by": {
- "type": "integer",
- "description": "创建者ID"
- },
- "created_at": {
- "type": "string",
- "format": "date-time",
- "description": "创建时间"
- },
- "updated_at": {
- "type": "string",
- "format": "date-time",
- "description": "更新时间"
- }
- }
- },
- "AnnouncementCreate": {
- "type": "object",
- "required": [
- "title",
- "content",
- "type",
- "priority"
- ],
- "properties": {
- "title": {
- "type": "string",
- "description": "公告标题",
- "example": "系统维护通知"
- },
- "content": {
- "type": "string",
- "description": "公告内容",
- "example": "系统将于今晚进行维护,预计维护时间2小时。"
- },
- "type": {
- "type": "string",
- "description": "公告类型",
- "enum": [
- "system",
- "activity",
- "maintenance",
- "urgent"
- ],
- "example": "maintenance"
- },
- "priority": {
- "type": "string",
- "description": "优先级",
- "enum": [
- "high",
- "medium",
- "low"
- ],
- "example": "high"
- },
- "status": {
- "type": "string",
- "description": "公告状态",
- "enum": [
- "draft",
- "published"
- ],
- "default": "draft",
- "example": "draft"
- },
- "isTop": {
- "type": "boolean",
- "description": "是否置顶",
- "default": false,
- "example": false
- },
- "publishTime": {
- "type": "string",
- "format": "date-time",
- "description": "发布时间",
- "example": "2024-01-15T10:00:00Z"
- },
- "expireTime": {
- "type": "string",
- "format": "date-time",
- "description": "过期时间",
- "example": "2024-01-20T10:00:00Z"
- }
- }
- },
- "AnnouncementUpdate": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "公告标题",
- "example": "系统维护通知(更新)"
- },
- "content": {
- "type": "string",
- "description": "公告内容",
- "example": "系统维护时间调整为明晚进行。"
- },
- "type": {
- "type": "string",
- "description": "公告类型",
- "enum": [
- "system",
- "activity",
- "maintenance",
- "urgent"
- ],
- "example": "maintenance"
- },
- "priority": {
- "type": "string",
- "description": "优先级",
- "enum": [
- "high",
- "medium",
- "low"
- ],
- "example": "medium"
- },
- "status": {
- "type": "string",
- "description": "公告状态",
- "enum": [
- "draft",
- "published",
- "expired"
- ],
- "example": "published"
- },
- "isTop": {
- "type": "boolean",
- "description": "是否置顶",
- "example": true
- },
- "publishTime": {
- "type": "string",
- "format": "date-time",
- "description": "发布时间",
- "example": "2024-01-16T10:00:00Z"
- },
- "expireTime": {
- "type": "string",
- "format": "date-time",
- "description": "过期时间",
- "example": "2024-01-21T10:00:00Z"
- }
- }
- },
- "AnnouncementList": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "announcements": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Announcement"
- }
- },
- "total": {
- "type": "integer",
- "description": "总记录数",
- "example": 50
- },
- "page": {
- "type": "integer",
- "description": "当前页码",
- "example": 1
- },
- "limit": {
- "type": "integer",
- "description": "每页数量",
- "example": 10
- },
- "totalPages": {
- "type": "integer",
- "description": "总页数",
- "example": 5
- }
- }
- }
- }
- },
- "AnnouncementResponse": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "操作成功"
- },
- "data": {
- "$ref": "#/components/schemas/Announcement"
- }
- }
- },
- "AnnouncementError": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": false
- },
- "message": {
- "type": "string",
- "example": "操作失败"
- },
- "error": {
- "type": "string",
- "example": "公告不存在"
- }
- }
- },
- "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": "更新时间"
- },
- "spec_combination_id": {
- "type": "integer",
- "description": "商品规格组合ID"
- },
- "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"
- }
- }
- }
- }
- },
- "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": "商品数量"
- }
- }
- },
- "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": "去重后的支付方式列表"
- }
- }
- },
- "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": "更新时间"
- }
- }
- },
- "User": {
- "type": "object",
- "required": [
- "username",
- "password",
- "real_name",
- "id_card"
- ],
- "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": "更新时间"
- },
- "role": {
- "type": "string",
- "description": "用户角色",
- "enum": [
- "user",
- "admin",
- "merchant"
- ]
- },
- "real_name": {
- "type": "string",
- "description": "真实姓名"
- },
- "id_card": {
- "type": "string",
- "description": "身份证号"
- },
- "is_system_account": {
- "type": "boolean",
- "description": "是否为系统账户"
- },
- "is_distribute": {
- "type": "boolean",
- "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",
- "phone",
- "password",
- "registrationCode",
- "city",
- "district_id",
- "captchaId",
- "captchaText",
- "smsCode"
- ],
- "properties": {
- "username": {
- "type": "string",
- "description": "用户名"
- },
- "email": {
- "type": "string",
- "format": "email",
- "description": "邮箱地址"
- },
- "password": {
- "type": "string",
- "description": "密码"
- },
- "phone": {
- "type": "string",
- "description": "手机号"
- },
- "registrationCode": {
- "type": "string",
- "description": "注册激活码"
- },
- "city": {
- "type": "string",
- "description": "城市"
- },
- "district_id": {
- "type": "string",
- "description": "区域ID"
- },
- "captchaId": {
- "type": "string",
- "description": "图形验证码ID"
- },
- "captchaText": {
- "type": "string",
- "description": "图形验证码文本"
- },
- "smsCode": {
- "type": "string",
- "description": "短信验证码"
- },
- "role": {
- "type": "string",
- "description": "用户角色",
- "default": "user"
- }
- }
- },
- "Address": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer",
- "description": "地址ID"
- },
- "user_id": {
- "type": "integer",
- "description": "用户ID"
- },
- "recipient_name": {
- "type": "string",
- "description": "收件人姓名"
- },
- "phone": {
- "type": "string",
- "description": "联系电话"
- },
- "province_code": {
- "type": "string",
- "description": "省份编码"
- },
- "province_name": {
- "type": "string",
- "description": "省份名称"
- },
- "city_code": {
- "type": "string",
- "description": "城市编码"
- },
- "city_name": {
- "type": "string",
- "description": "城市名称"
- },
- "district_code": {
- "type": "string",
- "description": "区县编码"
- },
- "district_name": {
- "type": "string",
- "description": "区县名称"
- },
- "detailed_address": {
- "type": "string",
- "description": "详细地址"
- },
- "postal_code": {
- "type": "string",
- "description": "邮政编码"
- },
- "label_id": {
- "type": "integer",
- "description": "地址标签ID"
- },
- "is_default": {
- "type": "boolean",
- "description": "是否为默认地址"
- },
- "label_name": {
- "type": "string",
- "description": "标签名称"
- },
- "label_color": {
- "type": "string",
- "description": "标签颜色"
- }
- },
- "required": [
- "recipient_name",
- "phone",
- "province_code",
- "province_name",
- "city_code",
- "city_name",
- "district_code",
- "district_name",
- "detailed_address"
- ]
- },
- "LoginCredentials": {
- "type": "object",
- "required": [
- "username",
- "password"
- ],
- "properties": {
- "username": {
- "type": "string",
- "description": "用户名或手机号"
- },
- "password": {
- "type": "string",
- "description": "密码"
- }
- }
- },
- "MatchingOrder": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer",
- "description": "匹配订单ID"
- },
- "initiator_id": {
- "type": "integer",
- "description": "发起人ID"
- },
- "matching_type": {
- "type": "string",
- "enum": [
- "small",
- "large"
- ],
- "description": "匹配类型(小额或大额)"
- },
- "amount": {
- "type": "number",
- "description": "匹配总金额"
- },
- "status": {
- "type": "string",
- "enum": [
- "pending",
- "matching",
- "completed",
- "failed"
- ],
- "description": "订单状态"
- },
- "created_at": {
- "type": "string",
- "format": "date-time",
- "description": "创建时间"
- }
- }
- },
- "Allocation": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer",
- "description": "分配ID"
- },
- "from_user_id": {
- "type": "integer",
- "description": "发送方用户ID"
- },
- "to_user_id": {
- "type": "integer",
- "description": "接收方用户ID"
- },
- "amount": {
- "type": "number",
- "description": "分配金额"
- },
- "cycle_number": {
- "type": "integer",
- "description": "轮次编号"
- },
- "status": {
- "type": "string",
- "enum": [
- "pending",
- "confirmed",
- "rejected",
- "cancelled"
- ],
- "description": "分配状态"
- },
- "created_at": {
- "type": "string",
- "format": "date-time",
- "description": "创建时间"
- }
- }
- },
- "UnreasonableMatch": {
- "type": "object",
- "properties": {
- "allocation_id": {
- "type": "integer",
- "description": "分配ID"
- },
- "from_user_id": {
- "type": "integer",
- "description": "发送方用户ID"
- },
- "to_user_id": {
- "type": "integer",
- "description": "接收方用户ID"
- },
- "amount": {
- "type": "number",
- "description": "分配金额"
- },
- "status": {
- "type": "string",
- "enum": [
- "pending",
- "confirmed",
- "rejected",
- "cancelled"
- ],
- "description": "分配状态"
- },
- "to_username": {
- "type": "string",
- "description": "接收方用户名"
- },
- "to_user_balance": {
- "type": "number",
- "description": "接收方用户余额"
- },
- "from_username": {
- "type": "string",
- "description": "发送方用户名"
- },
- "from_user_balance": {
- "type": "number",
- "description": "发送方用户余额"
- }
- }
- },
- "PointsHistory": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer",
- "description": "积分历史记录ID"
- },
- "points_change": {
- "type": "integer",
- "description": "积分变动数量"
- },
- "type": {
- "type": "string",
- "description": "积分变动类型(earn-获得, spend-消费, admin_adjust-管理员调整)"
- },
- "description": {
- "type": "string",
- "description": "积分变动描述"
- },
- "created_at": {
- "type": "string",
- "format": "date-time",
- "description": "创建时间"
- }
- }
- },
- "Region": {
- "type": "object",
- "properties": {
- "code": {
- "type": "string",
- "description": "地区编码"
- },
- "name": {
- "type": "string",
- "description": "地区名称"
- }
- }
- },
- "ZhejiangRegion": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer",
- "description": "地区ID"
- },
- "city_name": {
- "type": "string",
- "description": "城市名称"
- },
- "district_name": {
- "type": "string",
- "description": "区县名称"
- },
- "region_code": {
- "type": "string",
- "description": "地区编码"
- },
- "is_available": {
- "type": "integer",
- "description": "是否可用(1:可用 0:不可用)"
- }
- }
- },
- "SMSVerification": {
- "type": "object",
- "properties": {
- "phone": {
- "type": "string",
- "description": "手机号码"
- },
- "code": {
- "type": "string",
- "description": "验证码"
- }
- }
- },
- "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": {
- "id": {
- "type": "integer",
- "description": "转账记录ID"
- },
- "user_id": {
- "type": "integer",
- "description": "用户ID"
- },
- "recipient_id": {
- "type": "integer",
- "description": "接收方用户ID"
- },
- "amount": {
- "type": "number",
- "format": "float",
- "description": "转账金额"
- },
- "status": {
- "type": "string",
- "enum": [
- "pending",
- "completed",
- "failed",
- "cancelled"
- ],
- "description": "转账状态"
- },
- "transfer_type": {
- "type": "string",
- "enum": [
- "user_to_user",
- "user_to_system",
- "system_to_user"
- ],
- "description": "转账类型"
- },
- "voucher_image": {
- "type": "string",
- "description": "转账凭证图片路径"
- },
- "remark": {
- "type": "string",
- "description": "转账备注"
- },
- "created_at": {
- "type": "string",
- "format": "date-time",
- "description": "创建时间"
- },
- "updated_at": {
- "type": "string",
- "format": "date-time",
- "description": "更新时间"
- }
- }
- },
- "Pagination": {
- "type": "object",
- "properties": {
- "total": {
- "type": "integer",
- "description": "总记录数"
- },
- "page": {
- "type": "integer",
- "description": "当前页码"
- },
- "limit": {
- "type": "integer",
- "description": "每页记录数"
- },
- "total_pages": {
- "type": "integer",
- "description": "总页数"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "paths": {
- "/api/announcements/{id}": {
- "get": {
- "summary": "获取单个公告详情",
- "tags": [
- "Announcements"
- ],
- "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": {
- "$ref": "#/components/schemas/Announcement"
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "公告不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- },
- "put": {
- "summary": "更新通知公告",
- "tags": [
- "Announcements"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "id",
- "required": true,
- "schema": {
- "type": "integer"
- },
- "description": "公告ID"
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "title": {
- "type": "string",
- "description": "公告标题"
- },
- "content": {
- "type": "string",
- "description": "公告内容"
- },
- "type": {
- "type": "string",
- "enum": [
- "system",
- "activity",
- "maintenance",
- "urgent"
- ],
- "description": "公告类型"
- },
- "priority": {
- "type": "string",
- "enum": [
- "high",
- "medium",
- "low",
- "urgent"
- ],
- "description": "优先级"
- },
- "status": {
- "type": "string",
- "enum": [
- "draft",
- "published",
- "expired"
- ],
- "description": "状态"
- },
- "is_pinned": {
- "type": "boolean"
- },
- "publish_time": {
- "type": "string",
- "format": "date-time"
- },
- "expire_time": {
- "type": "string",
- "format": "date-time"
- },
- "isTop": {
- "type": "boolean",
- "description": "是否置顶"
- },
- "publishTime": {
- "type": "string",
- "format": "date-time",
- "description": "发布时间"
- },
- "expireTime": {
- "type": "string",
- "format": "date-time",
- "description": "过期时间"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "公告更新成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "公告更新成功"
- },
- "data": {
- "$ref": "#/components/schemas/Announcement"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "公告不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- },
- "delete": {
- "summary": "删除通知公告",
- "tags": [
- "Announcements"
- ],
- "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",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "公告删除成功"
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "公告不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/announcements/{id}/read": {
- "post": {
- "summary": "标记公告为已读",
- "tags": [
- "Announcements"
- ],
- "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",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "已标记为已读"
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "公告不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/announcements/unread/count": {
- "get": {
- "summary": "获取用户未读公告数量",
- "tags": [
- "Announcements"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "获取未读数量成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "unread_count": {
- "type": "integer",
- "example": 5,
- "description": "未读公告数量"
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/announcements/batch/read": {
- "post": {
- "summary": "批量标记公告为已读",
- "tags": [
- "Announcements"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "announcement_ids"
- ],
- "properties": {
- "announcement_ids": {
- "type": "array",
- "items": {
- "type": "integer"
- },
- "example": [
- 1,
- 2,
- 3
- ],
- "description": "公告ID列表"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "批量标记已读成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "批量标记已读成功"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/announcements": {
- "post": {
- "summary": "创建新的通知公告",
- "tags": [
- "Announcements"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "title",
- "content",
- "type",
- "priority"
- ],
- "properties": {
- "title": {
- "type": "string",
- "description": "公告标题",
- "example": "系统维护通知"
- },
- "content": {
- "type": "string",
- "description": "公告内容",
- "example": "系统将于今晚进行维护,预计维护时间2小时"
- },
- "type": {
- "type": "string",
- "enum": [
- "system",
- "activity",
- "maintenance",
- "urgent"
- ],
- "default": "system",
- "description": "公告类型",
- "example": "maintenance"
- },
- "priority": {
- "type": "string",
- "enum": [
- "high",
- "medium",
- "low",
- "urgent"
- ],
- "default": "medium",
- "description": "优先级",
- "example": "high"
- },
- "status": {
- "type": "string",
- "enum": [
- "draft",
- "published"
- ],
- "default": "draft",
- "description": "状态"
- },
- "is_pinned": {
- "type": "boolean",
- "default": false
- },
- "publish_time": {
- "type": "string",
- "format": "date-time"
- },
- "expire_time": {
- "type": "string",
- "format": "date-time"
- },
- "isTop": {
- "type": "boolean",
- "default": false,
- "description": "是否置顶"
- },
- "publishTime": {
- "type": "string",
- "format": "date-time",
- "description": "发布时间"
- },
- "expireTime": {
- "type": "string",
- "format": "date-time",
- "description": "过期时间"
- }
- }
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "公告创建成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "公告创建成功"
- },
- "data": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer"
- }
- },
- "$ref": "#/components/schemas/Announcement"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- },
- "get": {
- "summary": "获取通知公告列表",
- "tags": [
- "Announcements"
- ],
- "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": "type",
- "schema": {
- "type": "string",
- "enum": [
- "system",
- "activity",
- "maintenance",
- "urgent"
- ]
- },
- "description": "公告类型"
- },
- {
- "in": "query",
- "name": "priority",
- "schema": {
- "type": "string",
- "enum": [
- "high",
- "medium",
- "low"
- ]
- },
- "description": "优先级"
- },
- {
- "in": "query",
- "name": "status",
- "schema": {
- "type": "string",
- "enum": [
- "draft",
- "published",
- "expired"
- ]
- },
- "description": "状态"
- },
- {
- "in": "query",
- "name": "isTop",
- "schema": {
- "type": "boolean"
- },
- "description": "是否置顶"
- },
- {
- "in": "query",
- "name": "sortBy",
- "schema": {
- "type": "string",
- "enum": [
- "created_at",
- "updated_at",
- "publish_time",
- "priority"
- ],
- "default": "created_at"
- },
- "description": "排序字段"
- },
- {
- "in": "query",
- "name": "sortOrder",
- "schema": {
- "type": "string",
- "enum": [
- "ASC",
- "DESC"
- ],
- "default": "DESC"
- },
- "description": "排序方向"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取公告列表",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "announcements": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Announcement"
- }
- },
- "total": {
- "type": "integer",
- "example": 50
- },
- "page": {
- "type": "integer",
- "example": 1
- },
- "limit": {
- "type": "integer",
- "example": 10
- },
- "totalPages": {
- "type": "integer",
- "example": 5
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/announcements/public/list": {
- "get": {
- "summary": "获取公开发布的公告列表(无需认证)",
- "tags": [
- "Announcements"
- ],
- "parameters": [
- {
- "in": "query",
- "name": "limit",
- "schema": {
- "type": "integer",
- "default": 5
- },
- "description": "获取数量"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取公开公告列表",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Announcement"
- }
- }
- }
- }
- }
- }
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/announcements/{id}/toggle-top": {
- "put": {
- "summary": "切换公告置顶状态",
- "tags": [
- "Announcements"
- ],
- "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",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "置顶状态更新成功"
- },
- "data": {
- "type": "object",
- "properties": {
- "isTop": {
- "type": "boolean",
- "example": true
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "公告不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/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"
- },
- "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": "服务器错误"
- }
- }
- }
- },
- "/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": "服务器错误"
- }
- }
- }
- },
- "/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": "服务器错误"
- }
- }
- }
- },
- "/api/orders/{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": {
- "order": {
- "$ref": "#/components/schemas/Order"
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "订单不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/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"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "/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"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "/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"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "/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",
- "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": "商品不存在"
- }
- }
- }
- },
- "/addresses": {
- "get": {
- "summary": "获取用户收货地址列表",
- "tags": [
- "Addresses"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取地址列表",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Address"
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- },
- "post": {
- "summary": "创建收货地址",
- "tags": [
- "Addresses"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "recipient_name": {
- "type": "string",
- "description": "收件人姓名"
- },
- "phone": {
- "type": "string",
- "description": "联系电话"
- },
- "province_code": {
- "type": "string",
- "description": "省份编码"
- },
- "province_name": {
- "type": "string",
- "description": "省份名称"
- },
- "city_code": {
- "type": "string",
- "description": "城市编码"
- },
- "city_name": {
- "type": "string",
- "description": "城市名称"
- },
- "district_code": {
- "type": "string",
- "description": "区县编码"
- },
- "district_name": {
- "type": "string",
- "description": "区县名称"
- },
- "detailed_address": {
- "type": "string",
- "description": "详细地址"
- },
- "postal_code": {
- "type": "string",
- "description": "邮政编码"
- },
- "label_id": {
- "type": "integer",
- "description": "地址标签ID"
- },
- "is_default": {
- "type": "boolean",
- "description": "是否为默认地址"
- }
- },
- "required": [
- "recipient_name",
- "phone",
- "province_code",
- "province_name",
- "city_code",
- "city_name",
- "district_code",
- "district_name",
- "detailed_address"
- ]
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "地址创建成功"
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/addresses/{id}": {
- "get": {
- "summary": "获取单个收货地址详情",
- "tags": [
- "Addresses"
- ],
- "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": {
- "$ref": "#/components/schemas/Address"
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "地址不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- },
- "put": {
- "summary": "更新收货地址",
- "tags": [
- "Addresses"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "id",
- "required": true,
- "schema": {
- "type": "integer"
- },
- "description": "地址ID"
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "recipient_name": {
- "type": "string",
- "description": "收件人姓名"
- },
- "phone": {
- "type": "string",
- "description": "联系电话"
- },
- "province_code": {
- "type": "string",
- "description": "省份编码"
- },
- "province_name": {
- "type": "string",
- "description": "省份名称"
- },
- "city_code": {
- "type": "string",
- "description": "城市编码"
- },
- "city_name": {
- "type": "string",
- "description": "城市名称"
- },
- "district_code": {
- "type": "string",
- "description": "区县编码"
- },
- "district_name": {
- "type": "string",
- "description": "区县名称"
- },
- "detailed_address": {
- "type": "string",
- "description": "详细地址"
- },
- "postal_code": {
- "type": "string",
- "description": "邮政编码"
- },
- "label_id": {
- "type": "integer",
- "description": "地址标签ID"
- },
- "is_default": {
- "type": "boolean",
- "description": "是否为默认地址"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "地址更新成功"
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "地址不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- },
- "delete": {
- "summary": "删除收货地址",
- "tags": [
- "Addresses"
- ],
- "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",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "收货地址删除成功"
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "地址不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/addresses/{id}/default": {
- "put": {
- "summary": "设置默认地址",
- "tags": [
- "Addresses"
- ],
- "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",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "默认地址设置成功"
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "地址不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/auth/register": {
- "post": {
- "summary": "用户注册",
- "description": "需要提供有效的激活码才能注册",
- "tags": [
- "Authentication"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/RegisterRequest"
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "用户注册成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "message": {
- "type": "string"
- },
- "token": {
- "type": "string",
- "description": "JWT认证令牌"
- },
- "user": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer"
- },
- "username": {
- "type": "string"
- },
- "role": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/auth/login": {
- "post": {
- "summary": "用户登录",
- "tags": [
- "Authentication"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/LoginCredentials"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "登录成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "message": {
- "type": "string"
- },
- "token": {
- "type": "string",
- "description": "JWT认证令牌"
- },
- "user": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer"
- },
- "username": {
- "type": "string"
- },
- "role": {
- "type": "string"
- },
- "avatar": {
- "type": "string"
- },
- "points": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "用户名或密码错误"
- },
- "403": {
- "description": "账户审核未通过"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/captcha/generate": {
- "get": {
- "summary": "生成图形验证码",
- "tags": [
- "Captcha"
- ],
- "responses": {
- "200": {
- "description": "成功生成验证码",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "captchaId": {
- "type": "string",
- "description": "验证码唯一ID"
- },
- "image": {
- "type": "string",
- "description": "Base64编码的SVG验证码图片"
- }
- }
- }
- }
- }
- }
- }
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/captcha/verify": {
- "post": {
- "summary": "验证用户输入的验证码",
- "tags": [
- "Captcha"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "captchaId",
- "captchaText"
- ],
- "properties": {
- "captchaId": {
- "type": "string",
- "description": "验证码唯一ID"
- },
- "captchaText": {
- "type": "string",
- "description": "用户输入的验证码"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "验证码验证成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "验证码验证成功"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "验证码错误或已过期",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": false
- },
- "message": {
- "type": "string",
- "example": "验证码错误"
- }
- }
- }
- }
- }
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/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": "创建匹配订单",
- "tags": [
- "Matching"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "matchingType": {
- "type": "string",
- "enum": [
- "small",
- "large"
- ],
- "default": "small",
- "description": "匹配类型(小额或大额)"
- },
- "customAmount": {
- "type": "number",
- "description": "大额匹配时的自定义金额(5000-50000之间)"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "匹配订单创建成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "message": {
- "type": "string"
- },
- "data": {
- "type": "object",
- "properties": {
- "matchingOrderId": {
- "type": "integer"
- },
- "amounts": {
- "type": "array",
- "items": {
- "type": "number"
- }
- },
- "matchingType": {
- "type": "string"
- },
- "totalAmount": {
- "type": "number"
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "参数错误或用户未满足匹配条件"
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "用户不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching/my-orders": {
- "get": {
- "summary": "获取用户的匹配订单列表",
- "tags": [
- "Matching"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "query",
- "name": "page",
- "schema": {
- "type": "integer",
- "default": 1
- },
- "description": "页码"
- },
- {
- "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": "array",
- "items": {
- "$ref": "#/components/schemas/MatchingOrder"
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching/pending-allocations": {
- "get": {
- "summary": "获取用户待处理的分配",
- "tags": [
- "Matching"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取待处理分配",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Allocation"
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching/allocation/{id}": {
- "get": {
- "summary": "获取分配详情",
- "tags": [
- "Matching"
- ],
- "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": {
- "$ref": "#/components/schemas/Allocation"
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "无权限访问"
- },
- "404": {
- "description": "分配不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching/confirm-allocation/{allocationId}": {
- "post": {
- "summary": "确认分配(创建转账)",
- "tags": [
- "Matching"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "allocationId",
- "required": true,
- "schema": {
- "type": "integer"
- },
- "description": "分配ID"
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "transferAmount": {
- "type": "number",
- "description": "转账金额"
- },
- "description": {
- "type": "string",
- "description": "转账描述"
- },
- "voucher": {
- "type": "string",
- "description": "转账凭证(图片URL)"
- }
- },
- "required": [
- "voucher"
- ]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "转账凭证提交成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "message": {
- "type": "string"
- },
- "data": {
- "type": "object",
- "properties": {
- "transferId": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching/reject-allocation/{allocationId}": {
- "post": {
- "summary": "拒绝分配",
- "tags": [
- "Matching"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "allocationId",
- "required": true,
- "schema": {
- "type": "integer"
- },
- "description": "分配ID"
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "reason": {
- "type": "string",
- "description": "拒绝原因"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "拒绝分配成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "message": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "分配不存在或无权限"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching/order/{orderId}": {
- "get": {
- "summary": "获取匹配订单详情",
- "tags": [
- "Matching"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "orderId",
- "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/MatchingOrder"
- },
- "allocations": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Allocation"
- }
- },
- "records": {
- "type": "array",
- "items": {
- "type": "object"
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "无权限查看"
- },
- "404": {
- "description": "订单不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching/stats": {
- "get": {
- "summary": "获取匹配统计信息",
- "tags": [
- "Matching"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取统计信息",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "object",
- "properties": {
- "userStats": {
- "type": "object",
- "properties": {
- "initiated_orders": {
- "type": "integer"
- },
- "participated_allocations": {
- "type": "integer"
- },
- "total_initiated_amount": {
- "type": "number"
- },
- "total_participated_amount": {
- "type": "number"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching-admin/unreasonable-matches": {
- "get": {
- "summary": "获取不合理的匹配记录(正余额用户被匹配的情况)",
- "tags": [
- "MatchingAdmin"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "query",
- "name": "page",
- "schema": {
- "type": "integer",
- "default": 1
- },
- "description": "页码"
- },
- {
- "in": "query",
- "name": "limit",
- "schema": {
- "type": "integer",
- "default": 20
- },
- "description": "每页数量"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取不合理匹配记录",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "object",
- "properties": {
- "matches": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/UnreasonableMatch"
- }
- },
- "pagination": {
- "type": "object",
- "properties": {
- "page": {
- "type": "integer"
- },
- "limit": {
- "type": "integer"
- },
- "total": {
- "type": "integer"
- },
- "totalPages": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "无管理员权限"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching-admin/fix-unreasonable-match/{allocationId}": {
- "post": {
- "summary": "修复不合理的匹配记录",
- "tags": [
- "MatchingAdmin"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "allocationId",
- "required": true,
- "schema": {
- "type": "integer"
- },
- "description": "分配ID"
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "action": {
- "type": "string",
- "enum": [
- "cancel",
- "reassign"
- ],
- "description": "修复操作类型(取消或重新分配)"
- }
- },
- "required": [
- "action"
- ]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "修复成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "message": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "参数错误或无需修复"
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "无管理员权限"
- },
- "404": {
- "description": "分配记录不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching-admin/matching-stats": {
- "get": {
- "summary": "获取匹配统计信息",
- "tags": [
- "MatchingAdmin"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取匹配统计信息",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "object",
- "properties": {
- "currentStats": {
- "type": "object",
- "properties": {
- "unreasonable_matches": {
- "type": "integer"
- },
- "reasonable_matches": {
- "type": "integer"
- },
- "system_matches": {
- "type": "integer"
- },
- "unreasonable_amount": {
- "type": "number"
- },
- "reasonable_amount": {
- "type": "number"
- }
- }
- },
- "yesterdayStats": {
- "type": "object",
- "properties": {
- "total_outbound": {
- "type": "number"
- },
- "unique_amounts": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "无管理员权限"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching-admin/fix-all-unreasonable": {
- "post": {
- "summary": "批量修复所有不合理匹配",
- "tags": [
- "MatchingAdmin"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "批量修复完成",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "message": {
- "type": "string"
- },
- "data": {
- "type": "object",
- "properties": {
- "fixedCount": {
- "type": "integer",
- "description": "成功修复的记录数"
- },
- "errorCount": {
- "type": "integer",
- "description": "修复失败的记录数"
- },
- "errors": {
- "type": "array",
- "items": {
- "type": "string"
- },
- "description": "错误信息列表(最多10条)"
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "无管理员权限"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching-admin/confirm-allocation/{allocationId}": {
- "post": {
- "summary": "管理员确认分配",
- "tags": [
- "MatchingAdmin"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "allocationId",
- "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": "未授权"
- },
- "403": {
- "description": "无管理员权限"
- },
- "404": {
- "description": "分配不存在或状态不是待处理"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/matching-admin/cancel-allocation/{allocationId}": {
- "post": {
- "summary": "管理员取消分配",
- "tags": [
- "MatchingAdmin"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "allocationId",
- "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": "未授权"
- },
- "403": {
- "description": "无管理员权限"
- },
- "404": {
- "description": "分配不存在或状态不是待处理"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/orders/{id}/cancel": {
- "put": {
- "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"
- },
- "message": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "只能取消待处理的订单"
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "订单不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/orders/{id}/confirm": {
- "put": {
- "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"
- },
- "message": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "只能确认已发货的订单"
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "订单不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/orders/{id}/status": {
- "put": {
- "summary": "更新订单状态(管理员)",
- "tags": [
- "Orders"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "id",
- "required": true,
- "schema": {
- "type": "integer"
- },
- "description": "订单ID"
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "status": {
- "type": "string",
- "enum": [
- "pending",
- "shipped",
- "completed",
- "cancelled"
- ],
- "description": "订单状态"
- }
- },
- "required": [
- "status"
- ]
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "订单状态更新成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "message": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "无效的订单状态"
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "无管理员权限"
- },
- "404": {
- "description": "订单不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/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": "获取订单统计信息(管理员权限)",
- "tags": [
- "Orders"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取订单统计信息",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "object",
- "properties": {
- "totalOrders": {
- "type": "integer",
- "description": "总订单数"
- },
- "pendingOrders": {
- "type": "integer",
- "description": "待发货订单数"
- },
- "completedOrders": {
- "type": "integer",
- "description": "已完成订单数"
- },
- "monthOrders": {
- "type": "integer",
- "description": "本月新增订单数"
- },
- "monthGrowthRate": {
- "type": "number",
- "description": "月增长率"
- },
- "totalPointsConsumed": {
- "type": "number",
- "description": "总积分消费"
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "无管理员权限"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/points/balance": {
- "get": {
- "summary": "获取用户当前积分余额",
- "tags": [
- "Points"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取积分余额",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "points": {
- "type": "integer",
- "description": "用户当前积分"
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权,需要登录"
- },
- "404": {
- "description": "用户不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/points/history": {
- "get": {
- "summary": "获取用户积分历史记录",
- "tags": [
- "Points"
- ],
- "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": "type",
- "schema": {
- "type": "string",
- "enum": [
- "earn",
- "spend",
- "admin_adjust"
- ]
- },
- "description": "积分变动类型"
- },
- {
- "in": "query",
- "name": "username",
- "schema": {
- "type": "string"
- },
- "description": "用户名(仅管理员可用)"
- },
- {
- "in": "query",
- "name": "change",
- "schema": {
- "type": "string",
- "enum": [
- "positive",
- "negative"
- ]
- },
- "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",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "records": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/PointsHistory"
- }
- },
- "pagination": {
- "type": "object",
- "properties": {
- "page": {
- "type": "integer"
- },
- "limit": {
- "type": "integer"
- },
- "total": {
- "type": "integer"
- },
- "totalPages": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权,需要登录"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/points/adjust": {
- "post": {
- "summary": "管理员调整用户积分",
- "tags": [
- "Points"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "userId",
- "points",
- "reason"
- ],
- "properties": {
- "userId": {
- "type": "integer",
- "description": "用户ID"
- },
- "points": {
- "type": "integer",
- "description": "调整的积分数量(正数为增加,负数为减少)"
- },
- "reason": {
- "type": "string",
- "description": "调整原因"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "积分调整成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "积分调整成功"
- },
- "data": {
- "type": "object",
- "properties": {
- "userId": {
- "type": "integer"
- },
- "pointsChanged": {
- "type": "integer"
- },
- "newBalance": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "参数错误或积分不足"
- },
- "401": {
- "description": "未授权,需要管理员权限"
- },
- "404": {
- "description": "用户不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/points/recharge": {
- "post": {
- "summary": "管理员给用户充值积分",
- "tags": [
- "Points"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "user_id",
- "points"
- ],
- "properties": {
- "user_id": {
- "type": "integer",
- "description": "用户ID"
- },
- "points": {
- "type": "integer",
- "description": "充值的积分数量(必须为正数)"
- },
- "description": {
- "type": "string",
- "description": "充值描述",
- "default": "管理员充值"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "积分充值成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "积分充值成功"
- },
- "data": {
- "type": "object",
- "properties": {
- "userId": {
- "type": "integer"
- },
- "pointsAdded": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "参数错误"
- },
- "401": {
- "description": "未授权,需要管理员权限"
- },
- "404": {
- "description": "用户不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/points/leaderboard": {
- "get": {
- "summary": "获取积分排行榜",
- "tags": [
- "Points"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "query",
- "name": "limit",
- "schema": {
- "type": "integer",
- "default": 10
- },
- "description": "返回的排行榜数量"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取积分排行榜",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "leaderboard": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "rank": {
- "type": "integer"
- },
- "userId": {
- "type": "integer"
- },
- "username": {
- "type": "string"
- },
- "points": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权,需要登录"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/points/stats": {
- "get": {
- "summary": "获取积分统计信息(管理员权限)",
- "tags": [
- "Points"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取积分统计信息",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "stats": {
- "type": "object",
- "properties": {
- "totalPoints": {
- "type": "integer",
- "description": "系统中总积分数量"
- },
- "totalEarned": {
- "type": "integer",
- "description": "总积分发放量"
- },
- "totalSpent": {
- "type": "integer",
- "description": "总积分消费量"
- },
- "activeUsers": {
- "type": "integer",
- "description": "活跃用户数"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权,需要管理员权限"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/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"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "/regions/zhejiang": {
- "get": {
- "summary": "获取浙江省所有地区数据",
- "tags": [
- "Regions"
- ],
- "responses": {
- "200": {
- "description": "成功获取浙江省地区数据",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/ZhejiangRegion"
- }
- },
- "message": {
- "type": "string",
- "example": "获取地区数据成功"
- }
- }
- }
- }
- }
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/regions/provinces": {
- "get": {
- "summary": "获取所有省份",
- "tags": [
- "Regions"
- ],
- "responses": {
- "200": {
- "description": "成功获取省份列表",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Region"
- }
- }
- }
- }
- }
- }
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/regions/cities/{provinceCode}": {
- "get": {
- "summary": "根据省份代码获取城市列表",
- "tags": [
- "Regions"
- ],
- "parameters": [
- {
- "in": "path",
- "name": "provinceCode",
- "required": true,
- "schema": {
- "type": "string"
- },
- "description": "省份代码"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取城市列表",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Region"
- }
- }
- }
- }
- }
- }
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/regions/districts/{cityCode}": {
- "get": {
- "summary": "根据城市代码获取区县列表",
- "tags": [
- "Regions"
- ],
- "parameters": [
- {
- "in": "path",
- "name": "cityCode",
- "required": true,
- "schema": {
- "type": "string"
- },
- "description": "城市代码"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取区县列表",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Region"
- }
- }
- }
- }
- }
- }
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/regions/path/{regionCode}": {
- "get": {
- "summary": "根据区域代码获取完整路径(省-市-区)",
- "tags": [
- "Regions"
- ],
- "parameters": [
- {
- "in": "path",
- "name": "regionCode",
- "required": true,
- "schema": {
- "type": "string"
- },
- "description": "区域代码"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取区域完整路径",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "province": {
- "$ref": "#/components/schemas/Region"
- },
- "city": {
- "$ref": "#/components/schemas/Region"
- },
- "district": {
- "$ref": "#/components/schemas/Region"
- }
- }
- }
- }
- }
- }
- }
- },
- "404": {
- "description": "区域不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/risk-management/users": {
- "get": {
- "summary": "获取风险用户列表",
- "tags": [
- "RiskManagement"
- ],
- "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": "is_blacklisted",
- "schema": {
- "type": "integer",
- "enum": [
- 0,
- 1
- ]
- },
- "description": "是否被拉黑"
- },
- {
- "in": "query",
- "name": "username",
- "schema": {
- "type": "string"
- },
- "description": "用户名"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取风险用户列表",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "object",
- "properties": {
- "users": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer"
- },
- "username": {
- "type": "string"
- },
- "real_name": {
- "type": "string"
- },
- "is_blacklisted": {
- "type": "boolean"
- },
- "blacklist_reason": {
- "type": "string"
- },
- "blacklisted_at": {
- "type": "string",
- "format": "date-time"
- }
- }
- }
- },
- "pagination": {
- "type": "object",
- "properties": {
- "total": {
- "type": "integer"
- },
- "page": {
- "type": "integer"
- },
- "limit": {
- "type": "integer"
- },
- "pages": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "权限不足"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/risk-management/blacklist/{userId}": {
- "post": {
- "summary": "拉黑用户",
- "tags": [
- "RiskManagement"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "userId",
- "schema": {
- "type": "integer"
- },
- "required": true,
- "description": "用户ID"
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "reason"
- ],
- "properties": {
- "reason": {
- "type": "string",
- "description": "拉黑原因"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "用户已被拉黑",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "用户已被拉黑"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "权限不足"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/risk-management/unblacklist/{userId}": {
- "post": {
- "summary": "解除拉黑",
- "tags": [
- "RiskManagement"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "path",
- "name": "userId",
- "schema": {
- "type": "integer"
- },
- "required": true,
- "description": "用户ID"
- }
- ],
- "responses": {
- "200": {
- "description": "已解除拉黑",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "已解除拉黑"
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "权限不足"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/risk-management/overdue-transfers": {
- "get": {
- "summary": "获取超时转账列表",
- "tags": [
- "RiskManagement"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "query",
- "name": "page",
- "schema": {
- "type": "integer",
- "default": 1
- },
- "description": "页码"
- },
- {
- "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": {
- "transfers": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer"
- },
- "user_id": {
- "type": "integer"
- },
- "recipient_id": {
- "type": "integer"
- },
- "amount": {
- "type": "number"
- },
- "status": {
- "type": "string"
- },
- "created_at": {
- "type": "string",
- "format": "date-time"
- },
- "username": {
- "type": "string"
- },
- "recipient_name": {
- "type": "string"
- },
- "overdue_hours": {
- "type": "number"
- }
- }
- }
- },
- "pagination": {
- "type": "object",
- "properties": {
- "total": {
- "type": "integer"
- },
- "page": {
- "type": "integer"
- },
- "limit": {
- "type": "integer"
- },
- "pages": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "权限不足"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/sms/send": {
- "post": {
- "summary": "发送短信验证码",
- "tags": [
- "SMS"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "phone"
- ],
- "properties": {
- "phone": {
- "type": "string",
- "description": "手机号码"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "验证码发送成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "验证码发送成功"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "参数错误或发送频率限制"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/api/sms/verify": {
- "post": {
- "summary": "验证短信验证码",
- "tags": [
- "SMS"
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "phone",
- "code"
- ],
- "properties": {
- "phone": {
- "type": "string",
- "description": "手机号码"
- },
- "code": {
- "type": "string",
- "description": "验证码"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "验证码验证成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "手机号验证成功"
- },
- "data": {
- "type": "object",
- "properties": {
- "phone": {
- "type": "string"
- },
- "verified": {
- "type": "boolean"
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "参数错误或验证码错误"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/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": "获取转账列表",
- "tags": [
- "Transfers"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "query",
- "name": "status",
- "schema": {
- "type": "string"
- },
- "description": "转账状态过滤"
- },
- {
- "in": "query",
- "name": "transfer_type",
- "schema": {
- "type": "string"
- },
- "description": "转账类型过滤"
- },
- {
- "in": "query",
- "name": "start_date",
- "schema": {
- "type": "string",
- "format": "date"
- },
- "description": "开始日期过滤"
- },
- {
- "in": "query",
- "name": "end_date",
- "schema": {
- "type": "string",
- "format": "date"
- },
- "description": "结束日期过滤"
- },
- {
- "in": "query",
- "name": "search",
- "schema": {
- "type": "string"
- },
- "description": "搜索关键词(用户名或真实姓名)"
- },
- {
- "in": "query",
- "name": "page",
- "schema": {
- "type": "integer",
- "default": 1
- },
- "description": "页码"
- },
- {
- "in": "query",
- "name": "limit",
- "schema": {
- "type": "integer",
- "default": 10
- },
- "description": "每页数量"
- },
- {
- "in": "query",
- "name": "sort",
- "schema": {
- "type": "string"
- },
- "description": "排序字段"
- },
- {
- "in": "query",
- "name": "order",
- "schema": {
- "type": "string",
- "enum": [
- "asc",
- "desc"
- ]
- },
- "description": "排序方向"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取转账列表",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "object",
- "properties": {
- "transfers": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Transfer"
- }
- },
- "pagination": {
- "$ref": "#/components/schemas/Pagination"
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/transfers/list": {
- "get": {
- "summary": "获取转账记录列表",
- "tags": [
- "Transfers"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "query",
- "name": "status",
- "schema": {
- "type": "string"
- },
- "description": "转账状态过滤"
- },
- {
- "in": "query",
- "name": "transfer_type",
- "schema": {
- "type": "string"
- },
- "description": "转账类型过滤"
- },
- {
- "in": "query",
- "name": "start_date",
- "schema": {
- "type": "string",
- "format": "date"
- },
- "description": "开始日期过滤"
- },
- {
- "in": "query",
- "name": "end_date",
- "schema": {
- "type": "string",
- "format": "date"
- },
- "description": "结束日期过滤"
- },
- {
- "in": "query",
- "name": "page",
- "schema": {
- "type": "integer",
- "default": 1
- },
- "description": "页码"
- },
- {
- "in": "query",
- "name": "limit",
- "schema": {
- "type": "integer",
- "default": 10
- },
- "description": "每页数量"
- },
- {
- "in": "query",
- "name": "sort",
- "schema": {
- "type": "string"
- },
- "description": "排序字段"
- },
- {
- "in": "query",
- "name": "order",
- "schema": {
- "type": "string",
- "enum": [
- "asc",
- "desc"
- ]
- },
- "description": "排序方向"
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取转账记录列表",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "data": {
- "type": "object",
- "properties": {
- "transfers": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Transfer"
- }
- },
- "pagination": {
- "$ref": "#/components/schemas/Pagination"
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/transfers/public-account": {
- "get": {
- "summary": "获取公户信息",
- "tags": [
- "Transfers"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "responses": {
- "200": {
- "description": "成功获取公户信息",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "data": {
- "type": "object",
- "properties": {
- "id": {
- "type": "integer",
- "description": "公户ID"
- },
- "username": {
- "type": "string",
- "description": "公户用户名",
- "example": "public_account"
- },
- "real_name": {
- "type": "string",
- "description": "公户名称"
- },
- "balance": {
- "type": "number",
- "format": "float",
- "description": "公户余额"
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "404": {
- "description": "公户不存在"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/transfers/create": {
- "post": {
- "summary": "创建转账记录",
- "tags": [
- "Transfers"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "to_user_id",
- "amount",
- "transfer_type"
- ],
- "properties": {
- "to_user_id": {
- "type": "integer",
- "description": "接收方用户ID"
- },
- "amount": {
- "type": "number",
- "format": "float",
- "description": "转账金额"
- },
- "transfer_type": {
- "type": "string",
- "enum": [
- "user_to_user",
- "user_to_system",
- "system_to_user"
- ],
- "description": "转账类型"
- },
- "remark": {
- "type": "string",
- "description": "转账备注"
- }
- }
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "转账记录创建成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "转账记录创建成功,等待确认"
- },
- "data": {
- "type": "object",
- "properties": {
- "transfer_id": {
- "type": "integer",
- "description": "转账记录ID"
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/transfers/admin/create": {
- "post": {
- "summary": "管理员创建转账记录",
- "tags": [
- "Transfers"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "from_user_id",
- "to_user_id",
- "amount",
- "transfer_type"
- ],
- "properties": {
- "from_user_id": {
- "type": "integer",
- "description": "发送方用户ID"
- },
- "to_user_id": {
- "type": "integer",
- "description": "接收方用户ID"
- },
- "amount": {
- "type": "number",
- "format": "float",
- "description": "转账金额"
- },
- "transfer_type": {
- "type": "string",
- "enum": [
- "user_to_user",
- "user_to_system",
- "system_to_user"
- ],
- "description": "转账类型"
- },
- "description": {
- "type": "string",
- "description": "转账描述"
- }
- }
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "转账记录创建成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "转账记录创建成功"
- },
- "data": {
- "type": "object",
- "properties": {
- "transfer_id": {
- "type": "integer",
- "description": "转账记录ID"
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "权限不足"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/upload/image": {
- "post": {
- "summary": "上传图片",
- "tags": [
- "Upload"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "multipart/form-data": {
- "schema": {
- "type": "object",
- "properties": {
- "file": {
- "type": "string",
- "format": "binary",
- "description": "要上传的图片文件"
- },
- "type": {
- "type": "string",
- "enum": [
- "avatar",
- "product",
- "document"
- ],
- "default": "document",
- "description": "上传文件类型"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "图片上传成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "url": {
- "type": "string",
- "description": "上传后的文件URL"
- },
- "filename": {
- "type": "string",
- "description": "上传后的文件名"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/upload": {
- "post": {
- "summary": "多文件上传接口 (支持MediaUpload组件)",
- "tags": [
- "Upload"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "multipart/form-data": {
- "schema": {
- "type": "object",
- "properties": {
- "files": {
- "type": "array",
- "items": {
- "type": "string",
- "format": "binary"
- },
- "description": "要上传的文件列表"
- },
- "type": {
- "type": "string",
- "enum": [
- "avatar",
- "product",
- "document"
- ],
- "default": "document",
- "description": "上传文件类型"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "文件上传成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "文件上传成功"
- },
- "data": {
- "type": "array",
- "items": {
- "type": "object",
- "properties": {
- "filename": {
- "type": "string"
- },
- "originalname": {
- "type": "string"
- },
- "mimetype": {
- "type": "string"
- },
- "size": {
- "type": "integer"
- },
- "path": {
- "type": "string"
- },
- "url": {
- "type": "string"
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/upload/single": {
- "post": {
- "summary": "单文件上传接口(兼容性接口)",
- "tags": [
- "Upload"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "multipart/form-data": {
- "schema": {
- "type": "object",
- "properties": {
- "file": {
- "type": "string",
- "format": "binary",
- "description": "要上传的文件"
- },
- "type": {
- "type": "string",
- "enum": [
- "avatar",
- "product",
- "document"
- ],
- "default": "document",
- "description": "上传文件类型"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "文件上传成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "example": true
- },
- "message": {
- "type": "string",
- "example": "文件上传成功"
- },
- "url": {
- "type": "string",
- "description": "上传后的文件URL"
- },
- "filename": {
- "type": "string",
- "description": "上传后的文件名"
- },
- "originalname": {
- "type": "string",
- "description": "原始文件名"
- },
- "size": {
- "type": "integer",
- "description": "文件大小"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/users": {
- "post": {
- "summary": "创建用户(管理员权限)",
- "tags": [
- "Users"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "requestBody": {
- "required": true,
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": [
- "username",
- "password",
- "real_name",
- "id_card"
- ],
- "properties": {
- "username": {
- "type": "string"
- },
- "password": {
- "type": "string"
- },
- "role": {
- "type": "string",
- "enum": [
- "user",
- "admin",
- "merchant"
- ],
- "default": "user"
- },
- "is_system_account": {
- "type": "boolean",
- "default": false
- },
- "real_name": {
- "type": "string"
- },
- "id_card": {
- "type": "string"
- },
- "wechat_qr": {
- "type": "string"
- },
- "alipay_qr": {
- "type": "string"
- },
- "bank_card": {
- "type": "string"
- },
- "unionpay_qr": {
- "type": "string"
- },
- "phone": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "responses": {
- "201": {
- "description": "用户创建成功",
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean"
- },
- "message": {
- "type": "string"
- },
- "user": {
- "$ref": "#/components/schemas/User"
- }
- }
- }
- }
- }
- },
- "400": {
- "description": "请求参数错误"
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "权限不足"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/users/pending-audit": {
- "get": {
- "summary": "获取待审核用户列表(管理员权限)",
- "tags": [
- "Users"
- ],
- "security": [
- {
- "bearerAuth": []
- }
- ],
- "parameters": [
- {
- "in": "query",
- "name": "page",
- "schema": {
- "type": "integer",
- "default": 1
- },
- "description": "页码"
- },
- {
- "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": {
- "users": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/User"
- }
- },
- "pagination": {
- "type": "object",
- "properties": {
- "page": {
- "type": "integer"
- },
- "limit": {
- "type": "integer"
- },
- "total": {
- "type": "integer"
- },
- "pages": {
- "type": "integer"
- }
- }
- }
- }
- }
- }
- }
- }
- }
- },
- "401": {
- "description": "未授权"
- },
- "403": {
- "description": "权限不足"
- },
- "500": {
- "description": "服务器错误"
- }
- }
- }
- },
- "/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": [
- {
- "name": "Announcements",
- "description": "通知公告管理API"
- },
- {
- "name": "Orders",
- "description": "订单管理API"
- },
- {
- "name": "Products",
- "description": "商品管理API"
- },
- {
- "name": "Authentication",
- "description": "用户认证API"
- },
- {
- "name": "Captcha",
- "description": "验证码API"
- },
- {
- "name": "Cart",
- "description": "购物车管理相关接口"
- },
- {
- "name": "Matching",
- "description": "匹配订单相关接口"
- },
- {
- "name": "MatchingAdmin",
- "description": "匹配订单管理员相关接口"
- },
- {
- "name": "Points",
- "description": "积分管理相关接口"
- },
- {
- "name": "Regions",
- "description": "地区数据API"
- },
- {
- "name": "RiskManagement",
- "description": "风险管理API"
- },
- {
- "name": "SMS",
- "description": "短信验证码相关接口"
- },
- {
- "name": "Upload",
- "description": "文件上传API"
- },
- {
- "name": "Users",
- "description": "用户管理API"
- }
- ]
-}
\ No newline at end of file
diff --git a/apifox-sync.js b/apifox-sync.js
deleted file mode 100644
index 9c93f17..0000000
--- a/apifox-sync.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * 自动导出Swagger文档到本地文件
- * 使用方法:
- * 1. 运行此脚本: node apifox-sync.js
- * 2. 手动将生成的swagger.json文件导入到Apifox
- */
-const fs = require('fs');
-const path = require('path');
-const swaggerSpecs = require('./swagger');
-
-// 确保输出目录存在
-const outputDir = path.join(__dirname, 'api-docs');
-if (!fs.existsSync(outputDir)) {
- fs.mkdirSync(outputDir, { recursive: true });
-}
-
-// 导出Swagger文档到JSON文件
-const outputPath = path.join(outputDir, 'swagger.json');
-fs.writeFileSync(outputPath, JSON.stringify(swaggerSpecs, null, 2));
-console.log(`Swagger文档已导出到: ${outputPath}`);
-console.log('请手动将此文件导入到Apifox:');
-console.log('1. 登录Apifox网页版');
-console.log('2. 打开您的项目');
-console.log('3. 点击"导入"按钮');
-console.log('4. 选择"导入OpenAPI(Swagger)"');
-console.log('5. 上传刚才生成的swagger.json文件');
-console.log('6. 选择导入方式(合并或覆盖)并完成导入');
\ No newline at end of file
diff --git a/cert/apiclient_cert.p12 b/cert/apiclient_cert.p12
deleted file mode 100644
index 425cddd..0000000
Binary files a/cert/apiclient_cert.p12 and /dev/null differ
diff --git a/cert/apiclient_cert.pem b/cert/apiclient_cert.pem
deleted file mode 100644
index ee924ac..0000000
--- a/cert/apiclient_cert.pem
+++ /dev/null
@@ -1,25 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEKDCCAxCgAwIBAgIUXysc+VIXyKce5hqbA2vg9d9uH7kwDQYJKoZIhvcNAQEL
-BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
-FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
-Q0EwHhcNMjUwOTAyMDg0NDA0WhcNMzAwOTAxMDg0NDA0WjCBgTETMBEGA1UEAwwK
-MTcyNjM3NzMzNjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMS0wKwYDVQQL
-DCTlroHms6Lngqzono3mrYbliJvnp5HmioDmnInpmZDlhazlj7gxCzAJBgNVBAYT
-AkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
-AQoCggEBAKncf/y0dT12nyCmnK5w3ECQgtoHrU32LqQH/N5SUbiJVbkys99Qq41D
-MO0539Ki2MBuOuDHnWOrgNbRhH4WMXS8qodTcWu39cgVlnmw/KtQJdyInrPynFYH
-lWO6boc1jfM4UEbEk90iaLM49uFo75+bCNGZVHOt6UMmTNFB9zHDfnhm+UaxNJOf
-p1z7sntmD3H98c4ghzcE5L51KL6A7JtW+cp0zTpOunj0T/drvmRoyQVzqZ4IlXga
-9pJ/6Un2fVNn8pZL804ldKumAf5KVKmqEmz64ydh3896MoAshT/UJIHDuduN+Jbk
-gROt9QiM68jR6CuqeRsV7EZ1OA/BLI8CAwEAAaOBuTCBtjAJBgNVHRMEAjAAMAsG
-A1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2Y2Eu
-aXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRC
-MDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJCMjdB
-OUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQCzyJZ/
-+rnvUd1kJ74KCb6kxAwM/bX8w5lhkdUkeyQxdmbUCXCrkOGJ8uRQMfiK93eeET4h
-KMrZywQHvL1E5WXQpUmQZVYj6eAxaUO+RoW8wPeWb5x/LbSXqQCrCNF6U+AvC6wj
-6haW8TK8egCLsjxPBXL9NjkxvcsIOIM8F6JKhMSAAjT7F1nkXthyxC50o/Mbox6l
-YfvVZ70gWuUR2e6o9Sob1tTq6YwSwDr5OMwJT8QpDEdjAbWLLtv7a+ApzfqWeTHP
-dmVawsaQnOzFaUPDtEGYSh23/eaTrJ9DHpgtubtU/CQsGos6QHD6huRYMmvw3Wml
-mFAVWux4DJ8lZXGZ
------END CERTIFICATE-----
diff --git a/cert/apiclient_key.pem b/cert/apiclient_key.pem
deleted file mode 100644
index e87a2d3..0000000
--- a/cert/apiclient_key.pem
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCp3H/8tHU9dp8g
-ppyucNxAkILaB61N9i6kB/zeUlG4iVW5MrPfUKuNQzDtOd/SotjAbjrgx51jq4DW
-0YR+FjF0vKqHU3Frt/XIFZZ5sPyrUCXciJ6z8pxWB5Vjum6HNY3zOFBGxJPdImiz
-OPbhaO+fmwjRmVRzrelDJkzRQfcxw354ZvlGsTSTn6dc+7J7Zg9x/fHOIIc3BOS+
-dSi+gOybVvnKdM06Trp49E/3a75kaMkFc6meCJV4GvaSf+lJ9n1TZ/KWS/NOJXSr
-pgH+SlSpqhJs+uMnYd/PejKALIU/1CSBw7nbjfiW5IETrfUIjOvI0egrqnkbFexG
-dTgPwSyPAgMBAAECggEAF5dGN0Sg285zv0ckj52hGV54retPCHrec22gkwf/zY9V
-VolSLfu4N8BTNT9KdKilTeSBTOKsW0FgfXVP32sZp0rkrDLMl9dOzWEiKviHvws8
-lupqkDdruw8Gknk8DI9FjbgOfiWjG51ByVJqB1hZn2Ma0HFpJz/KG8df99gfisuP
-3s4s/N8gChKRobNJzoFTVlEexVwM8vPZl7foxm9SEc0tbXyU2yB+pglVv/BkkLo5
-aZFNIC2TVnGX1WJlTTfuhng+MMeckSIPjJGH9c7RpgAChKGOBFOAK87/1+3tep47
-qp3i2WnceIxy2Y/wIT4cGWUAd0Ghi/kkIo8KqpagEQKBgQDfiwhBitymWc5QRr+R
-LM6+Yu/EWlpd4/ZrcIgX9S5Z/La4gs/fqyWyKp9r/KjyYKG5sEfArim3NenvQBac
-Vp99kNpjAl6SCSWFNeA7aY+zLDJBRHSaG4ovxWXk1ZYD2hBLKahzp7PCdcNvPICU
-60AapDMiGlfbcJDot1uP+9i16wKBgQDChiW9qroJGfZcpe5nkh38orPOp7or09mG
-perNOAXGghJMV7tc1Se+ttjcvlfK6hrQcxPCMccH/x0bmeZ/MaG1RZjZS2zAXqH5
-9huZtgIHc2idVS+j1t4HsszuIBp84c6ykCU+6NltW1LQFRZ9Y2HuA5HW/fgyhLMG
-RHpRdgfG7QKBgDd9P5NlcNgqOrhal3rl8Hv5+yJ2ezALQkPxLxcjWVolDQZIEmmn
-BjhvtBsOILHporuBMo51rQ05aNRmyDYOmpCEwHELSYZelt22Pe8BiRYkxmTFJVyL
-sYWiLmTbT92s55aAxLvQySJgMR8Pmatdqg/y6m5ws5ZZHt9lhGj9TxH5AoGAR8Rc
-WjyJxF/av9XMPlPvUkzoz76b9h2D7KR8G1im8NT+UUIw8xAFSNyG5/Ily8xRNkSu
-rn/U8YNSxuMh4h16jrltqgWkythfJCyDhFNdLkiK+Tj7iZP1eJuj9drMSvS4YLLD
-uxEHXsxJolGVaY9oCvswLESo9GJ29kH/atyEBAUCgYB+xm2NI9OpF+dSq5O8vMiW
-NtlD744WfsiBDzatQz1A9PAB6cyLi03zjW10+HCNZRxBSfI8au/sn9lOdcCjvSGk
-8tjZXMzL71jDhEYs4qTUaiKJ2YpbLNcLFeTy48hWeRpew7t7bunksx8zb+0W14M2
-zDmIHi+CSve59dOzoiYuNg==
------END PRIVATE KEY-----
diff --git a/cert/证书使用说明.txt b/cert/证书使用说明.txt
deleted file mode 100644
index 9a0aab1..0000000
--- a/cert/证书使用说明.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-欢迎使用微信支付!
-附件中的三份文件(证书pkcs12格式、证书pem格式、证书密钥pem格式),为接口中强制要求时需携带的证书文件。
-证书属于敏感信息,请妥善保管不要泄露和被他人复制。
-不同开发语言下的证书格式不同,以下为说明指引:
- 证书pkcs12格式(apiclient_cert.p12)
- 包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份
- 部分安全性要求较高的API需要使用该证书来确认您的调用身份
- windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户号(如:1900006031)
- 证书pem格式(apiclient_cert.pem)
- 从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制
- 部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
- 您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem
- 证书密钥pem格式(apiclient_key.pem)
- 从apiclient_cert.p12中导出密钥部分的文件,为pem格式
- 部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
- 您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem
-备注说明:
- 由于绝大部分操作系统已内置了微信支付服务器证书的根CA证书, 2018年3月6日后, 不再提供CA证书文件(rootca.pem)下载
\ No newline at end of file
diff --git a/certs/README.md b/certs/README.md
deleted file mode 100644
index bef908a..0000000
--- a/certs/README.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# 支付宝支付证书配置指南
-
-## 证书文件说明
-
-本目录需要包含以下证书文件,用于支付宝支付V3版本API的证书模式:
-
-1. `appCertPublicKey.crt` - 应用公钥证书(已存在)
-2. `alipayRootCert.crt` - 支付宝根证书(需下载)
-3. `alipayCertPublicKey_RSA2.crt` - 支付宝公钥证书(需下载)
-
-## 如何获取证书
-
-### 已有证书
-- `appCertPublicKey.crt` - 应用公钥证书已存在于本目录
-
-### 需要下载的证书
-请登录支付宝开放平台,从您的应用详情页面下载以下证书:
-
-1. **支付宝根证书**:下载后重命名为 `alipayRootCert.crt` 并放入本目录
-2. **支付宝公钥证书**:下载后重命名为 `alipayCertPublicKey_RSA2.crt` 并放入本目录
-
-## 证书下载步骤
-
-1. 登录[支付宝开放平台](https://open.alipay.com/)
-2. 进入「开发者中心」->「我的应用」
-3. 选择对应的应用
-4. 点击「开发设置」
-5. 在「接口加签方式」区域,选择「公钥证书」
-6. 下载「支付宝根证书」和「支付宝公钥证书」
-7. 将下载的证书放入本目录,并按上述名称重命名
-
-## 配置验证
-
-证书配置完成后,系统将使用证书模式进行支付宝API调用,支持V3版本的接口。
\ No newline at end of file
diff --git a/certs/alipay-private-key-test.pem b/certs/alipay-private-key-test.pem
deleted file mode 100644
index 8385c1e..0000000
--- a/certs/alipay-private-key-test.pem
+++ /dev/null
@@ -1 +0,0 @@
-MIIEowIBAAKCAQEAk3HgYh7dK97PBX4UhlsusYGI4bLL7EQak2JfOZUeoVJK144ENg6yF0t3eohiryiK/vpyuyqX3hP5jEQMQHM92Mor9mf/KPj6Dw5AAvNLEdEaDCLwKUByk8sN2M6Xibdh6X0nQ67wOe6kaDkOG5Efkr4rHJCBfwbINXuaxXnJUr1mLdzwp1XN09IBUcKt+EXOKfpwbIuU6JoxTMiz7hnRHDuVKTYqnvx9CdV5Ng1eqYv5OI4r9abiFttRUJ37+RXbvXzffJGa/HFjpyPp4MOuK8n24ZW4vdjSBlflzzRHTAuo3tSo56gy9UouY6XtT0C9sXE7CxHkHeupOO3Wzl6t9wIDAQABAoIBAFvPRNDUNxPiITZiU5V1oZWV+w3Or3vmzEWJs5G/cNsyyrd+DtE6RVqL+1GpKwm2TRaIDHjPBNGbzn+wv5BCMfiTqtw71X5Fyi8lyGYN6Gins3hrKPAG2VF91plxyppOPgGNlK1oeN0Z4/Wh0U8JBofxMhcPRBM8vd3PoYflVZ7WpeHaApdL4n0S0Z+HcaCtnhfbJDh2fTXNkl01e2nI1Rqatkqc/Giwc8Pg5SrTKBO2g6GhPbk9LUUPqat9+3jmbT1CI56HS4OVTWFbqCMFqQE/KSXNpuFYRRTCgWDQSPbmJEKyDQMinG+DdJj2IIMhD60qYut+OBb6XQVqtdlBnoECgYEA/fbCiFIZPZ6IU7hnn75Jj2JDc6XIqFAVrbofViL3HUyeZ1KSYkXtDdh42Cl8fBCiT0xyagA/fdHmFTTJBueKfoW3+sqFMm3vKe4spJCNLmSAP8gu60zTAslmiQ5zyq+UVd74BE1Y7HSLoAv3lZnwIJkhOgrUq2mH4+r73SPN+C8CgYEAlKB+s7RZW+h4NX0YGZFdQ1AQJI0uSYOFt6YVqhTIRhUSqM++Yy/giE8X0TRckqhhAsBD6SN05rB54o6CdSacVriALtD+vrVZBDt+abTmeh1BhRUkrYeC7wZK5/+5pni9Nr1GfNKe+U2a8rzKCUJg6BWW6qGUrHOiU+9lYhQ17LkCgYBudtiipuMntD8j+z/HceNZJKqmMOQYoczsJdrfgpHuApeb5YSajkPQE+psS49D/5A54cyaYsU7GwNzEeSmxiutYMhno5NQHhU7LcfpRJ7EIR7Pn2kZG+9kdOnOj6S58qkYuMU0Sdh18TOSR+JHBhA2faTANFnQvTRIZLtsstgyLQKBgQCBl8/icYbZFMJ8IS86W/2uC8mHlXKetweJMlABlU1rjkRO3ZVsdvqY4B4sVDPDzP2JoIuWZUwxOf+NBCXMcHYxR369U45MS2PqxNVc5ldwcsIGgIESre4E7L+zus7t0KlraW5kuGHVj01kCilAGZjVxL1qqKkyFUGdXkhQVL8QQQKBgCO6GkpCR5yBs181iPh+AdI3EZh70SEBHfZNNJFs5a4FvDhxZhvkwIG77FY9IVyBn/aZLrVHJXUVB/HGA1QeNhHSXvIkWGwsNQ04OZJu0uio+4D+yGxY5XQ4F6Ti9KPI9dbu9S7OZKfZCsj5nhm/kKB3MlExN2MKqoQIMGHDcI0k
\ No newline at end of file
diff --git a/certs/alipay-private-key.pem b/certs/alipay-private-key.pem
deleted file mode 100644
index a908ace..0000000
--- a/certs/alipay-private-key.pem
+++ /dev/null
@@ -1 +0,0 @@
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCV9dEIPimkwxhJXXy2cntNZqeZdV4JAdx7ZpnvbjV6OjeEZczksKFa6lgsYR6Wq6G/khEdVRyNW4RsepBRwVGDBxxUN0nB9uTtPti220PtXZIJycLY585nL/iaUjUt+Cyh2qsyIGQ0lGx9VFWU+x5stGCLgfZbHOaMo0S+49upEsX5p4MyRbc4zA/vf+xgmGm5f1YB1Nwm32JBhVRourmHhi82MLX3Hxwo6R/SIOrDIcUrELYC9XRC2LUWYJIsQ1QMtrE/0XVFmIB68gU9JqRTNXhLCyL19K3WUGvRXG9N5mTaZSBIYco7ZrtbTFHtIxSOYxqYTOtkPKVYxGn1I2WHAgMBAAECggEAEeaA6Dn7YJaFPKSzMVgjDc82LGRNMEgPmI9byq/eJFP0spIwThAjgqW8lreVHikoqqR19IlnWhxVh1luBsRLxZdAs3DSFhwxoXxCBDnSNvBXcWGrJ5csFTcttsYfYPhh44QlsVsaewhIlwFNIfaD4Df72ktOK/wcLSeEGkE6xEixfMcLawg6a8rkHbvJtppFdTSL2YBB4X+dnt3cYCxFYch7Q5+RF+Wqr++860B5ztBRSX8xmqEIDS/95tf09qDyhyrnE2a2wh/jT+BWzvILKNc8goNo29kACo8bE9p+rWCnG8YsL+4emlrxk6/OYzR5lG6xZNvXAmnY0LDKzQsqeQKBgQDTm/uQP82o0ez9YHP/sAvTVq7pIJlEnaZqI2jBvyGxf6fDTU5b0yfJF9VO5EoPVT9CHRJ7A0T4RUDhIAzo9fjrz3VjkXO/RZyOIHsptjvOFeKCiFGrHO8sKxC5i4IZIRwIB7A84DU7rVzQ5Kh3XRqi8srKl/DBXo1BloWU0HJetQKBgQC1axqVH/eIwZrYjSbXgSHwGYMhH0YmOIJEYavD6TmOp9QL5N1ax6B0r/BIVt7XAIRck8kueKY4LeRulq3azXIhBy5Qx6sp2Sb/SX3vrPrvA/ImoDQKR4CeL5zXQJkqbVgbbS//UTEWilKyQXNz13D4qbCqNBbLJaDirXqBT0ycywKBgCMvT2/XvAlzBlXHAOKl0gGM6z5mJjXrhK0nQBbfAeoykKF/rCTGgloEdXpNqSbNhNwoW1dK3t/tG/GS07K0m3QSJbGtkLJgD7zuF6yC2YTVzLjpk7LA99+/NWO0l6g4AiIvrRUiLpfCpqkxK/XU7EXl2uQ+yVBNuW0LayCoXCv1AoGAU/CJbSRMUO9baQTuStoJzODRBltFBtwwkdkrM0tPAU1v1E0BikZBXJwnLiFbm9k2ZOtQM3tJVUcOoYiASnOyccuzx1aLQKKj44yqg2Hi/QIzYWHQkk0BGq/m/sV52OKc2JvNkHGNp+M6XhXgiGHPeI5zGl1dioMPjLI9s2Twir8CgYEAn5wAbpx2imCPDtKgMxb0zxbKB5kq37QGuIRRAoorPkFr0Z7xSZs1M0qkhZcaX/kfnNgigPVwpUeUDQmETBRaXrCuXxRkOXGo1st4v5gn9kgQYBCv0dVbV1QNnnXp2fAtQ/3Jw6l9w4T0PgPBhu9TPnSTVZA0YldNo7TEEL5kEbA=
\ No newline at end of file
diff --git a/certs/alipay-public-key-test.pem b/certs/alipay-public-key-test.pem
deleted file mode 100644
index 29aa871..0000000
--- a/certs/alipay-public-key-test.pem
+++ /dev/null
@@ -1 +0,0 @@
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhptRt5n0KVACDX69KfZogfeLULSuZGkH0m3wVDI32a4eBH3TrzKjSShdO2WBF6glfvdCruuMuLmYJrIxa09koNcAF5CNc/2iHE8O8pUbrsLKgX+dpXoNZt6XVo/jHqsxmYDFjZogL3xFVW0z0pGcxaIWnVEhQ0hQ+ji2RxJ0Bb45mmOZOOVihpLf9hEFW0rHamf2Tfu+Hd4NWTb/CVZwgzchJ/cwLTqP1Ar3GeQdmB2tmaCTu1h8wyt6lkSUTOYTEf8xCdmv8xfS12dXAeh11t1a3SuqPT2b4UxuLJHHMmiKndD7BnPZIENxi7e6N5JKz1zahyuh23GeBCHs1wHtQQIDAQAB
diff --git a/certs/alipay-public-key-y.pem b/certs/alipay-public-key-y.pem
deleted file mode 100644
index 89738c0..0000000
--- a/certs/alipay-public-key-y.pem
+++ /dev/null
@@ -1,9 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlfXRCD4ppMMYSV18tnJ7
-TWanmXVeCQHce2aZ7241ejo3hGXM5LChWupYLGEelquhv5IRHVUcjVuEbHqQUcFR
-gwccVDdJwfbk7T7YtttD7V2SCcnC2OfOZy/4mlI1LfgsodqrMiBkNJRsfVRVlPse
-bLRgi4H2WxzmjKNEvuPbqRLF+aeDMkW3OMwP73/sYJhpuX9WAdTcJt9iQYVUaLq5
-h4YvNjC19x8cKOkf0iDqwyHFKxC2AvV0Qti1FmCSLENUDLaxP9F1RZiAevIFPSak
-UzV4Swsi9fSt1lBr0VxvTeZk2mUgSGHKO2a7W0xR7SMUjmMamEzrZDylWMRp9SNl
-hwIDAQAB
------END PUBLIC KEY-----
\ No newline at end of file
diff --git a/certs/alipay-public-key.pem b/certs/alipay-public-key.pem
deleted file mode 100644
index 8c927a9..0000000
--- a/certs/alipay-public-key.pem
+++ /dev/null
@@ -1 +0,0 @@
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5swLKPSzOMucRC52c9kKJZI9cYWDFd+s3UuE+aDtWodGrGV8g3szmp7hUWlaWY/didKc9vQNq93y67eEyw6QsMn26WwlzDbgP0xTcHEt+qDCeAltSqf6MX3KPmlz0f/DNneR9DR9ZGwaW1ATY3kg8gj+kIWngrqgjOv37UJWEpQOxUfWDGTBC1zzhC0PTXY7lX3GUZmDEtDtBs1BsFUdk995TbTD1cTiyDFuea49br0dovmU1ROOg6vK3G9xDd4Mke/opDunLTHe63+fBCnB7FyZ9F8zWg4LYND1QPmIX2m5gwICBHhNm8WqIfp9T64vpAxlM74BEsMlv3hNy0INQQIDAQAB
\ No newline at end of file
diff --git a/certs/alipayCertPublicKey_RSA2.crt b/certs/alipayCertPublicKey_RSA2.crt
deleted file mode 100644
index 60f26b0..0000000
--- a/certs/alipayCertPublicKey_RSA2.crt
+++ /dev/null
@@ -1,3 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5swLKPSzOMucRC52c9kKJZI9cYWDFd+s3UuE+aDtWodGrGV8g3szmp7hUWlaWY/didKc9vQNq93y67eEyw6QsMn26WwlzDbgP0xTcHEt+qDCeAltSqf6MX3KPmlz0f/DNneR9DR9ZGwaW1ATY3kg8gj+kIWngrqgjOv37UJWEpQOxUfWDGTBC1zzhC0PTXY7lX3GUZmDEtDtBs1BsFUdk995TbTD1cTiyDFuea49br0dovmU1ROOg6vK3G9xDd4Mke/opDunLTHe63+fBCnB7FyZ9F8zWg4LYND1QPmIX2m5gwICBHhNm8WqIfp9T64vpAxlM74BEsMlv3hNy0INQQIDAQAB
------END PUBLIC KEY-----
\ No newline at end of file
diff --git a/certs/alipayPublicKey_RSA2.crt b/certs/alipayPublicKey_RSA2.crt
deleted file mode 100644
index 60f26b0..0000000
--- a/certs/alipayPublicKey_RSA2.crt
+++ /dev/null
@@ -1,3 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5swLKPSzOMucRC52c9kKJZI9cYWDFd+s3UuE+aDtWodGrGV8g3szmp7hUWlaWY/didKc9vQNq93y67eEyw6QsMn26WwlzDbgP0xTcHEt+qDCeAltSqf6MX3KPmlz0f/DNneR9DR9ZGwaW1ATY3kg8gj+kIWngrqgjOv37UJWEpQOxUfWDGTBC1zzhC0PTXY7lX3GUZmDEtDtBs1BsFUdk995TbTD1cTiyDFuea49br0dovmU1ROOg6vK3G9xDd4Mke/opDunLTHe63+fBCnB7FyZ9F8zWg4LYND1QPmIX2m5gwICBHhNm8WqIfp9T64vpAxlM74BEsMlv3hNy0INQQIDAQAB
------END PUBLIC KEY-----
\ No newline at end of file
diff --git a/certs/appCertPublicKey.crt b/certs/appCertPublicKey.crt
deleted file mode 100644
index d13ac61..0000000
--- a/certs/appCertPublicKey.crt
+++ /dev/null
@@ -1,3 +0,0 @@
------BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlfXRCD4ppMMYSV18tnJ7TWanmXVeCQHce2aZ7241ejo3hGXM5LChWupYLGEelquhv5IRHVUcjVuEbHqQUcFRgwccVDdJwfbk7T7YtttD7V2SCcnC2OfOZy/4mlI1LfgsodqrMiBkNJRsfVRVlPsebLRgi4H2WxzmjKNEvuPbqRLF+aeDMkW3OMwP73/sYJhpuX9WAdTcJt9iQYVUaLq5h4YvNjC19x8cKOkf0iDqwyHFKxC2AvV0Qti1FmCSLENUDLaxP9F1RZiAevIFPSakUzV4Swsi9fSt1lBr0VxvTeZk2mUgSGHKO2a7W0xR7SMUjmMamEzrZDylWMRp9SNlhwIDAQAB
------END PUBLIC KEY-----
\ No newline at end of file
diff --git a/config/dbv2.js b/config/dbv2.js
index e7e253b..9d971f1 100644
--- a/config/dbv2.js
+++ b/config/dbv2.js
@@ -1,13 +1,15 @@
+// queryBuilder.js
class QueryBuilder {
constructor() {
- this.conditions = {};
+ this.conditions = []; // { sql, params }
this.limit = null;
this.offset = null;
this.groupBy = null;
+ this.orderBy = null;
}
where(condition, ...params) {
- this.conditions[condition] = params;
+ this.conditions.push({ sql: condition, params });
return this;
}
@@ -26,88 +28,48 @@ class QueryBuilder {
return this;
}
- sqdata(sql, params) {
- return new Promise((resolve, reject) => {
- global.sqlReq.query(sql, params, (err, result) => {
- if (err) {
- reject(err);
- }
- resolve(result);
- });
- });
- }
-
- getParams() {
- return Object.values(this.conditions).flat();
+ orderByField(field, direction = 'ASC') {
+ this.orderBy = { field, direction: direction.toUpperCase() };
+ return this;
}
buildConditions() {
- return Object.keys(this.conditions).map(condition => `${condition}`).join(' AND ');
+ if (!this.conditions.length) return { sql: '', params: [] };
+ const sql = this.conditions.map(c => `(${c.sql})`).join(' AND ');
+ const params = this.conditions.flatMap(c => c.params);
+ return { sql, params };
+ }
+
+ buildLimitOffset() {
+ let sql = '';
+ if (this.limit !== null) sql += ` LIMIT ${this.limit}`;
+ if (this.offset !== null) sql += ` OFFSET ${this.offset}`;
+ return sql;
+ }
+
+ async execute(db) {
+ const { sql, params } = this.build();
+ const [rows] = await db.query(sql, params);
+ return rows;
}
}
+// ------------------- SELECT -------------------
class SelectBuilder extends QueryBuilder {
constructor() {
super();
this.selectFields = [];
this.tables = [];
- this.orderByField = '';
- this.orderByDirection = 'ASC';
- this.subQueries = []; // 用于存储子查询
- this.unions = []; // 存储UNION查询
- }
- // 添加UNION查询
- union(queryBuilder, type = 'UNION') {
- this.unions.push({queryBuilder, type});
- return this;
- }
-
- // 添加UNION ALL查询
- unionAll(queryBuilder) {
- this.union(queryBuilder, 'UNION ALL');
- return this;
- }
-
- // 构建主查询部分(不含ORDER BY/LIMIT/OFFSET)
- buildMainQuery() {
- const subQuerySQL = this.subQueries.map(({alias, subQuery}) => `(${subQuery}) AS ${alias}`);
- const selectClause = this.selectFields.concat(subQuerySQL).join(', ');
-
- let sql = `SELECT ${selectClause}
- FROM ${this.tables.join(' ')}`;
-
- const conditionClauses = this.buildConditions();
- if (conditionClauses) {
- sql += ` WHERE ${conditionClauses}`;
- }
-
- if (this.groupBy) {
- sql += ` GROUP BY ${this.groupBy}`;
- }
-
- const params = this.getParams();
- return {sql, params};
- }
-
- // 供UNION查询调用的构建方法
- buildForUnion() {
- return this.buildMainQuery();
+ this.joins = [];
+ this.unions = [];
}
select(fields) {
- this.selectFields = fields.split(',').map(field => field.trim());
- return this;
- }
-
- // 添加子查询
- addSubQuery(alias, subQuery) {
- this.subQueries.push({alias, subQuery});
- return this;
- }
-
- whereLike(fields, keyword) {
- const likeConditions = fields.map(field => `${field} LIKE ?`).join(' OR ');
- this.conditions[likeConditions] = fields.map(() => `%${keyword}%`);
+ if (typeof fields === 'string') {
+ this.selectFields = fields.split(',').map(f => f.trim());
+ } else if (Array.isArray(fields)) {
+ this.selectFields = fields;
+ }
return this;
}
@@ -116,78 +78,76 @@ class SelectBuilder extends QueryBuilder {
return this;
}
- leftJoin(table, condition) {
- this.tables.push(`LEFT JOIN ${table} ON ${condition}`);
+ join(type, table, condition) {
+ this.joins.push(`${type.toUpperCase()} JOIN ${table} ON ${condition}`);
return this;
}
- orderBy(field, direction = 'ASC') {
- this.orderByField = field;
- this.orderByDirection = direction.toUpperCase();
+ union(queryBuilder, type = 'UNION') {
+ this.unions.push({ queryBuilder, type });
return this;
}
- paginate(page, pageSize) {
+ unionAll(queryBuilder) {
+ return this.union(queryBuilder, 'UNION ALL');
+ }
+
+ build() {
+ const selectClause = this.selectFields.length ? this.selectFields.join(', ') : '*';
+ let sql = `SELECT ${selectClause} FROM ${this.tables.join(', ')}`;
+ if (this.joins.length) sql += ' ' + this.joins.join(' ');
+
+ const { sql: whereSql, params } = this.buildConditions();
+ if (whereSql) sql += ` WHERE ${whereSql}`;
+
+ if (this.groupBy) sql += ` GROUP BY ${this.groupBy}`;
+
+ // 处理 UNION
+ for (const u of this.unions) {
+ const { sql: uSql, params: uParams } = u.queryBuilder.build();
+ sql += ` ${u.type} ${uSql}`;
+ params.push(...uParams);
+ }
+
+ if (this.orderBy) {
+ sql += ` ORDER BY ${this.orderBy.field} ${this.orderBy.direction}`;
+ }
+
+ sql += this.buildLimitOffset();
+ return { sql, params };
+ }
+
+ async paginateWithCount(db, page = 1, pageSize = 10) {
if (page <= 0 || pageSize <= 0) {
throw new Error('分页参数必须大于0');
}
- this.limit = pageSize;
- this.offset = (page - 1) * pageSize;
- return this;
- }
- async chidBuild() {
+ // count 查询
+ let countSql = `SELECT COUNT(*) as total FROM ${this.tables.join(', ')}`;
+ if (this.joins.length) countSql += ' ' + this.joins.join(' ');
- let sql = `SELECT ${this.selectFields.join(', ')}
- FROM ${this.tables.join(' ')}`;
- let conditionClauses = this.buildConditions();
- if (conditionClauses) {
- sql += ` WHERE ${conditionClauses}`;
- }
- if (this.orderByField) {
- sql += ` ORDER BY ${this.orderByField} ${this.orderByDirection}`;
- }
- if (this.limit !== null) {
- sql += ` LIMIT ${this.limit}`;
- }
- if (this.offset !== null) {
- sql += ` OFFSET ${this.offset}`;
- }
- return sql;
- }
+ const { sql: whereSql, params } = this.buildConditions();
+ if (whereSql) countSql += ` WHERE ${whereSql}`;
- async build() {
- const main = this.buildMainQuery();
- let fullSql = `(${main.sql})`;
- const allParams = [...main.params];
+ const [countRows] = await db.query(countSql, params);
+ const total = countRows[0].total;
- // 处理UNION部分
- for (const union of this.unions) {
- const unionBuilder = union.queryBuilder;
- if (!(unionBuilder instanceof SelectBuilder)) {
- throw new Error('UNION query must be a SelectBuilder instance');
- }
- const unionResult = unionBuilder.buildForUnion();
- fullSql += ` ${union.type} (${unionResult.sql})`;
- allParams.push(...unionResult.params);
- }
+ // 数据查询
+ this.setLimit(pageSize).setOffset((page - 1) * pageSize);
+ const { sql, params: dataParams } = this.build();
+ const [rows] = await db.query(sql, dataParams);
- // 添加ORDER BY、LIMIT、OFFSET
- if (this.orderByField) {
- fullSql += ` ORDER BY ${this.orderByField} ${this.orderByDirection}`;
- }
- if (this.limit !== null) {
- fullSql += ` LIMIT ${this.limit}`;
- }
- if (this.offset !== null) {
- fullSql += ` OFFSET ${this.offset}`;
- }
- console.log(fullSql,allParams);
- return await this.sqdata(fullSql, allParams);
+ return {
+ data: rows,
+ total,
+ page,
+ pageSize,
+ totalPages: Math.ceil(total / pageSize)
+ };
}
}
-
+// ------------------- UPDATE -------------------
class UpdateBuilder extends QueryBuilder {
constructor() {
super();
@@ -201,157 +161,107 @@ class UpdateBuilder extends QueryBuilder {
}
set(field, value) {
- if (value && value.increment && typeof value === 'object' ) {
- this.updateFields[field] = {increment: value.increment};
+ if (typeof value === 'object' && value.increment !== undefined) {
+ this.updateFields[field] = { increment: value.increment };
} else {
this.updateFields[field] = value;
}
return this;
}
- async build() {
- let sql = `UPDATE ${this.table}
- SET `;
- let updateClauses = Object.keys(this.updateFields).map(field => {
- const value = this.updateFields[field];
- if (value && value.increment && typeof value === 'object' ) {
+ setFields(fieldsObj) {
+ Object.entries(fieldsObj).forEach(([k, v]) => this.set(k, v));
+ return this;
+ }
+
+ build() {
+ const updateClauses = Object.keys(this.updateFields).map(field => {
+ const val = this.updateFields[field];
+ if (typeof val === 'object' && val.increment !== undefined) {
return `${field} = ${field} + ?`;
}
return `${field} = ?`;
}).join(', ');
- sql += updateClauses;
+ let sql = `UPDATE ${this.table} SET ${updateClauses}`;
+ const { sql: whereSql, params: whereParams } = this.buildConditions();
+ if (whereSql) sql += ` WHERE ${whereSql}`;
- let conditionClauses = this.buildConditions();
- if (conditionClauses) {
- sql += ` WHERE ${conditionClauses}`;
- }
- // 处理参数,确保自增字段也传入增量值
const params = [
- ...Object.values(this.updateFields).map(value =>
- (value && value.increment && typeof value === 'object' ) ? value.increment : value
+ ...Object.values(this.updateFields).map(v =>
+ (typeof v === 'object' && v.increment !== undefined) ? v.increment : v
),
- ...this.getParams()
+ ...whereParams
];
- return await this.sqdata(sql, params);
+ return { sql, params };
}
}
+// ------------------- INSERT -------------------
class InsertBuilder extends QueryBuilder {
constructor() {
super();
this.table = '';
- this.insertValues = [];
+ this.records = [];
this.updateValues = {};
}
- insertInto(table) {
+ into(table) {
this.table = table;
return this;
}
- // 仍然保留单条记录的插入
- values(values) {
- if (Array.isArray(values)) {
- this.insertValues = values;
- } else {
- this.insertValues = [values]; // 将单条记录包装成数组
- }
+ values(records) {
+ if (!Array.isArray(records)) records = [records];
+ this.records = records;
return this;
}
- // 新增方法,支持一次插入多条记录
- valuesMultiple(records) {
- if (!Array.isArray(records) || records.length === 0) {
- throw new Error('Values must be a non-empty array');
- }
-
- // 确保每一条记录都是对象
- records.forEach(record => {
- if (typeof record !== 'object') {
- throw new Error('Each record must be an object');
- }
- });
-
- this.insertValues = records;
+ upsert(records, updateFields) {
+ this.values(records);
+ this.updateValues = updateFields;
return this;
}
- // 新增 upsert 方法,支持更新或插入
- upsert(values, updateFields) {
- // values: 要插入的记录
- // updateFields: 如果记录存在时,需要更新的字段
- if (!Array.isArray(values) || values.length === 0) {
- throw new Error('Values must be a non-empty array');
+ build() {
+ if (!this.records.length) throw new Error('No values to insert');
+
+ const columns = Object.keys(this.records[0]);
+ const placeholders = `(${columns.map(() => '?').join(', ')})`;
+ const valuesClause = this.records.map(() => placeholders).join(', ');
+ const params = this.records.flatMap(r => columns.map(c => r[c]));
+
+ let sql = `INSERT INTO ${this.table} (${columns.join(', ')}) VALUES ${valuesClause}`;
+
+ if (Object.keys(this.updateValues).length) {
+ const updates = Object.keys(this.updateValues)
+ .map(f => `${f} = VALUES(${f})`) // MySQL 8.0+ 兼容写法
+ .join(', ');
+ sql += ` ON DUPLICATE KEY UPDATE ${updates}`;
}
- // 检查每条记录是否是对象
- values.forEach(record => {
- if (typeof record !== 'object') {
- throw new Error('Each record must be an object');
- }
- });
-
- this.insertValues = values;
- this.updateValues = updateFields || {};
- return this;
- }
-
- async build() {
- if (this.insertValues.length === 0) {
- throw new Error("No values to insert");
- }
-
- // 获取表单列名,假设所有记录有相同的字段
- const columns = Object.keys(this.insertValues[0]);
-
- // 构建 VALUES 子句,支持批量插入
- const valuePlaceholders = this.insertValues.map(() =>
- `(${columns.map(() => '?').join(', ')})`
- ).join(', ');
-
- // 展平所有的插入值
- const params = this.insertValues.flatMap(record =>
- columns.map(column => record[column])
- );
-
- // 如果有 updateFields,构建 ON DUPLICATE KEY UPDATE 子句
- let updateClause = '';
- if (Object.keys(this.updateValues).length > 0) {
- updateClause = ' ON DUPLICATE KEY UPDATE ' +
- Object.keys(this.updateValues).map(field => {
- return `${field} = VALUES(${field})`;
- }).join(', ');
- }
-
- // 生成 SQL 语句
- const sql = `INSERT INTO ${this.table} (${columns.join(', ')})
- VALUES ${valuePlaceholders} ${updateClause}`;
- // 执行查询
- return await this.sqdata(sql, params);
+ return { sql, params };
}
}
-
+// ------------------- DELETE -------------------
class DeleteBuilder extends QueryBuilder {
constructor() {
super();
this.table = '';
}
- deleteFrom(table) {
+ from(table) {
this.table = table;
return this;
}
- async build() {
- let sql = `DELETE
- FROM ${this.table}`;
- let conditionClauses = this.buildConditions();
- if (conditionClauses) {
- sql += ` WHERE ${conditionClauses}`;
- }
- return await this.sqdata(sql, this.getParams());
+ build() {
+ let sql = `DELETE FROM ${this.table}`;
+ const { sql: whereSql, params } = this.buildConditions();
+ if (!whereSql) throw new Error('DELETE without WHERE is not allowed!');
+ sql += ` WHERE ${whereSql}`;
+ return { sql, params };
}
}
@@ -359,5 +269,5 @@ module.exports = {
SelectBuilder,
UpdateBuilder,
InsertBuilder,
- DeleteBuilder,
+ DeleteBuilder
};
diff --git a/database.js b/database.js
index 949a3af..5d8b8df 100644
--- a/database.js
+++ b/database.js
@@ -14,7 +14,7 @@ const dbConfig = {
dateStrings: true,
// 连接池配置
connectionLimit: 20, // 连接池最大连接数
- queueLimit: 0, // 排队等待连接的最大数量,0表示无限制
+ // queueLimit: 0, // 排队等待连接的最大数量,0表示无限制
// 连接超时配置
// acquireTimeout: 60000, // 获取连接超时时间 60秒
// timeout: 60000, // 查询超时时间 60秒
diff --git a/docs/README.md b/docs/README.md
deleted file mode 100644
index 73d8db2..0000000
--- a/docs/README.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# API 文档结构说明
-
-本项目已将 Swagger API 文档从路由文件中分离出来,采用模块化的文档管理方式。
-
-## 文件夹结构
-
-```
-docs/
-├── README.md # 本说明文件
-├── schemas/ # 数据模型定义
-│ ├── product.js # 商品相关数据模型
-│ ├── order.js # 订单相关数据模型
-│ ├── user.js # 用户相关数据模型
-│ ├── cart.js # 购物车相关数据模型
-│ └── announcement.js # 通知公告相关数据模型
-└── apis/ # API 接口定义
- ├── products.js # 商品相关 API
- ├── orders.js # 订单相关 API
- └── announcements.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/announcements.js b/docs/apis/announcements.js
deleted file mode 100644
index 12c5eea..0000000
--- a/docs/apis/announcements.js
+++ /dev/null
@@ -1,736 +0,0 @@
-/**
- * @swagger
- * tags:
- * name: Announcements
- * description: 通知公告管理API
- */
-
-/**
- * @swagger
- * /api/announcements/{id}:
- * get:
- * summary: 获取单个公告详情
- * tags: [Announcements]
- * 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:
- * $ref: '#/components/schemas/Announcement'
- * 401:
- * description: 未授权
- * 404:
- * description: 公告不存在
- * 500:
- * description: 服务器错误
- */
-
-/**
- * @swagger
- * /api/announcements/{id}/read:
- * post:
- * summary: 标记公告为已读
- * tags: [Announcements]
- * 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
- * example: true
- * message:
- * type: string
- * example: "已标记为已读"
- * 401:
- * description: 未授权
- * 404:
- * description: 公告不存在
- * 500:
- * description: 服务器错误
- */
-
-/**
- * @swagger
- * /api/announcements/unread/count:
- * get:
- * summary: 获取用户未读公告数量
- * tags: [Announcements]
- * security:
- * - bearerAuth: []
- * responses:
- * 200:
- * description: 获取未读数量成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: object
- * properties:
- * unread_count:
- * type: integer
- * example: 5
- * description: 未读公告数量
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-
-/**
- * @swagger
- * /api/announcements/batch/read:
- * post:
- * summary: 批量标记公告为已读
- * tags: [Announcements]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - announcement_ids
- * properties:
- * announcement_ids:
- * type: array
- * items:
- * type: integer
- * example: [1, 2, 3]
- * description: 公告ID列表
- * responses:
- * 200:
- * description: 批量标记已读成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: "批量标记已读成功"
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-
-/**
- * @swagger
- * /api/announcements:
- * post:
- * summary: 创建新公告
- * tags: [Announcements]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - title
- * - content
- * properties:
- * title:
- * type: string
- * description: 公告标题
- * content:
- * type: string
- * description: 公告内容
- * type:
- * type: string
- * enum: [system, maintenance, promotion, warning]
- * default: system
- * priority:
- * type: string
- * enum: [low, medium, high, urgent]
- * default: medium
- * status:
- * type: string
- * enum: [draft, published]
- * default: draft
- * is_pinned:
- * type: boolean
- * default: false
- * publish_time:
- * type: string
- * format: date-time
- * expire_time:
- * type: string
- * format: date-time
- * responses:
- * 201:
- * description: 公告创建成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * message:
- * type: string
- * data:
- * type: object
- * properties:
- * id:
- * type: integer
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-
-/**
- * @swagger
- * /api/announcements/{id}:
- * put:
- * summary: 更新公告
- * tags: [Announcements]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: id
- * required: true
- * schema:
- * type: integer
- * description: 公告ID
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * title:
- * type: string
- * content:
- * type: string
- * type:
- * type: string
- * enum: [system, maintenance, promotion, warning]
- * priority:
- * type: string
- * enum: [low, medium, high, urgent]
- * status:
- * type: string
- * enum: [draft, published, archived]
- * is_pinned:
- * type: boolean
- * publish_time:
- * type: string
- * format: date-time
- * expire_time:
- * type: string
- * format: date-time
- * responses:
- * 200:
- * description: 公告更新成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * message:
- * type: string
- * data:
- * $ref: '#/components/schemas/Announcement'
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 404:
- * description: 公告不存在
- * 500:
- * description: 服务器错误
- *
- * delete:
- * summary: 删除公告
- * tags: [Announcements]
- * 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: 服务器错误
- */
-
-/**
- * @swagger
- * /api/announcements/public/list:
- * get:
- * summary: 获取公开发布的公告列表(无需认证)
- * tags: [Announcements]
- * parameters:
- * - in: query
- * name: limit
- * schema:
- * type: integer
- * default: 5
- * description: 获取数量
- * responses:
- * 200:
- * description: 成功获取公开公告列表
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: array
- * items:
- * $ref: '#/components/schemas/Announcement'
- * 500:
- * description: 服务器错误
- */
-
-/**
- * @swagger
- * components:
- * schemas:
- * Announcement:
- * type: object
- * required:
- * - title
- * - content
- * properties:
- * id:
- * type: integer
- * description: 公告ID
- * title:
- * type: string
- * description: 公告标题
- * content:
- * type: string
- * description: 公告内容
- * type:
- * type: string
- * description: 公告类型
- * enum: [system, maintenance, promotion, warning]
- * priority:
- * type: string
- * description: 优先级
- * enum: [low, medium, high, urgent]
- * status:
- * type: string
- * description: 状态
- * enum: [draft, published, archived]
- * is_pinned:
- * type: boolean
- * description: 是否置顶
- * publish_time:
- * type: string
- * format: date-time
- * description: 发布时间
- * expire_time:
- * type: string
- * format: date-time
- * description: 过期时间
- * created_by:
- * type: integer
- * description: 创建者ID
- * created_at:
- * type: string
- * format: date-time
- * description: 创建时间
- * updated_at:
- * type: string
- * format: date-time
- * description: 更新时间
- */
-
-/**
- * @swagger
- * /api/announcements:
- * get:
- * summary: 获取通知公告列表
- * tags: [Announcements]
- * 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: type
- * schema:
- * type: string
- * enum: [system, activity, maintenance, urgent]
- * description: 公告类型
- * - in: query
- * name: priority
- * schema:
- * type: string
- * enum: [high, medium, low]
- * description: 优先级
- * - in: query
- * name: status
- * schema:
- * type: string
- * enum: [draft, published, expired]
- * description: 状态
- * - in: query
- * name: isTop
- * schema:
- * type: boolean
- * description: 是否置顶
- * - in: query
- * name: sortBy
- * schema:
- * type: string
- * enum: [created_at, updated_at, publish_time, priority]
- * default: created_at
- * description: 排序字段
- * - in: query
- * name: sortOrder
- * schema:
- * type: string
- * enum: [ASC, DESC]
- * default: DESC
- * description: 排序方向
- * responses:
- * 200:
- * description: 成功获取公告列表
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: object
- * properties:
- * announcements:
- * type: array
- * items:
- * $ref: '#/components/schemas/Announcement'
- * total:
- * type: integer
- * example: 50
- * page:
- * type: integer
- * example: 1
- * limit:
- * type: integer
- * example: 10
- * totalPages:
- * type: integer
- * example: 5
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- *
- * post:
- * summary: 创建新的通知公告
- * tags: [Announcements]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - title
- * - content
- * - type
- * - priority
- * properties:
- * title:
- * type: string
- * description: 公告标题
- * example: "系统维护通知"
- * content:
- * type: string
- * description: 公告内容
- * example: "系统将于今晚进行维护,预计维护时间2小时"
- * type:
- * type: string
- * enum: [system, activity, maintenance, urgent]
- * description: 公告类型
- * example: "maintenance"
- * priority:
- * type: string
- * enum: [high, medium, low]
- * description: 优先级
- * example: "high"
- * status:
- * type: string
- * enum: [draft, published]
- * default: draft
- * description: 状态
- * isTop:
- * type: boolean
- * default: false
- * description: 是否置顶
- * publishTime:
- * type: string
- * format: date-time
- * description: 发布时间
- * expireTime:
- * type: string
- * format: date-time
- * description: 过期时间
- * responses:
- * 201:
- * description: 公告创建成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: "公告创建成功"
- * data:
- * $ref: '#/components/schemas/Announcement'
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-
-/**
- * @swagger
- * /api/announcements/{id}:
- * put:
- * summary: 更新通知公告
- * tags: [Announcements]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: id
- * required: true
- * schema:
- * type: integer
- * description: 公告ID
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * title:
- * type: string
- * description: 公告标题
- * content:
- * type: string
- * description: 公告内容
- * type:
- * type: string
- * enum: [system, activity, maintenance, urgent]
- * description: 公告类型
- * priority:
- * type: string
- * enum: [high, medium, low]
- * description: 优先级
- * status:
- * type: string
- * enum: [draft, published, expired]
- * description: 状态
- * isTop:
- * type: boolean
- * description: 是否置顶
- * publishTime:
- * type: string
- * format: date-time
- * description: 发布时间
- * expireTime:
- * type: string
- * format: date-time
- * description: 过期时间
- * responses:
- * 200:
- * description: 公告更新成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: "公告更新成功"
- * data:
- * $ref: '#/components/schemas/Announcement'
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 404:
- * description: 公告不存在
- * 500:
- * description: 服务器错误
- *
- * delete:
- * summary: 删除通知公告
- * tags: [Announcements]
- * 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
- * example: true
- * message:
- * type: string
- * example: "公告删除成功"
- * 401:
- * description: 未授权
- * 404:
- * description: 公告不存在
- * 500:
- * description: 服务器错误
- */
-
-/**
- * @swagger
- * /api/announcements/{id}/toggle-top:
- * put:
- * summary: 切换公告置顶状态
- * tags: [Announcements]
- * 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
- * example: true
- * message:
- * type: string
- * example: "置顶状态更新成功"
- * data:
- * type: object
- * properties:
- * isTop:
- * type: boolean
- * example: true
- * 401:
- * description: 未授权
- * 404:
- * description: 公告不存在
- * 500:
- * description: 服务器错误
- */
\ No newline at end of file
diff --git a/docs/apis/captcha.js b/docs/apis/captcha.js
deleted file mode 100644
index 3a46213..0000000
--- a/docs/apis/captcha.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * @swagger
- * tags:
- * name: Captcha
- * description: 验证码API
- */
-/**
- * @swagger
- * /captcha/generate:
- * get:
- * summary: 生成图形验证码
- * tags: [Captcha]
- * responses:
- * 200:
- * description: 成功生成验证码
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: object
- * properties:
- * captchaId:
- * type: string
- * description: 验证码唯一ID
- * image:
- * type: string
- * description: Base64编码的SVG验证码图片
- * 500:
- * description: 服务器错误
- */
-/**
- * @swagger
- * /captcha/verify:
- * post:
- * summary: 验证用户输入的验证码
- * tags: [Captcha]
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - captchaId
- * - captchaText
- * properties:
- * captchaId:
- * type: string
- * description: 验证码唯一ID
- * captchaText:
- * type: string
- * description: 用户输入的验证码
- * responses:
- * 200:
- * description: 验证码验证成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 验证码验证成功
- * 400:
- * description: 验证码错误或已过期
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: false
- * message:
- * type: string
- * example: 验证码错误
- * 500:
- * description: 服务器错误
- */
\ No newline at end of file
diff --git a/docs/apis/matching.js b/docs/apis/matching.js
deleted file mode 100644
index 7e7a72b..0000000
--- a/docs/apis/matching.js
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * @swagger
- * tags:
- * name: Matching
- * description: 匹配订单相关接口
- */
-/**
- * @swagger
- * /api/matching/my-orders:
- * get:
- * summary: 获取用户的匹配订单列表
- * tags: [Matching]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: query
- * name: page
- * schema:
- * type: integer
- * default: 1
- * description: 页码
- * - 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: array
- * items:
- * $ref: '#/components/schemas/MatchingOrder'
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-/**
- * @swagger
- * components:
- * schemas:
- * MatchingOrder:
- * type: object
- * properties:
- * id:
- * type: integer
- * description: 匹配订单ID
- * initiator_id:
- * type: integer
- * description: 发起人ID
- * matching_type:
- * type: string
- * enum: [small, large]
- * description: 匹配类型(小额或大额)
- * amount:
- * type: number
- * description: 匹配总金额
- * status:
- * type: string
- * enum: [pending, matching, completed, failed]
- * description: 订单状态
- * created_at:
- * type: string
- * format: date-time
- * description: 创建时间
- * Allocation:
- * type: object
- * properties:
- * id:
- * type: integer
- * description: 分配ID
- * from_user_id:
- * type: integer
- * description: 发送方用户ID
- * to_user_id:
- * type: integer
- * description: 接收方用户ID
- * amount:
- * type: number
- * description: 分配金额
- * cycle_number:
- * type: integer
- * description: 轮次编号
- * status:
- * type: string
- * enum: [pending, confirmed, rejected, cancelled]
- * description: 分配状态
- * created_at:
- * type: string
- * format: date-time
- * description: 创建时间
- */
-
-/**
- * @swagger
- * /api/matching/create:
- * post:
- * summary: 创建匹配订单
- * tags: [Matching]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * matchingType:
- * type: string
- * enum: [small, large]
- * default: small
- * description: 匹配类型(小额或大额)
- * customAmount:
- * type: number
- * description: 大额匹配时的自定义金额(5000-50000之间)
- * responses:
- * 200:
- * description: 匹配订单创建成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * message:
- * type: string
- * data:
- * type: object
- * properties:
- * matchingOrderId:
- * type: integer
- * amounts:
- * type: array
- * items:
- * type: number
- * matchingType:
- * type: string
- * totalAmount:
- * type: number
- * 400:
- * description: 参数错误或用户未满足匹配条件
- * 401:
- * description: 未授权
- * 404:
- * description: 用户不存在
- * 500:
- * description: 服务器错误
- */
\ No newline at end of file
diff --git a/docs/apis/orders.js b/docs/apis/orders.js
deleted file mode 100644
index 8342c27..0000000
--- a/docs/apis/orders.js
+++ /dev/null
@@ -1,273 +0,0 @@
-/**
- * @swagger
- * tags:
- * name: Orders
- * description: 订单管理API
- */
-
-/**
- * @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
- * 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: 服务器错误
- */
-
-/**
- * @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: 服务器错误
- */
-
-/**
- * @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: 服务器错误
- */
-
-/**
- * @swagger
- * /api/orders/{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:
- * order:
- * $ref: '#/components/schemas/Order'
- * 401:
- * description: 未授权
- * 404:
- * description: 订单不存在
- * 500:
- * description: 服务器错误
- */
\ No newline at end of file
diff --git a/docs/apis/products.js b/docs/apis/products.js
deleted file mode 100644
index da8520c..0000000
--- a/docs/apis/products.js
+++ /dev/null
@@ -1,154 +0,0 @@
-/**
- * @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/apis/transfers.js b/docs/apis/transfers.js
deleted file mode 100644
index 1db9f85..0000000
--- a/docs/apis/transfers.js
+++ /dev/null
@@ -1,388 +0,0 @@
-/**
- * @swagger
- * components:
- * schemas:
- * Transfer:
- * type: object
- * properties:
- * id:
- * type: integer
- * description: 转账记录ID
- * user_id:
- * type: integer
- * description: 用户ID
- * recipient_id:
- * type: integer
- * description: 接收方用户ID
- * amount:
- * type: number
- * format: float
- * description: 转账金额
- * status:
- * type: string
- * enum: [pending, completed, failed, cancelled]
- * description: 转账状态
- * transfer_type:
- * type: string
- * enum: [user_to_user, user_to_system, system_to_user]
- * description: 转账类型
- * voucher_image:
- * type: string
- * description: 转账凭证图片路径
- * remark:
- * type: string
- * description: 转账备注
- * created_at:
- * type: string
- * format: date-time
- * description: 创建时间
- * updated_at:
- * type: string
- * format: date-time
- * description: 更新时间
- * Pagination:
- * type: object
- * properties:
- * total:
- * type: integer
- * description: 总记录数
- * page:
- * type: integer
- * description: 当前页码
- * limit:
- * type: integer
- * description: 每页记录数
- * total_pages:
- * type: integer
- * description: 总页数
- */
-/**
- * @swagger
- * /transfers:
- * get:
- * summary: 获取转账列表
- * tags: [Transfers]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: query
- * name: status
- * schema:
- * type: string
- * description: 转账状态过滤
- * - in: query
- * name: transfer_type
- * schema:
- * type: string
- * description: 转账类型过滤
- * - in: query
- * name: start_date
- * schema:
- * type: string
- * format: date
- * description: 开始日期过滤
- * - in: query
- * name: end_date
- * schema:
- * type: string
- * format: date
- * description: 结束日期过滤
- * - in: query
- * name: search
- * schema:
- * type: string
- * description: 搜索关键词(用户名或真实姓名)
- * - in: query
- * name: page
- * schema:
- * type: integer
- * default: 1
- * description: 页码
- * - in: query
- * name: limit
- * schema:
- * type: integer
- * default: 10
- * description: 每页数量
- * - in: query
- * name: sort
- * schema:
- * type: string
- * description: 排序字段
- * - in: query
- * name: order
- * schema:
- * type: string
- * enum: [asc, desc]
- * description: 排序方向
- * responses:
- * 200:
- * description: 成功获取转账列表
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: object
- * properties:
- * transfers:
- * type: array
- * items:
- * $ref: '#/components/schemas/Transfer'
- * pagination:
- * $ref: '#/components/schemas/Pagination'
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-/**
- * @swagger
- * /transfers/list:
- * get:
- * summary: 获取转账记录列表
- * tags: [Transfers]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: query
- * name: status
- * schema:
- * type: string
- * description: 转账状态过滤
- * - in: query
- * name: transfer_type
- * schema:
- * type: string
- * description: 转账类型过滤
- * - in: query
- * name: start_date
- * schema:
- * type: string
- * format: date
- * description: 开始日期过滤
- * - in: query
- * name: end_date
- * schema:
- * type: string
- * format: date
- * description: 结束日期过滤
- * - in: query
- * name: page
- * schema:
- * type: integer
- * default: 1
- * description: 页码
- * - in: query
- * name: limit
- * schema:
- * type: integer
- * default: 10
- * description: 每页数量
- * - in: query
- * name: sort
- * schema:
- * type: string
- * description: 排序字段
- * - in: query
- * name: order
- * schema:
- * type: string
- * enum: [asc, desc]
- * description: 排序方向
- * responses:
- * 200:
- * description: 成功获取转账记录列表
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: object
- * properties:
- * transfers:
- * type: array
- * items:
- * $ref: '#/components/schemas/Transfer'
- * pagination:
- * $ref: '#/components/schemas/Pagination'
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-/**
- * @swagger
- * /transfers/public-account:
- * get:
- * summary: 获取公户信息
- * tags: [Transfers]
- * security:
- * - bearerAuth: []
- * responses:
- * 200:
- * description: 成功获取公户信息
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: object
- * properties:
- * id:
- * type: integer
- * description: 公户ID
- * username:
- * type: string
- * description: 公户用户名
- * example: public_account
- * real_name:
- * type: string
- * description: 公户名称
- * balance:
- * type: number
- * format: float
- * description: 公户余额
- * 401:
- * description: 未授权
- * 404:
- * description: 公户不存在
- * 500:
- * description: 服务器错误
- */
-/**
- * @swagger
- * /transfers/create:
- * post:
- * summary: 创建转账记录
- * tags: [Transfers]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - to_user_id
- * - amount
- * - transfer_type
- * properties:
- * to_user_id:
- * type: integer
- * description: 接收方用户ID
- * amount:
- * type: number
- * format: float
- * description: 转账金额
- * transfer_type:
- * type: string
- * enum: [user_to_user, user_to_system, system_to_user]
- * description: 转账类型
- * remark:
- * type: string
- * description: 转账备注
- * responses:
- * 201:
- * description: 转账记录创建成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 转账记录创建成功,等待确认
- * data:
- * type: object
- * properties:
- * transfer_id:
- * type: integer
- * description: 转账记录ID
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-/**
- * @swagger
- * /transfers/admin/create:
- * post:
- * summary: 管理员创建转账记录
- * tags: [Transfers]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - from_user_id
- * - to_user_id
- * - amount
- * - transfer_type
- * properties:
- * from_user_id:
- * type: integer
- * description: 发送方用户ID
- * to_user_id:
- * type: integer
- * description: 接收方用户ID
- * amount:
- * type: number
- * format: float
- * description: 转账金额
- * transfer_type:
- * type: string
- * enum: [user_to_user, user_to_system, system_to_user]
- * description: 转账类型
- * description:
- * type: string
- * description: 转账描述
- * responses:
- * 201:
- * description: 转账记录创建成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 转账记录创建成功
- * data:
- * type: object
- * properties:
- * transfer_id:
- * type: integer
- * description: 转账记录ID
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 403:
- * description: 权限不足
- * 500:
- * description: 服务器错误
- */
\ No newline at end of file
diff --git a/docs/apis/user.js b/docs/apis/user.js
deleted file mode 100644
index c437846..0000000
--- a/docs/apis/user.js
+++ /dev/null
@@ -1,423 +0,0 @@
-/**
- * @swagger
- * tags:
- * name: Authentication
- * description: 用户认证API
- */
-/**
- * @swagger
- * components:
- * schemas:
- * LoginCredentials:
- * type: object
- * required:
- * - username
- * - password
- * properties:
- * username:
- * type: string
- * description: 用户名或手机号
- * password:
- * type: string
- * description: 密码
- * RegisterRequest:
- * type: object
- * required:
- * - username
- * - phone
- * - password
- * - registrationCode
- * - city
- * - district_id
- * - captchaId
- * - captchaText
- * - smsCode
- * properties:
- * username:
- * type: string
- * description: 用户名
- * phone:
- * type: string
- * description: 手机号
- * password:
- * type: string
- * description: 密码
- * registrationCode:
- * type: string
- * description: 注册激活码
- * city:
- * type: string
- * description: 城市
- * district_id:
- * type: string
- * description: 区域ID
- * captchaId:
- * type: string
- * description: 图形验证码ID
- * captchaText:
- * type: string
- * description: 图形验证码文本
- * smsCode:
- * type: string
- * description: 短信验证码
- * role:
- * type: string
- * description: 用户角色
- * default: user
- */
-/**
- * @swagger
- * /users/pending-audit:
- * get:
- * summary: 获取待审核用户列表(管理员权限)
- * tags: [Users]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: query
- * name: page
- * schema:
- * type: integer
- * default: 1
- * description: 页码
- * - 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:
- * users:
- * type: array
- * items:
- * $ref: '#/components/schemas/User'
- * pagination:
- * type: object
- * properties:
- * page:
- * type: integer
- * limit:
- * type: integer
- * total:
- * type: integer
- * pages:
- * type: integer
- * 401:
- * description: 未授权
- * 403:
- * description: 权限不足
- * 500:
- * description: 服务器错误
- */
-/**
- * @swagger
- * /auth/register:
- * post:
- * summary: 用户注册
- * description: 需要提供有效的激活码才能注册
- * tags: [Authentication]
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * $ref: '#/components/schemas/RegisterRequest'
- * responses:
- * 201:
- * description: 用户注册成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * message:
- * type: string
- * token:
- * type: string
- * description: JWT认证令牌
- * user:
- * type: object
- * properties:
- * id:
- * type: integer
- * username:
- * type: string
- * role:
- * type: string
- * 400:
- * description: 请求参数错误
- * 500:
- * description: 服务器错误
- */
-
-/**
- * @swagger
- * /auth/login:
- * post:
- * summary: 用户登录
- * tags: [Authentication]
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * $ref: '#/components/schemas/LoginCredentials'
- * responses:
- * 200:
- * description: 登录成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * message:
- * type: string
- * token:
- * type: string
- * description: JWT认证令牌
- * user:
- * type: object
- * properties:
- * id:
- * type: integer
- * username:
- * type: string
- * role:
- * type: string
- * avatar:
- * type: string
- * points:
- * type: integer
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 用户名或密码错误
- * 403:
- * description: 账户审核未通过
- * 500:
- * description: 服务器错误
- */
-/**
- * @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: "服务器内部错误"
- */
-/**
- * @swagger
- * components:
- * schemas:
- * User:
- * type: object
- * required:
- * - username
- * - password
- * - real_name
- * - id_card
- * properties:
- * id:
- * type: integer
- * description: 用户ID
- * username:
- * type: string
- * description: 用户名
- * role:
- * type: string
- * description: 用户角色
- * enum: [user, admin, merchant]
- * avatar:
- * type: string
- * description: 用户头像URL
- * points:
- * type: integer
- * description: 用户积分
- * real_name:
- * type: string
- * description: 真实姓名
- * id_card:
- * type: string
- * description: 身份证号
- * phone:
- * type: string
- * description: 手机号
- * is_system_account:
- * type: boolean
- * description: 是否为系统账户
- * is_distribute:
- * type: boolean
- * description: 是否为分发账户
- * created_at:
- * type: string
- * format: date-time
- * description: 创建时间
- * updated_at:
- * type: string
- * format: date-time
- * description: 更新时间
- */
-
-/**
- * @swagger
- * /users:
- * post:
- * summary: 创建用户(管理员权限)
- * tags: [Users]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - username
- * - password
- * - real_name
- * - id_card
- * properties:
- * username:
- * type: string
- * password:
- * type: string
- * role:
- * type: string
- * enum: [user, admin, merchant]
- * default: user
- * is_system_account:
- * type: boolean
- * default: false
- * real_name:
- * type: string
- * id_card:
- * type: string
- * wechat_qr:
- * type: string
- * alipay_qr:
- * type: string
- * bank_card:
- * type: string
- * unionpay_qr:
- * type: string
- * phone:
- * type: string
- * responses:
- * 201:
- * description: 用户创建成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * message:
- * type: string
- * user:
- * $ref: '#/components/schemas/User'
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 403:
- * description: 权限不足
- * 500:
- * description: 服务器错误
- */
\ No newline at end of file
diff --git a/docs/schemas/announcement.js b/docs/schemas/announcement.js
deleted file mode 100644
index ddb6e92..0000000
--- a/docs/schemas/announcement.js
+++ /dev/null
@@ -1,225 +0,0 @@
-/**
- * @swagger
- * components:
- * schemas:
- * Announcement:
- * type: object
- * required:
- * - title
- * - content
- * - type
- * - priority
- * properties:
- * id:
- * type: integer
- * description: 公告ID
- * example: 1
- * title:
- * type: string
- * description: 公告标题
- * example: "系统维护通知"
- * content:
- * type: string
- * description: 公告内容
- * example: "系统将于今晚进行维护,预计维护时间2小时,期间可能影响部分功能使用。"
- * type:
- * type: string
- * description: 公告类型
- * enum: [system, activity, maintenance, urgent]
- * example: "maintenance"
- * priority:
- * type: string
- * description: 优先级
- * enum: [high, medium, low]
- * example: "high"
- * status:
- * type: string
- * description: 公告状态
- * enum: [draft, published, expired]
- * example: "published"
- * isTop:
- * type: boolean
- * description: 是否置顶
- * example: false
- * publishTime:
- * type: string
- * format: date-time
- * description: 发布时间
- * example: "2024-01-15T10:00:00Z"
- * expireTime:
- * type: string
- * format: date-time
- * description: 过期时间
- * example: "2024-01-20T10:00:00Z"
- * createdBy:
- * type: integer
- * description: 创建者用户ID
- * example: 1
- * createdAt:
- * type: string
- * format: date-time
- * description: 创建时间
- * example: "2024-01-15T09:00:00Z"
- * updatedAt:
- * type: string
- * format: date-time
- * description: 更新时间
- * example: "2024-01-15T09:30:00Z"
- * creator:
- * type: object
- * description: 创建者信息
- * properties:
- * id:
- * type: integer
- * example: 1
- * username:
- * type: string
- * example: "admin"
- * email:
- * type: string
- * example: "admin@example.com"
- *
- * AnnouncementCreate:
- * type: object
- * required:
- * - title
- * - content
- * - type
- * - priority
- * properties:
- * title:
- * type: string
- * description: 公告标题
- * example: "系统维护通知"
- * content:
- * type: string
- * description: 公告内容
- * example: "系统将于今晚进行维护,预计维护时间2小时。"
- * type:
- * type: string
- * description: 公告类型
- * enum: [system, activity, maintenance, urgent]
- * example: "maintenance"
- * priority:
- * type: string
- * description: 优先级
- * enum: [high, medium, low]
- * example: "high"
- * status:
- * type: string
- * description: 公告状态
- * enum: [draft, published]
- * default: draft
- * example: "draft"
- * isTop:
- * type: boolean
- * description: 是否置顶
- * default: false
- * example: false
- * publishTime:
- * type: string
- * format: date-time
- * description: 发布时间
- * example: "2024-01-15T10:00:00Z"
- * expireTime:
- * type: string
- * format: date-time
- * description: 过期时间
- * example: "2024-01-20T10:00:00Z"
- *
- * AnnouncementUpdate:
- * type: object
- * properties:
- * title:
- * type: string
- * description: 公告标题
- * example: "系统维护通知(更新)"
- * content:
- * type: string
- * description: 公告内容
- * example: "系统维护时间调整为明晚进行。"
- * type:
- * type: string
- * description: 公告类型
- * enum: [system, activity, maintenance, urgent]
- * example: "maintenance"
- * priority:
- * type: string
- * description: 优先级
- * enum: [high, medium, low]
- * example: "medium"
- * status:
- * type: string
- * description: 公告状态
- * enum: [draft, published, expired]
- * example: "published"
- * isTop:
- * type: boolean
- * description: 是否置顶
- * example: true
- * publishTime:
- * type: string
- * format: date-time
- * description: 发布时间
- * example: "2024-01-16T10:00:00Z"
- * expireTime:
- * type: string
- * format: date-time
- * description: 过期时间
- * example: "2024-01-21T10:00:00Z"
- *
- * AnnouncementList:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: object
- * properties:
- * announcements:
- * type: array
- * items:
- * $ref: '#/components/schemas/Announcement'
- * total:
- * type: integer
- * description: 总记录数
- * example: 50
- * page:
- * type: integer
- * description: 当前页码
- * example: 1
- * limit:
- * type: integer
- * description: 每页数量
- * example: 10
- * totalPages:
- * type: integer
- * description: 总页数
- * example: 5
- *
- * AnnouncementResponse:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: "操作成功"
- * data:
- * $ref: '#/components/schemas/Announcement'
- *
- * AnnouncementError:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: false
- * message:
- * type: string
- * example: "操作失败"
- * error:
- * type: string
- * example: "公告不存在"
- */
\ No newline at end of file
diff --git a/docs/schemas/cart.js b/docs/schemas/cart.js
deleted file mode 100644
index 71ffbe9..0000000
--- a/docs/schemas/cart.js
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * @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
deleted file mode 100644
index 9151207..0000000
--- a/docs/schemas/order.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/**
- * @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
deleted file mode 100644
index 314372d..0000000
--- a/docs/schemas/product.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * @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
deleted file mode 100644
index 08288a1..0000000
--- a/docs/schemas/user.js
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * @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/export-swagger.js b/export-swagger.js
deleted file mode 100644
index e086c50..0000000
--- a/export-swagger.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * 导出Swagger文档到JSON文件,用于导入Apifox
- */
-const fs = require('fs');
-const path = require('path');
-const swaggerSpecs = require('./swagger');
-
-// 确保输出目录存在
-const outputDir = path.join(__dirname, 'api-docs');
-if (!fs.existsSync(outputDir)) {
- fs.mkdirSync(outputDir, { recursive: true });
-}
-
-// 导出Swagger文档到JSON文件
-const outputPath = path.join(outputDir, 'swagger.json');
-fs.writeFileSync(outputPath, JSON.stringify(swaggerSpecs, null, 2));
-
-console.log(`Swagger文档已导出到: ${outputPath}`);
-console.log('现在您可以将此文件导入到Apifox中:');
-console.log('1. 打开Apifox应用');
-console.log('2. 选择您的项目');
-console.log('3. 点击"导入"按钮');
-console.log('4. 选择"导入OpenAPI(Swagger)"');
-console.log('5. 选择刚才导出的swagger.json文件');
-console.log('6. 按照Apifox的导入向导完成导入');
\ No newline at end of file
diff --git a/middleware/auth.js b/middleware/auth.js
index ddb8180..66f041c 100644
--- a/middleware/auth.js
+++ b/middleware/auth.js
@@ -10,7 +10,6 @@ const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key'; // 在生产环
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization')?.replace('Bearer ', '');
-
if (!token) {
return res.status(401).json({ success: false, message: '未提供认证令牌' });
}
@@ -48,6 +47,7 @@ const auth = async (req, res, next) => {
req.user = user;
next();
} catch (error) {
+ console.log(error);
res.status(401).json({ success: false, message: '无效的认证令牌' });
}
};
diff --git a/migrations/add_alipay_support.sql b/migrations/add_alipay_support.sql
deleted file mode 100644
index 36d716d..0000000
--- a/migrations/add_alipay_support.sql
+++ /dev/null
@@ -1,17 +0,0 @@
--- 为支付订单表添加支付宝支持
--- 更新交易类型枚举,添加支付宝相关类型
-ALTER TABLE `payment_orders` MODIFY COLUMN `trade_type` varchar(32) NOT NULL COMMENT '交易类型(H5/JSAPI/ALIPAY_WAP/ALIPAY_APP等)';
-
--- 更新transaction_id字段注释,支持多种支付方式
-ALTER TABLE `payment_orders` MODIFY COLUMN `transaction_id` varchar(64) DEFAULT NULL COMMENT '第三方支付订单号(微信/支付宝)';
-
--- 添加支付方式字段
-ALTER TABLE `payment_orders` ADD COLUMN `payment_method` enum('wechat','alipay') DEFAULT 'wechat' COMMENT '支付方式' AFTER `trade_type`;
-
--- 添加支付宝特有字段
-ALTER TABLE `payment_orders` ADD COLUMN `buyer_user_id` varchar(32) DEFAULT NULL COMMENT '支付宝买家用户ID' AFTER `transaction_id`;
-ALTER TABLE `payment_orders` ADD COLUMN `trade_status` varchar(32) DEFAULT NULL COMMENT '支付宝交易状态' AFTER `buyer_user_id`;
-
--- 添加索引
-ALTER TABLE `payment_orders` ADD KEY `idx_payment_method` (`payment_method`);
-ALTER TABLE `payment_orders` ADD KEY `idx_trade_status` (`trade_status`);
\ No newline at end of file
diff --git a/migrations/create_payment_orders_table.sql b/migrations/create_payment_orders_table.sql
deleted file mode 100644
index fb4ea53..0000000
--- a/migrations/create_payment_orders_table.sql
+++ /dev/null
@@ -1,27 +0,0 @@
--- 创建支付订单表
-CREATE TABLE IF NOT EXISTS `payment_orders` (
- `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
- `user_id` int(11) NOT NULL COMMENT '用户ID',
- `out_trade_no` varchar(64) NOT NULL COMMENT '商户订单号',
- `transaction_id` varchar(64) DEFAULT NULL COMMENT '微信支付订单号',
- `total_fee` int(11) NOT NULL COMMENT '订单金额(分)',
- `body` varchar(128) NOT NULL COMMENT '商品描述',
- `trade_type` varchar(16) NOT NULL COMMENT '交易类型',
- `prepay_id` varchar(64) DEFAULT NULL COMMENT '预支付交易会话标识',
- `mweb_url` text DEFAULT NULL COMMENT 'H5支付跳转链接',
- `status` enum('pending','paid','failed','cancelled') NOT NULL DEFAULT 'pending' COMMENT '支付状态',
- `paid_at` datetime DEFAULT NULL COMMENT '支付完成时间',
- `created_at` datetime NOT NULL COMMENT '创建时间',
- `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
- PRIMARY KEY (`id`),
- UNIQUE KEY `uk_out_trade_no` (`out_trade_no`),
- KEY `idx_user_id` (`user_id`),
- KEY `idx_status` (`status`),
- KEY `idx_created_at` (`created_at`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付订单表';
-
--- 为用户表添加支付状态字段
-ALTER TABLE `users` ADD COLUMN `payment_status` enum('unpaid','paid') NOT NULL DEFAULT 'unpaid' COMMENT '支付状态' AFTER `status`;
-
--- 添加索引
-ALTER TABLE `users` ADD KEY `idx_payment_status` (`payment_status`);
\ No newline at end of file
diff --git a/routes/address-labels.js b/routes/address-labels.js
deleted file mode 100644
index b304bce..0000000
--- a/routes/address-labels.js
+++ /dev/null
@@ -1,180 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const { getDB } = require('../database');
-const { auth } = require('../middleware/auth');
-
-// 获取地址标签列表(包含系统默认和用户自定义)
-router.get('/', auth, async (req, res) => {
- try {
- const userId = req.user.id;
-
- const [labels] = await getDB().execute(
- `SELECT * FROM address_labels
- WHERE user_id IS NULL OR user_id = ?
- ORDER BY is_system DESC, created_at ASC`,
- [userId]
- );
-
- res.json({
- success: true,
- data: labels
- });
- } catch (error) {
- console.error('获取地址标签列表错误:', error);
- res.status(500).json({ message: '获取地址标签列表失败' });
- }
-});
-
-// 创建自定义地址标签
-router.post('/', auth, async (req, res) => {
- try {
- const userId = req.user.id;
- const { name, color = '#1890ff' } = req.body;
-
- if (!name || name.trim() === '') {
- return res.status(400).json({ message: '标签名称不能为空' });
- }
-
- // 检查标签名称是否已存在(系统标签或用户自定义标签)
- const [existing] = await getDB().execute(
- `SELECT id FROM address_labels
- WHERE name = ? AND (user_id IS NULL OR user_id = ?)`,
- [name.trim(), userId]
- );
-
- if (existing.length > 0) {
- return res.status(400).json({ message: '标签名称已存在' });
- }
-
- const [result] = await getDB().execute(
- `INSERT INTO address_labels (name, color, user_id, is_system, created_at, updated_at)
- VALUES (?, ?, ?, false, NOW(), NOW())`,
- [name.trim(), color, userId]
- );
-
- res.status(201).json({
- success: true,
- message: '地址标签创建成功',
- data: { labelId: result.insertId }
- });
- } catch (error) {
- console.error('创建地址标签错误:', error);
- res.status(500).json({ message: '创建地址标签失败' });
- }
-});
-
-// 更新自定义地址标签
-router.put('/:id', auth, async (req, res) => {
- try {
- const labelId = req.params.id;
- const userId = req.user.id;
- const { name, color } = req.body;
-
- // 检查标签是否存在且属于当前用户(不能修改系统标签)
- const [existing] = await getDB().execute(
- 'SELECT id, is_system FROM address_labels WHERE id = ? AND user_id = ?',
- [labelId, userId]
- );
-
- if (existing.length === 0) {
- return res.status(404).json({ message: '地址标签不存在或无权限修改' });
- }
-
- if (existing[0].is_system) {
- return res.status(403).json({ message: '系统标签不能修改' });
- }
-
- if (name && name.trim() !== '') {
- // 检查新名称是否已存在
- const [nameExists] = await getDB().execute(
- `SELECT id FROM address_labels
- WHERE name = ? AND id != ? AND (user_id IS NULL OR user_id = ?)`,
- [name.trim(), labelId, userId]
- );
-
- if (nameExists.length > 0) {
- return res.status(400).json({ message: '标签名称已存在' });
- }
- }
-
- const updateFields = [];
- const updateValues = [];
-
- if (name && name.trim() !== '') {
- updateFields.push('name = ?');
- updateValues.push(name.trim());
- }
-
- if (color) {
- updateFields.push('color = ?');
- updateValues.push(color);
- }
-
- if (updateFields.length === 0) {
- return res.status(400).json({ message: '没有需要更新的字段' });
- }
-
- updateFields.push('updated_at = NOW()');
- updateValues.push(labelId, userId);
-
- await getDB().execute(
- `UPDATE address_labels SET ${updateFields.join(', ')} WHERE id = ? AND user_id = ?`,
- updateValues
- );
-
- res.json({
- success: true,
- message: '地址标签更新成功'
- });
- } catch (error) {
- console.error('更新地址标签错误:', error);
- res.status(500).json({ message: '更新地址标签失败' });
- }
-});
-
-// 删除自定义地址标签
-router.delete('/:id', auth, async (req, res) => {
- try {
- const labelId = req.params.id;
- const userId = req.user.id;
-
- // 检查标签是否存在且属于当前用户(不能删除系统标签)
- const [existing] = await getDB().execute(
- 'SELECT id, is_system FROM address_labels WHERE id = ? AND user_id = ?',
- [labelId, userId]
- );
-
- if (existing.length === 0) {
- return res.status(404).json({ message: '地址标签不存在或无权限删除' });
- }
-
- if (existing[0].is_system) {
- return res.status(403).json({ message: '系统标签不能删除' });
- }
-
- // 检查是否有地址正在使用该标签
- const [addressesUsingLabel] = await getDB().execute(
- 'SELECT COUNT(*) as count FROM user_addresses WHERE label_id = ? AND deleted_at IS NULL',
- [labelId]
- );
-
- if (addressesUsingLabel[0].count > 0) {
- return res.status(400).json({ message: '该标签正在被使用,无法删除' });
- }
-
- await getDB().execute(
- 'DELETE FROM address_labels WHERE id = ? AND user_id = ?',
- [labelId, userId]
- );
-
- res.json({
- success: true,
- message: '地址标签删除成功'
- });
- } catch (error) {
- console.error('删除地址标签错误:', error);
- res.status(500).json({ message: '删除地址标签失败' });
- }
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/addresses.js b/routes/addresses.js
deleted file mode 100644
index 101bf80..0000000
--- a/routes/addresses.js
+++ /dev/null
@@ -1,567 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const { getDB } = require('../database');
-const { auth } = require('../middleware/auth');
-
-/**
- * @swagger
- * components:
- * schemas:
- * Address:
- * type: object
- * properties:
- * id:
- * type: integer
- * description: 地址ID
- * user_id:
- * type: integer
- * description: 用户ID
- * recipient_name:
- * type: string
- * description: 收件人姓名
- * phone:
- * type: string
- * description: 联系电话
- * province_code:
- * type: string
- * description: 省份编码
- * province_name:
- * type: string
- * description: 省份名称
- * city_code:
- * type: string
- * description: 城市编码
- * city_name:
- * type: string
- * description: 城市名称
- * district_code:
- * type: string
- * description: 区县编码
- * district_name:
- * type: string
- * description: 区县名称
- * detailed_address:
- * type: string
- * description: 详细地址
- * postal_code:
- * type: string
- * description: 邮政编码
- * label_id:
- * type: integer
- * description: 地址标签ID
- * is_default:
- * type: boolean
- * description: 是否为默认地址
- * label_name:
- * type: string
- * description: 标签名称
- * label_color:
- * type: string
- * description: 标签颜色
- * required:
- * - recipient_name
- * - phone
- * - province_code
- * - province_name
- * - city_code
- * - city_name
- * - district_code
- * - district_name
- * - detailed_address
- */
-
-/**
- * @swagger
- * /addresses:
- * get:
- * summary: 获取用户收货地址列表
- * tags: [Addresses]
- * security:
- * - bearerAuth: []
- * responses:
- * 200:
- * description: 成功获取地址列表
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: array
- * items:
- * $ref: '#/components/schemas/Address'
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-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,
- 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 = 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
- });
- } catch (error) {
- console.error('获取收货地址列表错误:', error);
- res.status(500).json({ message: '获取收货地址列表失败' });
- }
-});
-
-/**
- * @swagger
- * /addresses/{id}:
- * get:
- * summary: 获取单个收货地址详情
- * tags: [Addresses]
- * 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:
- * $ref: '#/components/schemas/Address'
- * 401:
- * description: 未授权
- * 404:
- * description: 地址不存在
- * 500:
- * description: 服务器错误
- */
-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
- LEFT JOIN address_labels al ON ua.label_id = al.id
- 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]
- });
- } catch (error) {
- console.error('获取收货地址详情错误:', error);
- res.status(500).json({ message: '获取收货地址详情失败' });
- }
-});
-
-/**
- * @swagger
- * /addresses:
- * post:
- * summary: 创建收货地址
- * tags: [Addresses]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * recipient_name:
- * type: string
- * description: 收件人姓名
- * phone:
- * type: string
- * description: 联系电话
- * province_code:
- * type: string
- * description: 省份编码
- * province_name:
- * type: string
- * description: 省份名称
- * city_code:
- * type: string
- * description: 城市编码
- * city_name:
- * type: string
- * description: 城市名称
- * district_code:
- * type: string
- * description: 区县编码
- * district_name:
- * type: string
- * description: 区县名称
- * detailed_address:
- * type: string
- * description: 详细地址
- * postal_code:
- * type: string
- * description: 邮政编码
- * label_id:
- * type: integer
- * description: 地址标签ID
- * is_default:
- * type: boolean
- * description: 是否为默认地址
- * required:
- * - recipient_name
- * - phone
- * - province_code
- * - province_name
- * - city_code
- * - city_name
- * - district_code
- * - district_name
- * - detailed_address
- * responses:
- * 201:
- * description: 地址创建成功
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-router.post('/', auth, async (req, res) => {
- try {
- const userId = req.user.id;
- const {
- recipient_name,
- phone,
- province_code,
- city_code,
- district_code,
- detailed_address,
- 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 = ? ',
- [userId]
- );
- }
-
- const [result] = await getDB().execute(
- `INSERT INTO user_addresses (
- 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, city_code,
- district_code, detailed_address, is_default
- ]
- );
-
- res.status(201).json({
- success: true,
- message: '收货地址创建成功',
- data: { addressId: result.insertId }
- });
- } catch (error) {
- console.error('创建收货地址错误:', error);
- res.status(500).json({ message: '创建收货地址失败' });
- }
-});
-
-/**
- * @swagger
- * /addresses/{id}:
- * put:
- * summary: 更新收货地址
- * tags: [Addresses]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: id
- * required: true
- * schema:
- * type: integer
- * description: 地址ID
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * recipient_name:
- * type: string
- * description: 收件人姓名
- * phone:
- * type: string
- * description: 联系电话
- * province_code:
- * type: string
- * description: 省份编码
- * province_name:
- * type: string
- * description: 省份名称
- * city_code:
- * type: string
- * description: 城市编码
- * city_name:
- * type: string
- * description: 城市名称
- * district_code:
- * type: string
- * description: 区县编码
- * district_name:
- * type: string
- * description: 区县名称
- * detailed_address:
- * type: string
- * description: 详细地址
- * postal_code:
- * type: string
- * description: 邮政编码
- * label_id:
- * type: integer
- * description: 地址标签ID
- * is_default:
- * type: boolean
- * description: 是否为默认地址
- * responses:
- * 200:
- * description: 地址更新成功
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 404:
- * description: 地址不存在
- * 500:
- * description: 服务器错误
- */
-router.put('/:id', auth, async (req, res) => {
- try {
- const addressId = req.params.id;
- const userId = req.user.id;
- const {
- recipient_name,
- phone,
- province_code,
- city_code,
- district_code,
- detailed_address,
- 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 = ? ',
- [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 != ? ',
- [userId, addressId]
- );
- }
-
- const [result] = await getDB().execute(
- `UPDATE user_addresses SET
- receiver_name = ?, receiver_phone = ?, province = ?, city = ?,
- district = ?, detailed_address = ?, is_default = ?, updated_at = NOW()
- WHERE id = ? AND user_id = ?`,
- [
- recipient_name, phone, province_code, city_code,
- district_code, detailed_address, is_default,
- addressId, userId
- ]
- );
-
- res.json({
- success: true,
- message: '收货地址更新成功'
- });
- } catch (error) {
- console.error('更新收货地址错误:', error);
- res.status(500).json({ message: '更新收货地址失败' });
- }
-});
-
-/**
- * @swagger
- * /addresses/{id}:
- * delete:
- * summary: 删除收货地址
- * tags: [Addresses]
- * 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
- * example: true
- * message:
- * type: string
- * example: 收货地址删除成功
- * 401:
- * description: 未授权
- * 404:
- * description: 地址不存在
- * 500:
- * description: 服务器错误
- */
-router.delete('/:id', auth, async (req, res) => {
- try {
- const addressId = req.params.id;
- const userId = req.user.id;
-
- const [result] = await getDB().execute(
- '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: '收货地址删除成功'
- });
- } catch (error) {
- console.error('删除收货地址错误:', error);
- res.status(500).json({ message: '删除收货地址失败' });
- }
-});
-
-/**
- * @swagger
- * /addresses/{id}/default:
- * put:
- * summary: 设置默认地址
- * tags: [Addresses]
- * 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
- * example: true
- * message:
- * type: string
- * example: 默认地址设置成功
- * 401:
- * description: 未授权
- * 404:
- * description: 地址不存在
- * 500:
- * description: 服务器错误
- */
-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: '默认地址设置成功'
- });
- } catch (error) {
- console.error('设置默认地址错误:', error);
- res.status(500).json({ message: '设置默认地址失败' });
- }
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/agent-withdrawals.js b/routes/agent-withdrawals.js
deleted file mode 100644
index 7b4cd34..0000000
--- a/routes/agent-withdrawals.js
+++ /dev/null
@@ -1,475 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const { getDB } = require('../database');
-const { auth } = require('../middleware/auth');
-const multer = require('multer');
-const path = require('path');
-const fs = require('fs');
-
-// 配置multer用于文件上传
-const storage = multer.diskStorage({
- destination: function (req, file, cb) {
- const uploadDir = 'uploads/qr-codes';
- // 确保上传目录存在
- if (!fs.existsSync(uploadDir)) {
- fs.mkdirSync(uploadDir, { recursive: true });
- }
- cb(null, uploadDir);
- },
- filename: function (req, file, cb) {
- // 生成唯一文件名
- const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
- cb(null, 'qr-code-' + uniqueSuffix + path.extname(file.originalname));
- }
-});
-
-// 文件过滤器
-const fileFilter = (req, file, cb) => {
- // 只允许图片文件
- if (file.mimetype.startsWith('image/')) {
- cb(null, true);
- } else {
- cb(new Error('只允许上传图片文件'), false);
- }
-};
-
-const upload = multer({
- storage: storage,
- fileFilter: fileFilter,
- limits: {
- fileSize: 5 * 1024 * 1024 // 限制文件大小为5MB
- }
-});
-
-// 获取数据库连接
-const db = {
- query: async (sql, params = []) => {
- const connection = getDB();
- const [rows] = await connection.execute(sql, params);
- return rows;
- }
-};
-
-/**
- * 检查用户是否为代理商
- */
-const requireAgent = async (req, res, next) => {
- try {
- const userId = req.user.id;
-
- // 查询用户是否为代理商
- const agentResult = await db.query(
- 'SELECT * FROM regional_agents WHERE user_id = ? AND status = "active"',
- [userId]
- );
-
- if (!agentResult || agentResult.length === 0) {
- return res.status(403).json({ success: false, message: '您不是活跃的代理商' });
- }
-
- req.agent = agentResult[0];
- next();
- } catch (error) {
- console.error('检查代理商身份失败:', error);
- res.status(500).json({ success: false, message: '检查代理商身份失败' });
- }
-};
-
-/**
- * 获取代理商佣金统计信息
- */
-router.get('/stats', auth, requireAgent, async (req, res) => {
- try {
- const agentId = req.agent.id;
-
- // 查询佣金统计
- const statsQuery = `
- SELECT
- CAST(COALESCE(commission_sum.total_commission, 0) AS DECIMAL(10,2)) as total_commission,
- CAST(COALESCE(ra.withdrawn_amount, 0) AS DECIMAL(10,2)) as withdrawn_amount,
- CAST(COALESCE(ra.pending_withdrawal, 0) AS DECIMAL(10,2)) as pending_withdrawal,
- CAST(COALESCE(commission_sum.total_commission, 0) - COALESCE(ra.withdrawn_amount, 0) - COALESCE(ra.pending_withdrawal, 0) AS DECIMAL(10,2)) as available_amount
- FROM regional_agents ra
- LEFT JOIN (
- SELECT agent_id, SUM(commission_amount) as total_commission
- FROM agent_commission_records
- WHERE agent_id = ?
- GROUP BY agent_id
- ) commission_sum ON ra.id = commission_sum.agent_id
- WHERE ra.id = ?
- `;
-
- const statsResult = await db.query(statsQuery, [agentId, agentId]);
- const stats = statsResult && statsResult.length > 0 ? statsResult[0] : {
- total_commission: 0,
- withdrawn_amount: 0,
- pending_withdrawal: 0,
- available_amount: 0
- };
-
- // 查询代理商信息包括收款方式
- const agentInfo = await db.query(
- 'SELECT payment_type, bank_name, account_number, account_holder, qr_code_url, bank_account FROM regional_agents WHERE id = ?',
- [agentId]
- );
-
- const agent = agentInfo[0] || {};
-
- // 构建收款方式信息,兼容旧数据
- const paymentInfo = {
- payment_type: agent.payment_type || 'bank',
- bank_name: agent.bank_name || '',
- account_number: agent.account_number || agent.bank_account, // 兼容旧字段
- account_holder: agent.account_holder,
- qr_code_url: agent.qr_code_url || ''
- };
-
- // 兼容旧的bankInfo字段
- const bankInfo = {
- bank_name: agent.bank_name || '',
- bank_account: agent.bank_account || agent.account_number,
- account_holder: agent.account_holder
- };
-
- res.json({
- success: true,
- data: {
- ...stats,
- paymentInfo: paymentInfo,
- bank_info: bankInfo // 保持向后兼容
- }
- });
- } catch (error) {
- console.error('获取佣金统计失败:', error);
- res.status(500).json({ success: false, message: '获取佣金统计失败' });
- }
-});
-
-/**
- * 更新收款方式信息
- */
-router.put('/payment-info', auth, requireAgent, async (req, res) => {
- try {
- const { payment_type, bank_name, account_number, account_holder, qr_code_url } = req.body;
- const agentId = req.agent.id;
-
- // 验证收款方式类型
- const validPaymentTypes = ['bank', 'wechat', 'alipay', 'unionpay'];
- if (!validPaymentTypes.includes(payment_type)) {
- return res.status(400).json({ success: false, message: '收款方式类型不正确' });
- }
-
- // 根据收款方式类型进行不同的验证
- if (payment_type === 'bank') {
- // 银行卡验证
- if (!bank_name || !account_number || !account_holder) {
- return res.status(400).json({ success: false, message: '银行信息不完整' });
- }
- // 验证银行账号格式(简单验证)
- if (!/^\d{10,25}$/.test(account_number.replace(/\s/g, ''))) {
- return res.status(400).json({ success: false, message: '银行账号格式不正确' });
- }
- } else {
- // 收款码验证
- if (!account_holder || !qr_code_url) {
- return res.status(400).json({ success: false, message: '收款码信息不完整' });
- }
- }
-
- // 更新收款方式信息
- await db.query(
- 'UPDATE regional_agents SET payment_type = ?, bank_name = ?, account_number = ?, account_holder = ?, qr_code_url = ? WHERE id = ?',
- [payment_type, bank_name, account_number, account_holder, qr_code_url, agentId]
- );
-
- res.json({
- success: true,
- message: '收款方式信息更新成功'
- });
- } catch (error) {
- console.error('更新收款方式信息失败:', error);
- res.status(500).json({ success: false, message: '更新收款方式信息失败' });
- }
-});
-
-/**
- * 上传收款码图片
- */
-router.post('/upload-qr-code', auth, requireAgent, upload.single('qrCode'), async (req, res) => {
- try {
- if (!req.file) {
- return res.status(400).json({ success: false, message: '请选择要上传的图片' });
- }
-
- // 构建文件访问URL
- const fileUrl = `/uploads/qr-codes/${req.file.filename}`;
-
- res.json({
- success: true,
- message: '收款码上传成功',
- data: {
- url: fileUrl,
- filename: req.file.filename
- }
- });
- } catch (error) {
- console.error('上传收款码失败:', error);
- res.status(500).json({ success: false, message: '上传收款码失败' });
- }
-});
-
-/**
- * 兼容旧的银行信息接口
- */
-router.put('/bank-info', auth, requireAgent, async (req, res) => {
- try {
- const agentId = req.agent.id;
- const { bank_name, bank_account, account_holder } = req.body;
-
- // 验证必填字段
- if (!bank_name || !bank_account || !account_holder) {
- return res.status(400).json({ success: false, message: '银行信息不完整' });
- }
-
- // 验证银行账号格式(简单验证)
- if (!/^\d{10,25}$/.test(bank_account)) {
- return res.status(400).json({ success: false, message: '银行账号格式不正确' });
- }
-
- // 更新银行信息
- await db.query(
- 'UPDATE regional_agents SET payment_type = "bank", bank_name = ?, account_number = ?, account_holder = ?, bank_account = ? WHERE id = ?',
- [bank_name, bank_account, account_holder, bank_account, agentId]
- );
-
- res.json({
- success: true,
- message: '银行信息更新成功'
- });
- } catch (error) {
- console.error('更新银行信息失败:', error);
- res.status(500).json({ success: false, message: '更新银行信息失败' });
- }
-});
-
-/**
- * 申请提现
- */
-router.post('/apply', auth, requireAgent, async (req, res) => {
- try {
- const agentId = req.agent.id;
- const { amount, apply_note } = req.body;
-
- // 验证提现金额
- if (!amount || amount <= 0) {
- return res.status(400).json({ success: false, message: '提现金额必须大于0' });
- }
-
- if (amount < 10) {
- return res.status(400).json({ success: false, message: '最低提现金额为100元' });
- }
-
- // 查询代理商信息和可提现金额
- const agentQuery = `
- SELECT
- ra.*,
- CAST(COALESCE(SUM(acr.commission_amount), 0) - COALESCE(ra.withdrawn_amount, 0) - COALESCE(ra.pending_withdrawal, 0) AS DECIMAL(10,2)) as available_amount
- FROM regional_agents ra
- LEFT JOIN agent_commission_records acr ON ra.id = acr.agent_id
- WHERE ra.id = ?
- GROUP BY ra.id
- `;
-
- const agentResult = await db.query(agentQuery, [agentId]);
-
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理商信息不存在' });
- }
-
- const agent = agentResult[0];
-
- // 检查收款方式信息是否完整
- const paymentType = agent.payment_type || 'bank';
-
- if (paymentType === 'bank') {
- // 银行卡收款方式验证
- if (!agent.bank_name || !agent.account_number || !agent.account_holder) {
- return res.status(400).json({ success: false, message: '请先完善银行信息' });
- }
- } else {
- // 收款码收款方式验证
- if (!agent.account_holder || !agent.qr_code_url) {
- return res.status(400).json({ success: false, message: '请先完善收款码信息' });
- }
- }
-
- // 检查可提现金额
- if (amount > agent.available_amount) {
- return res.status(400).json({
- success: false,
- message: `可提现金额不足,当前可提现:¥${agent.available_amount}`
- });
- }
-
- // 开始事务
- const pool = getDB();
- const connection = await pool.getConnection();
- await connection.beginTransaction();
-
- try {
- // 创建提现申请
- await connection.execute(
- 'INSERT INTO agent_withdrawals (agent_id, amount, payment_type, bank_name, account_number, account_holder, qr_code_url, apply_note, bank_account) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
- [agentId, amount, paymentType, agent.bank_name || '', agent.account_number, agent.account_holder, agent.qr_code_url, apply_note || null, agent.account_number]
- );
-
- // 更新代理的待提现金额
- await connection.execute(
- 'UPDATE regional_agents SET pending_withdrawal = pending_withdrawal + ? WHERE id = ?',
- [amount, agentId]
- );
-
- await connection.commit();
- connection.release(); // 释放连接回连接池
-
- res.json({
- success: true,
- message: '提现申请提交成功,请等待审核',
- data: {
- paymentType: paymentType
- }
- });
- } catch (error) {
- await connection.rollback();
- connection.release(); // 释放连接回连接池
- throw error;
- }
- } catch (error) {
- console.error('申请提现失败:', error);
- res.status(500).json({ success: false, message: '申请提现失败' });
- }
-});
-
-/**
- * 获取提现记录
- */
-router.get('/records', auth, requireAgent, async (req, res) => {
- try {
- const agentId = req.agent.id;
- const { page = 1, limit = 20, status } = req.query;
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(limit) || 20;
- const offset = (pageNum - 1) * limitNum;
-
- // 构建查询条件
- let whereConditions = ['agent_id = ?'];
- let queryParams = [agentId];
-
- if (status) {
- whereConditions.push('status = ?');
- queryParams.push(status);
- }
-
- const whereClause = whereConditions.join(' AND ');
-
- // 查询提现记录
- const recordsQuery = `
- SELECT
- id,
- amount,
- payment_type,
- bank_name,
- account_number,
- account_holder,
- qr_code_url,
- status,
- apply_note,
- admin_note,
- created_at,
- processed_at,
- bank_account
- FROM agent_withdrawals
- WHERE ${whereClause}
- ORDER BY created_at DESC
- LIMIT ${limitNum} OFFSET ${offset}
- `;
-
- const records = await db.query(recordsQuery, queryParams);
-
- // 处理记录数据,兼容旧格式
- const processedRecords = records.map(record => ({
- ...record,
- payment_type: record.payment_type || 'bank',
- account_number: record.account_number || record.bank_account,
- qr_code_url: record.qr_code_url || '',
- // 保持向后兼容
- bank_account: record.bank_account || record.account_number
- }));
-
- // 查询总数
- const totalResult = await db.query(
- `SELECT COUNT(*) as total FROM agent_withdrawals WHERE ${whereClause}`,
- queryParams
- );
- const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0;
-
- res.json({
- success: true,
- data: {
- records: processedRecords,
- total: parseInt(total)
- }
- });
- } catch (error) {
- console.error('获取提现记录失败:', error);
- res.status(500).json({ success: false, message: '获取提现记录失败' });
- }
-});
-
-/**
- * 获取佣金明细
- */
-router.get('/commissions', auth, requireAgent, async (req, res) => {
- try {
- const agentId = req.agent.id;
- const { page = 1, limit = 20 } = req.query;
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(limit) || 20;
- const offset = (pageNum - 1) * limitNum;
-
- // 查询佣金记录
- const commissionsQuery = `
- SELECT
- acr.*,
- u.real_name as merchant_name,
- CONCAT(SUBSTRING(u.phone, 1, 3), '****', SUBSTRING(u.phone, -4)) as merchant_phone_masked
- FROM agent_commission_records acr
- JOIN users u ON acr.merchant_id = u.id
- WHERE acr.agent_id = ?
- ORDER BY acr.created_at DESC
- LIMIT ${limitNum} OFFSET ${offset}
- `;
-
- const commissions = await db.query(commissionsQuery, [agentId]);
-
- // 查询总数
- const totalResult = await db.query(
- 'SELECT COUNT(*) as total FROM agent_commission_records WHERE agent_id = ?',
- [agentId]
- );
- const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0;
-
- res.json({
- success: true,
- data: {
- commissions,
- total: parseInt(total)
- }
- });
- } catch (error) {
- console.error('获取佣金明细失败:', error);
- res.status(500).json({ success: false, message: '获取佣金明细失败' });
- }
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/agents.js b/routes/agents.js
deleted file mode 100644
index 76147c7..0000000
--- a/routes/agents.js
+++ /dev/null
@@ -1,777 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const {getDB} = require('../database');
-const QRCode = require('qrcode');
-const crypto = require('crypto');
-const bcrypt = require('bcryptjs');
-const {auth} = require('../middleware/auth');
-const dayjs = require('dayjs');
-
-// 获取浙江省所有区域列表
-router.get('/regions', async (req, res) => {
- try {
- const [regions] = await getDB().execute(
- 'SELECT * FROM zhejiang_regions ORDER BY city_name, district_name'
- );
- res.json({success: true, data: regions});
- } catch (error) {
- console.error('获取区域列表失败:', error);
- res.status(500).json({success: false, message: '获取区域列表失败'});
- }
-});
-
-// 申请成为区域代理
-router.post('/apply', async (req, res) => {
- try {
- const {region_id, real_name, phone, id_card, contact_address} = req.body;
-
- if (!region_id || !real_name || !phone || !id_card) {
- return res.status(400).json({success: false, message: '请填写完整信息'});
- }
-
- // 检查该区域是否已有代理(包括所有状态,不仅仅是active)
- const [existingRegionAgent] = await getDB().execute(
- 'SELECT id, status FROM regional_agents WHERE region_id = ? AND status IN ("pending", "active")',
- [region_id]
- );
-
- if (existingRegionAgent.length > 0) {
- const status = existingRegionAgent[0].status;
- if (status === 'active') {
- return res.status(400).json({
- success: false,
- message: '该区域已有激活的代理,每个区域只能有一个代理账号'
- });
- } else if (status === 'pending') {
- return res.status(400).json({
- success: false,
- message: '该区域已有待审核的代理申请,每个区域只能有一个代理账号'
- });
- }
- }
-
- // 检查手机号是否已存在用户
- const [existingUser] = await getDB().execute(
- 'SELECT id FROM users WHERE phone = ?',
- [phone]
- );
-
- let userId;
- if (existingUser.length > 0) {
- userId = existingUser[0].id;
-
- // 检查该用户是否已申请过代理(包括所有状态)
- const [existingUserAgent] = await getDB().execute(
- 'SELECT id, status, region_id FROM regional_agents WHERE user_id = ?',
- [userId]
- );
-
- if (existingUserAgent.length > 0) {
- const agentStatus = existingUserAgent[0].status;
- if (agentStatus === 'active') {
- return res.status(400).json({
- success: false,
- message: '该用户已是其他区域的激活代理,一个用户只能申请一个区域的代理'
- });
- } else if (agentStatus === 'pending') {
- return res.status(400).json({
- success: false,
- message: '该用户已有待审核的代理申请,一个用户只能申请一个区域的代理'
- });
- } else if (agentStatus === 'suspended' || agentStatus === 'terminated') {
- return res.status(400).json({
- success: false,
- message: '该用户的代理资格已被暂停或终止,无法重新申请'
- });
- }
- }
- } else {
- // 创建新用户(为代理申请用户生成临时密码)
- const bcrypt = require('bcryptjs');
- const tempPassword = Math.random().toString(36).slice(-8); // 生成8位临时密码
- const hashedPassword = await bcrypt.hash(tempPassword, 10);
-
- const [userResult] = await getDB().execute(
- 'INSERT INTO users (username, password, phone, real_name, id_card, created_at) VALUES (?, ?, ?, ?, ?, NOW())',
- [phone, hashedPassword, phone, real_name, id_card]
- );
- userId = userResult.insertId;
- }
-
- // 生成代理编码
- const agentCode = 'AG' + Date.now().toString().slice(-8);
-
- // 创建代理申请
- await getDB().execute(
- 'INSERT INTO regional_agents (user_id, region_id, agent_code, status, created_at) VALUES (?, ?, ?, "pending", NOW())',
- [userId, region_id, agentCode]
- );
-
- res.json({success: true, message: '申请提交成功,请等待审核'});
- } catch (error) {
- console.error('申请代理失败:', error);
- res.status(500).json({success: false, message: '申请失败'});
- }
-});
-
-// 代理登录
-router.post('/login', async (req, res) => {
- try {
- const {phone, password} = req.body;
-
- if (!phone || !password) {
- return res.status(400).json({success: false, message: '请输入手机号和密码'});
- }
-
- // 先查询用户和代理信息(包含密码用于验证)
- const [agents] = await getDB().execute(
- `SELECT ra.*,
- u.id as user_id,
- u.username,
- u.phone,
- u.real_name,
- u.password,
- u.role,
- zr.name as city_name,
- d.name as district_name
- FROM regional_agents ra
- JOIN users u ON ra.user_id = u.id
- JOIN china_regions d ON d.code = u.district_id
- JOIN china_regions zr ON ra.region_id = zr.code
- WHERE u.phone = ?
- AND ra.status = "active"`,
- [phone]
- );
-
- if (agents.length === 0) {
- return res.status(401).json({success: false, message: '手机号或密码错误,或账户未激活'});
- }
-
- const agent = agents[0];
-
- // 验证密码
- const isPasswordValid = await bcrypt.compare(password, agent.password);
- if (!isPasswordValid) {
- return res.status(401).json({success: false, message: '手机号或密码错误,或账户未激活'});
- }
-
- // 生成JWT token
- const jwt = require('jsonwebtoken');
- const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
- const token = jwt.sign(
- {
- userId: agent.user_id,
- username: agent.username || agent.phone,
- role: agent.role || 'agent',
- agentId: agent.id
- },
- JWT_SECRET,
- {expiresIn: '24h'}
- );
-
- delete agent.password; // 不返回密码
-
- res.json({
- success: true,
- data: {
- ...agent,
- token
- },
- message: '登录成功'
- });
- } catch (error) {
- console.error('代理登录失败:', error);
- res.status(500).json({success: false, message: '登录失败'});
- }
-});
-
-// 获取代理的商户列表(包含所有商户,标注早期商户状态)
-router.get('/merchants/:agent_id',auth, async (req, res) => {
- try {
- const {agent_id} = req.params;
- const {page = 1, limit = 10} = req.query;
- const offset = (page - 1) * limit;
-
- // 首先获取代理的注册时间
- const [agentInfo] = await getDB().execute(
- `SELECT ra.created_at as agent_created_at, ra.region_id, ra.user_id
- FROM regional_agents ra
- WHERE ra.id = ?`,
- [parseInt(agent_id)]
- );
- const regionId = agentInfo[0].region_id;
-
- const userId = agentInfo[0].user_id;
-
- if (!agentInfo || agentInfo.length === 0) {
- return res.status(404).json({success: false, message: '代理不存在'});
- }
-
- const agentCreatedAt = agentInfo[0].agent_created_at;
-
- // 获取商户列表(包含所有商户,包括agent_merchants表中的和符合条件的早期商户)
- const [merchants] = await getDB().execute(
- `SELECT u.id,
- u.username,
- u.phone,
- u.real_name,
- u.created_at,
- u.audit_status,
- IFNULL(u.created_at, '未关联') as joined_at,
- CASE
- WHEN u.created_at < ? AND u.district_id = ? THEN 1
- ELSE 0
- END as is_early_merchant,
- CASE
- WHEN u.created_at < ? AND u.district_id = ? THEN '早期商户(不记录佣金)'
- ELSE '正常商户'
- END as merchant_status,
- (SELECT COUNT(*)
- FROM matching_orders
- WHERE initiator_id = u.id
- AND status = 'completed') as completed_matches
-
- FROM users u
- WHERE (u.inviter = ? OR (u.created_at < ? AND u.district_id = ? AND u.role = 'user'))
- ORDER BY u.created_at DESC
- LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}`,
- [agentCreatedAt, parseInt(regionId), agentCreatedAt, parseInt(regionId), parseInt(userId), agentCreatedAt, parseInt(regionId)]
- );
-
- // 获取总数(包括代理关联的商户和符合条件的早期商户)
- const [countResult] = await getDB().execute(
- `SELECT COUNT(*) as total
- FROM users u
- WHERE (u.inviter = ? OR (u.created_at < ? AND u.district_id = ?))`,
- [parseInt(userId), agentCreatedAt, parseInt(regionId)]
- );
-
- // 获取早期商户统计(从user表获取所有符合条件的早期商户)
- // 早期商户的判断条件:1.早期商户注册时间比代理要早。2.代理商代理的区县与商户的区县一致
- const [earlyMerchantStats] = await getDB().execute(
- `SELECT COUNT(*) as early_merchant_count
- FROM users u
- WHERE u.created_at < ?
- AND u.district_id = ?
- AND u.role = 'user'`,
- [agentCreatedAt, parseInt(regionId)]
- );
-
- // 获取正常商户统计(包括代理关联的商户,排除符合条件的早期商户)
- const [normalMerchantStats] = await getDB().execute(
- `SELECT COUNT(*) as normal_merchant_count
- FROM users u
- WHERE (u.inviter = ? AND (u.created_at >= ? OR u.district_id != ?))`,
- [parseInt(userId), agentCreatedAt, parseInt(regionId)]
- );
-
- res.json({
- success: true,
- data: {
- merchants,
- total: parseInt(countResult[0].total),
- page: parseInt(page),
- limit: parseInt(limit),
- stats: {
- total_merchants: parseInt(countResult[0].total),
- early_merchants: parseInt(earlyMerchantStats[0].early_merchant_count),
- normal_merchants: parseInt(normalMerchantStats[0].normal_merchant_count)
- }
- }
- });
- } catch (error) {
- console.error('获取商户列表失败:', error);
- res.status(500).json({success: false, message: '获取商户列表失败'});
- }
-});
-
-// 获取代理的佣金记录
-router.get('/commissions/:agent_id',auth, async (req, res) => {
- try {
- const {agent_id} = req.params;
- const {id} = req.user
- const {page = 1, limit = 10} = req.query;
- const offset = (page - 1) * limit;
-
- // 获取佣金记录
- const [commissions] = await getDB().execute(
- `SELECT acr.*, u.username, u.real_name
- FROM transfers acr
- JOIN users u ON acr.from_user_id = u.id
- WHERE acr.to_user_id = ${parseInt(id)} AND source_type='agent'
- ORDER BY acr.created_at DESC
- LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}`
- );
-
- // 获取总数和总佣金
- const [summary] = await getDB().execute(
- `SELECT COUNT(*) as total_records,
- COALESCE(SUM(amount), 0) as total_commission
- FROM transfers
- WHERE source_type = 'agent' AND to_user_id=${id}`
- );
-
- // 由于agent_commission_records表没有status字段,设置默认值
- summary[0].paid_commission = summary[0].total_commission;
- summary[0].pending_commission = 0;
-
- res.json({
- success: true,
- data: {
- commissions,
- summary: summary[0],
- page: parseInt(page),
- limit: parseInt(limit)
- }
- });
- } catch (error) {
- console.error('获取佣金记录失败:', error);
- res.status(500).json({success: false, message: '获取佣金记录失败'});
- }
-});
-
-// 获取代理统计信息
-router.get('/stats/:agent_id', auth,async (req, res) => {
- try {
- const {agent_id} = req.params;
-
- // 获取统计数据
- const [stats] = await getDB().execute(
- `SELECT (SELECT COUNT(*) FROM agent_merchants WHERE agent_id = ${parseInt(agent_id)}) as total_merchants,
- (SELECT COUNT(*)
- FROM agent_merchants am
- JOIN users u ON am.merchant_id = u.id
- WHERE am.agent_id = ${parseInt(agent_id)}
- AND u.audit_status = 'approved') as approved_merchants,
- (SELECT COALESCE(SUM(commission_amount), 0)
- FROM agent_commission_records
- WHERE agent_id = ${parseInt(agent_id)}) as total_commission,
- (SELECT COALESCE(SUM(commission_amount), 0)
- FROM agent_commission_records
- WHERE agent_id = ${parseInt(agent_id)}) as paid_commission,
- (SELECT COUNT(*)
- FROM registration_codes rc
- JOIN regional_agents ra ON rc.agent_id = ra.user_id
- WHERE ra.id = ${parseInt(agent_id)}
- AND rc.is_used = 1) as used_codes,
- (SELECT COUNT(*)
- FROM registration_codes rc
- JOIN regional_agents ra ON rc.agent_id = ra.user_id
- WHERE ra.id = ${parseInt(agent_id)}
- AND rc.is_used = 0
- AND rc.expires_at > NOW()) as active_codes`
- );
-
- res.json({success: true, data: stats[0]});
- } catch (error) {
- console.error('获取统计信息失败:', error);
- res.status(500).json({success: false, message: '获取统计信息失败'});
- }
-});
-
-// 获取代理列表
-router.get('/list', auth,async (req, res) => {
- try {
- const {page = 1, limit = 10, status, region_id} = req.query;
- const offset = (page - 1) * limit;
-
- let whereClause = '1=1';
- let params = [];
-
- if (status) {
- whereClause += ' AND ra.status = ?';
- params.push(status);
- }
-
- if (region_id) {
- whereClause += ' AND ra.region_id = ?';
- params.push(region_id);
- }
-
- // 获取代理列表
- const [agents] = await getDB().execute(
- `SELECT ra.*,
- u.username,
- u.phone,
- u.real_name,
- u.created_at as user_created_at,
- zr.city_name,
- zr.district_name,
- zr.region_code
- FROM regional_agents ra
- JOIN users u ON ra.user_id = u.id
- JOIN zhejiang_regions zr ON ra.region_id = zr.id
- WHERE ${whereClause}
- ORDER BY ra.created_at DESC
- LIMIT ${limit} OFFSET ${offset}`
- );
-
- // 获取总数
- const [countResult] = await getDB().execute(
- `SELECT COUNT(*) as total
- FROM regional_agents ra
- JOIN users u ON ra.user_id = u.id
- JOIN zhejiang_regions zr ON ra.region_id = zr.id
- WHERE ${whereClause}`
- );
-
- const total = countResult[0].total;
- const totalPages = Math.ceil(total / limit);
-
- res.json({
- success: true,
- data: {
- agents,
- pagination: {
- page: parseInt(page),
- limit: parseInt(limit),
- total,
- totalPages
- }
- }
- });
- } catch (error) {
- console.error('获取代理列表失败:', error);
- res.status(500).json({success: false, message: '获取代理列表失败'});
- }
-});
-
-/**
- * 获取代理佣金趋势数据
- * @route GET /agents/commission-trend/:agent_id
- * @param {string} agent_id - 代理ID
- * @param {string} period - 时间周期 (7d, 30d, 3m)
- * @returns {Object} 佣金趋势数据
- */
-router.get('/commission-trend/:agent_id',auth, async (req, res) => {
- try {
- console.log(req.params, 'req.params')
- const {agent_id} = req.params;
- let [agentUserInfo] = await getDB().execute(`
- SELECT u.*
- FROM regional_agents as rg
- LEFT JOIN users u ON rg.user_id = u.id
- WHERE rg.id = ${agent_id}
- `)
- let userId = agentUserInfo[0].id
- console.log(userId, 'userId')
- const {period = '7d'} = req.query;
- let days;
- switch (period) {
- case '7d':
- days = 7;
- break;
- case '30d':
- days = 30;
- break;
- case '3m':
- days = 90;
- break;
- default:
- days = 7;
- }
-
- console.log(period, 'period')
-
- const [trendData] = await getDB().execute(`
- SELECT DATE(created_at) as date,
- CAST(COALESCE(SUM(amount), 0) AS DECIMAL(10, 2)) as amount
- FROM transfers
- WHERE to_user_id = ?
- AND created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
- AND source_type = 'user_to_agent'
- GROUP BY DATE(created_at)
- ORDER BY date ASC
- `, [userId, parseInt(days)]);
-
- // 填充缺失的日期(佣金为0)
- const result = [];
-
- for (let i = days - 1; i >= 0; i--) {
- const date = dayjs().subtract(i, 'day');
- const dateStr = date.format('YYYY-MM-DD');
-
- const existingData = trendData.find(item => {
- const itemDateStr = dayjs(item.date).format('YYYY-MM-DD');
- return itemDateStr === dateStr;
- });
-
- result.push({
- date: date.format('MM-DD'),
- amount: existingData ? parseFloat(existingData.amount) : 0
- });
- }
-
- res.json({
- success: true,
- data: result
- });
-
- } catch (error) {
- console.log(error);
-
- res.status(500).json({
- success: false,
- message: '获取趋势数据失败'
- });
- }
-});
-
-/**
- * 获取代理商户状态分布数据
- * @route GET /agents/merchant-status/:agent_id
- * @param {string} agent_id - 代理ID
- * @returns {Object} 商户状态分布数据
- */
-router.get('/merchant-status/:agent_id',auth, async (req, res) => {
- try {
- const {agent_id} = req.params;
- let [agent] = await getDB().execute(`
- SELECT u.*
- FROM users as u
- LEFT JOIN regional_agents as rg ON rg.user_id = u.id
- WHERE rg.id = ${agent_id}
- `)
- let userId = agent[0].id
- // 获取商户状态分布
- const [statusData] = await getDB().execute(
- `SELECT CASE
- WHEN am.audit_status = 'approved' THEN '已审核'
- WHEN am.audit_status = 'pending' THEN '待审核'
- WHEN am.audit_status = 'rejected' THEN '已拒绝'
- ELSE '未知状态'
- END as status,
- COUNT(*) as count
- FROM users am
- WHERE am.inviter = ?
- GROUP BY am.audit_status
- ORDER BY count DESC`,
- [parseInt(userId)]
- );
-
- res.json({
- success: true,
- data: statusData
- });
- } catch (error) {
- console.error('获取商户状态分布数据失败:', error);
- res.status(500).json({success: false, message: '获取商户状态分布数据失败'});
- }
-});
-
-/**
- * 获取代理详细统计数据(包含更多维度)
- * @route GET /agents/detailed-stats/:agent_id
- * @param {string} agent_id - 代理ID
- * @returns {Object} 详细统计数据
- */
-router.get('/detailed-stats/:agent_id',auth, async (req, res) => {
- try {
- const {agent_id} = req.params;
- let [agentUserInfo] = await getDB().execute(`
- SELECT u.*
- FROM users as u
- LEFT JOIN regional_agents as ag ON ag.user_id = u.id
- WHERE ag.id = ${agent_id}
- `)
- let agUserInfo = agentUserInfo[0];
- // 获取基础统计数据
- const [basicStats] = await getDB().execute(
- `SELECT (SELECT COUNT(*) FROM users WHERE inviter = ?) as total_merchants,
- (SELECT COUNT(*)
- FROM users am
- WHERE am.inviter = ?
- AND am.audit_status = 'approved') as approved_merchants,
- (SELECT COUNT(*)
- FROM users am
- WHERE am.inviter = ?
- AND am.audit_status = 'pending') as pending_merchants,
- (SELECT COUNT(*)
- FROM users am
- WHERE am.inviter = ?
- AND am.audit_status = 'rejected') as rejected_merchants,
- (SELECT COALESCE(SUM(amount), 0)
- FROM transfers
- WHERE transfer_type = 'user_to_agent'
- AND to_user_id = ?) as total_commission
- `,
- [parseInt(agUserInfo.id), parseInt(agUserInfo.id), parseInt(agUserInfo.id), parseInt(agUserInfo.id), parseInt(agUserInfo.id)]
- );
-
- // 获取本月营收
- const [monthlyStats] = await getDB().execute(
- `SELECT COALESCE(SUM(amount), 0) as monthly_commission,
- COUNT(*) as monthly_commission_records
- FROM transfers
- WHERE to_user_id = ?
- AND transfer_type = 'user_to_agent'
- AND YEAR(created_at) = YEAR(CURDATE())
- AND MONTH(created_at) = MONTH(CURDATE())`,
- [parseInt(agUserInfo.id)]
- );
-
- // 获取今日佣金
- const [dailyStats] = await getDB().execute(
- `SELECT COALESCE(SUM(amount), 0) as daily_commission,
- COUNT(*) as daily_commission_records
- FROM transfers
- WHERE to_user_id = ?
- AND transfer_type = 'user_to_agent'
- AND DATE(created_at) = CURDATE()`,
- [parseInt(agUserInfo.id)]
- );
-
- // 获取最近7天新增商户数
- const [weeklyMerchants] = await getDB().execute(
- `SELECT COUNT(*) as weekly_new_merchants
- FROM users am
- WHERE am.inviter = ?
- AND am.created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)`,
- [parseInt(agUserInfo.id)]
- );
-
-
- // 合并所有统计数据
- const stats = {
- ...basicStats[0],
- ...monthlyStats[0],
- ...dailyStats[0],
- ...weeklyMerchants[0]
- };
-
- res.json({
- success: true,
- data: stats
- });
- } catch (error) {
- console.error('获取详细统计数据失败:', error);
- res.status(500).json({success: false, message: '获取详细统计数据失败'});
- }
-});
-
-/**
- * 获取代理商户的转账记录
- * @route GET /agents/merchants/:agent_id/transfers
- * @param {string} agent_id - 代理ID
- * @param {string} page - 页码
- * @param {string} limit - 每页数量
- * @returns {Object} 转账记录列表
- */
-router.get('/merchants/:agent_id/transfers', auth,async (req, res) => {
- try {
- const {agent_id} = req.params;
- const {page = 1, limit = 10} = req.query;
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(limit) || 10;
- const offset = (pageNum - 1) * limitNum;
-
- // 检查代理是否存在
- const [agentResult] = await getDB().execute(
- 'SELECT * FROM regional_agents WHERE id = ?',
- [parseInt(agent_id)]
- );
-
- if (agentResult.length === 0) {
- return res.status(404).json({success: false, message: '代理不存在'});
- }
-
- // 查询商户转账记录
- const transferQuery = `
- SELECT t.id,
- t.from_user_id,
- t.to_user_id,
- t.amount,
- t.status,
- t.transfer_type,
- t.description,
- t.created_at,
- t.confirmed_at,
- from_user.real_name as from_real_name,
- from_user.phone as from_phone,
- to_user.real_name as to_real_name,
- to_user.phone as to_phone
- FROM agent_merchants am
- JOIN transfers t ON am.merchant_id = t.from_user_id
- LEFT JOIN users from_user ON t.from_user_id = from_user.id
- LEFT JOIN users to_user ON t.to_user_id = to_user.id
- WHERE am.agent_id = ?
- ORDER BY t.created_at DESC
- LIMIT ${limitNum} OFFSET ${offset}
- `;
- const [transfers] = await getDB().execute(transferQuery, [parseInt(agent_id)]);
-
- // 查询总数
- const [totalResult] = await getDB().execute(
- `SELECT COUNT(*) as total
- FROM agent_merchants am
- JOIN transfers t ON am.merchant_id = t.from_user_id
- WHERE am.agent_id = ?`,
- [parseInt(agent_id)]
- );
-
- const total = totalResult[0].total;
-
- res.json({
- success: true,
- data: {
- transfers,
- pagination: {
- page: pageNum,
- limit: limitNum,
- total,
- pages: Math.ceil(total / limitNum)
- }
- }
- });
- } catch (error) {
- console.error('获取代理商户转账记录失败:', error);
- res.status(500).json({success: false, message: '获取代理商户转账记录失败,请稍后再试'});
- }
-});
-/**
- * 获取分销列表
- * @route GET /agents/distribution
- * @returns {Object} 分销列表
- */
-router.get('/distribution', auth, async (req, res) => {
- try {
-
- const {page = 1, size = 10, user_id} = req.query;
- const {id} = user_id || req.user;
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(size) || 10;
- const offset = (page - 1) * size;
- const [result] = await getDB().execute(
- `SELECT real_name, phone, username, avatar, created_at, id as user_id
- FROM users
- WHERE inviter = ?
- ORDER BY created_at DESC
- LIMIT ${size} OFFSET ${offset}`,
- [parseInt(id)]
- );
- const [totalResult] = await getDB().execute(
- `SELECT COUNT(*) as total
- FROM users
- WHERE inviter = ? `,
- [parseInt(id)]
- );
- result.forEach(item => {
- item.created_at = dayjs(item.created_at).format('YYYY-MM-DD HH:mm:ss');
- })
-
- const total = totalResult[0].total;
- res.json({
- success: true, data: result, pagination: {
- page: pageNum,
- limit: limitNum,
- total,
- pages: Math.ceil(total / limitNum)
- }
- });
- } catch (error) {
- console.error('获取分销列表失败:', error);
- res.status(500).json({success: false, message: '获取分销列表失败'});
- }
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/agents/agents.js b/routes/agents/agents.js
deleted file mode 100644
index 921e094..0000000
--- a/routes/agents/agents.js
+++ /dev/null
@@ -1,722 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const { getDB } = require('../../database');
-const bcrypt = require('bcryptjs');
-const { auth, adminAuth } = require('../../middleware/auth');
-const dayjs = require('dayjs');
-
-// 创建管理员认证中间件组合
-const authenticateAdmin = [auth, adminAuth];
-
-// 获取数据库连接
-const db = {
- query: async (sql, params = []) => {
- const connection = getDB();
- const [rows] = await connection.execute(sql, params);
- return rows;
- }
-};
-
-// 获取代理列表和统计信息
-router.get('/', authenticateAdmin, async (req, res) => {
- try {
- const { page = 1, limit = 20, status, city, search,district } = req.query;
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(limit) || 20;
- const offset = (pageNum - 1) * limitNum;
-
- // 构建查询条件
- let whereConditions = [];
- let queryParams = [];
-
- if (status) {
- whereConditions.push('ra.status = ?');
- queryParams.push(status);
- }
-
- if (district) {
- whereConditions.push('zr.name = ?');
- queryParams.push(district);
- }
-
- if (search) {
- whereConditions.push('(u.real_name LIKE ? OR u.phone LIKE ?)');
- queryParams.push(`%${search}%`, `%${search}%`);
- }
-
- const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
-
- // 查询代理列表
- const agentsQuery = `
- SELECT ra.*,
- u.real_name,
- u.phone,
- u.id_card,
- province.name AS province_name,
- city.name AS city_name,
- zr.name AS district_name,
- (SELECT COUNT(DISTINCT merchant_id)
- FROM agent_merchants
- WHERE agent_id = ra.id) as merchant_count,
- (SELECT CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10, 2))
- FROM agent_commission_records
- WHERE agent_id = ra.id) as total_commission,
- 0 as paid_commission,
- (SELECT CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10, 2))
- FROM agent_commission_records
- WHERE agent_id = ra.id) as pending_commission
- FROM regional_agents ra
- LEFT JOIN users u ON ra.user_id = u.id
- LEFT JOIN china_regions zr ON ra.region_id = zr.code -- 区
- LEFT JOIN china_regions city ON zr.parent_code = city.code -- 市
- LEFT JOIN china_regions province ON city.parent_code = province.code -- 省
- ${whereClause}
- ORDER BY ra.created_at
- DESC
- LIMIT ${limitNum} OFFSET ${offset};`;
- console.log(agentsQuery,queryParams)
- const agents = await db.query(agentsQuery, queryParams);
-
- // 查询总数
- const countQuery = `
- SELECT COUNT(DISTINCT ra.id) as total
- FROM regional_agents ra
- LEFT JOIN users u ON ra.user_id = u.id
- LEFT JOIN china_regions zr ON ra.region_id = zr.code
- ${whereClause}
- `;
-
- const totalResult = await db.query(countQuery, queryParams);
- const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0;
-
- // 查询统计信息
- const statsQuery = `
- SELECT
- COUNT(*) as total_agents,
- COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending_agents,
- COUNT(CASE WHEN status = 'active' THEN 1 END) as active_agents,
- CAST(COALESCE(SUM(commission_stats.total_commission), 0) AS DECIMAL(10,2)) as total_commission
- FROM regional_agents ra
- LEFT JOIN (
- SELECT
- agent_id,
- SUM(commission_amount) as total_commission
- FROM agent_commission_records
- GROUP BY agent_id
- ) commission_stats ON ra.id = commission_stats.agent_id
- `;
-
- const statsResult = await db.query(statsQuery);
- const stats = statsResult && statsResult.length > 0 ? statsResult[0] : {
- total_agents: 0,
- pending_agents: 0,
- active_agents: 0,
- total_commission: 0
- };
-
- res.json({
- success: true,
- data: {
- agents,
- total: parseInt(total),
- stats
- }
- });
- } catch (error) {
- console.error('获取代理列表失败:', error);
- res.status(500).json({ success: false, message: '获取代理列表失败' });
- }
-});
-
-// 获取代理详情
-router.get('/:id', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
-
- const agentQuery = `
- SELECT
- ra.*,
- u.real_name as name,
- u.phone,
- u.id_card,
- CONCAT(u.city, ' ', zr.district_name) as address,
- zr.city_name,
- zr.district_name,
- (
- SELECT COUNT(DISTINCT merchant_id)
- FROM agent_merchants
- WHERE agent_id = ra.id
- ) as merchant_count,
- (
- SELECT COALESCE(SUM(commission_amount), 0)
- FROM agent_commission_records
- WHERE agent_id = ra.id
- ) as total_commission,
- 0 as paid_commission,
- (
- SELECT COALESCE(SUM(commission_amount), 0)
- FROM agent_commission_records
- WHERE agent_id = ra.id
- ) as pending_commission
- FROM regional_agents ra
- LEFT JOIN users u ON ra.user_id = u.id
- LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id
- WHERE ra.id = ?
- `;
-
- const agentResult = await db.query(agentQuery, [id]);
-
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在' });
- }
-
- const agent = agentResult[0];
-
- res.json({
- success: true,
- data: agent
- });
- } catch (error) {
- console.error('获取代理详情失败:', error);
- res.status(500).json({ success: false, message: '获取代理详情失败' });
- }
-});
-
-// 审核通过代理申请
-router.put('/:id/approve', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
- const { password } = req.body;
-
- if (!password || password.length < 6) {
- return res.status(400).json({ success: false, message: '密码长度不能少于6位' });
- }
-
- // 检查代理是否存在且状态为待审核
- const agents = await db.query(
- 'SELECT * FROM regional_agents WHERE id = ? AND status = "pending"',
- [id]
- );
-
- if (!agents || agents.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在或状态不正确' });
- }
-
- const agent = agents[0];
-
- // 检查该区域是否已有其他激活的代理
- const existingActiveAgents = await db.query(
- 'SELECT id FROM regional_agents WHERE region_id = ? AND status = "active" AND id != ?',
- [agent.region_id, id]
- );
-
- if (existingActiveAgents && existingActiveAgents.length > 0) {
- return res.status(400).json({ success: false, message: '该区域已有激活的代理,每个区域只能有一个代理账号' });
- }
-
- // 加密密码并更新用户表
- const hashedPassword = await bcrypt.hash(password, 10);
-
- // 更新用户密码
- await db.query(
- `UPDATE users SET password = ? WHERE id = (
- SELECT user_id FROM regional_agents WHERE id = ?
- )`,
- [hashedPassword, id]
- );
-
- // 更新代理状态
- await db.query(
- `UPDATE regional_agents
- SET status = 'active', approved_at = NOW(), approved_by_admin_id = ?
- WHERE id = ?`,
- [req.user.id, id]
- );
-
- res.json({
- success: true,
- message: '代理申请已通过'
- });
- } catch (error) {
- console.error('审核代理申请失败:', error);
- res.status(500).json({ success: false, message: '审核代理申请失败' });
- }
-});
-
-// 拒绝代理申请
-router.put('/:id/reject', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
- const { reason } = req.body;
-
- if (!reason || reason.trim() === '') {
- return res.status(400).json({ success: false, message: '请输入拒绝原因' });
- }
-
- // 检查代理是否存在且状态为待审核
- const agentResult = await db.query(
- 'SELECT * FROM regional_agents WHERE id = ? AND status = "pending"',
- [id]
- );
-
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在或状态不正确' });
- }
-
- const agent = agentResult[0];
-
- // 更新代理状态
- await db.query(
- `UPDATE regional_agents
- SET status = 'rejected', reject_reason = ?, rejected_at = NOW(), rejected_by_admin_id = ?
- WHERE id = ?`,
- [reason.trim(), req.user.id, id]
- );
-
- res.json({
- success: true,
- message: '代理申请已拒绝'
- });
- } catch (error) {
- console.error('拒绝代理申请失败:', error);
- res.status(500).json({ success: false, message: '拒绝代理申请失败' });
- }
-});
-
-// 禁用代理
-router.put('/:id/disable', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
-
- // 检查代理是否存在且状态为激活
- const agentResult = await db.query(
- 'SELECT * FROM regional_agents WHERE id = ? AND status = "active"',
- [id]
- );
-
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在或状态不正确' });
- }
-
- const agent = agentResult[0];
-
- // 更新代理状态
- await db.query(
- 'UPDATE regional_agents SET status = "disabled", disabled_at = NOW() WHERE id = ?',
- [id]
- );
-
- res.json({
- success: true,
- message: '代理已禁用'
- });
- } catch (error) {
- console.error('禁用代理失败:', error);
- res.status(500).json({ success: false, message: '禁用代理失败' });
- }
-});
-
-// 启用代理
-router.put('/:id/enable', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
-
- // 检查代理是否存在且状态为禁用
- const agentResult = await db.query(
- 'SELECT * FROM regional_agents WHERE id = ? AND status = "disabled"',
- [id]
- );
-
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在或状态不正确' });
- }
-
- const agent = agentResult[0];
-
- // 检查该区域是否已有其他激活的代理
- const existingActiveAgentResult = await db.query(
- 'SELECT id FROM regional_agents WHERE region_id = ? AND status = "active" AND id != ?',
- [agent.region_id, id]
- );
-
- if (existingActiveAgentResult && existingActiveAgentResult.length > 0) {
- return res.status(400).json({ success: false, message: '该区域已有激活的代理,每个区域只能有一个代理账号' });
- }
-
- // 更新代理状态
- await db.query(
- 'UPDATE regional_agents SET status = "active", disabled_at = NULL WHERE id = ?',
- [id]
- );
-
- res.json({
- success: true,
- message: '代理已启用'
- });
- } catch (error) {
- console.error('启用代理失败:', error);
- res.status(500).json({ success: false, message: '启用代理失败' });
- }
-});
-
-// 获取代理商户列表
-router.get('/:id/merchants', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
- const { page = 1, limit = 20 } = req.query;
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(limit) || 20;
- const offset = (pageNum - 1) * limitNum;
-
- // 检查代理是否存在
- const agentResult = await db.query(`SELECT * FROM users WHERE id = ? AND (user_type='agent' OR user_type='agent_directly')`, [id]);
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在' });
- }
-
- // 查询代理的商户列表
- const merchantsQuery = `
- SELECT u.id,
- u.real_name,
- u.phone,
- u.created_at,
- u.created_at as joined_at
- FROM users u
- WHERE u.inviter = ${id}
- GROUP BY u.id, u.created_at
- ORDER BY u.created_at
- DESC
- LIMIT ${limitNum} OFFSET ${offset}
- `;
-
- const merchants = await db.query(merchantsQuery, [id]);
-
- // 查询总数
- const totalResult = await db.query(
- 'SELECT COUNT(*) as total FROM users WHERE inviter = ?',
- [id]
- );
- const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0;
-
- res.json({
- success: true,
- data: {
- merchants,
- total: parseInt(total)
- }
- });
- } catch (error) {
- console.error('获取代理商户列表失败:', error);
- res.status(500).json({ success: false, message: '获取代理商户列表失败' });
- }
-});
-
-// 获取代理佣金记录
-router.get('/:id/commissions', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
- const { page = 1, limit = 20 } = req.query;
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(limit) || 20;
- const offset = (pageNum - 1) * limitNum;
-
- // 检查代理是否存在
- const agentResult = await db.query('SELECT * FROM regional_agents WHERE id = ?', [id]);
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在' });
- }
- const agent = agentResult[0];
-
- // 查询佣金记录
- const commissionsQuery = `
- SELECT
- acr.*,
- u.phone as merchant_phone
- FROM agent_commission_records acr
- JOIN users u ON acr.merchant_id = u.id
- WHERE acr.agent_id = ?
- ORDER BY acr.created_at DESC
- LIMIT ${limitNum} OFFSET ${offset}
- `;
-
- const commissions = await db.query(commissionsQuery, [id]);
-
- // 查询总数
- const totalResult = await db.query(
- 'SELECT COUNT(*) as total FROM agent_commission_records WHERE agent_id = ?',
- [id]
- );
- const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0;
-
- res.json({
- success: true,
- data: {
- commissions,
- total: parseInt(total)
- }
- });
- } catch (error) {
- console.error('获取代理佣金记录失败:', error);
- res.status(500).json({ success: false, message: '获取代理佣金记录失败' });
- }
-});
-
-// 获取代理商户的转账记录
-router.get('/:id/merchant-transfers', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
- const { page = 1, limit = 20, merchant_id } = req.query;
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(limit) || 20;
- const offset = (pageNum - 1) * limitNum;
-
- // 检查代理是否存在
- const agentResult = await db.query(`SELECT * FROM users WHERE id = ? AND (user_type='agent' OR user_type='agent_directly')`, [id]);
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在' });
- }
-
- // 构建查询条件
- let whereConditions = ['am.inviter = ?'];
- let queryParams = [id];
-
- if (merchant_id) {
- whereConditions.push('t.from_user_id = ?');
- queryParams.push(merchant_id);
- }
-
- const whereClause = whereConditions.join(' AND ');
-
- // 查询商户转账记录
- const transfersQuery = `
- SELECT t.id,
- t.from_user_id,
- t.to_user_id,
- t.amount,
- t.status,
- t.transfer_type,
- t.description,
- t.created_at,
- t.confirmed_at,
- from_user.real_name as from_real_name,
- CONCAT(SUBSTRING(from_user.phone, 1, 3), '****', SUBSTRING(from_user.phone, -4)) as from_phone_masked,
- to_user.real_name as to_real_name,
- CONCAT(SUBSTRING(to_user.phone, 1, 3), '****', SUBSTRING(to_user.phone, -4)) as to_phone_masked
- FROM users as am
- JOIN transfers t ON am.id = t.from_user_id
- LEFT JOIN users from_user ON t.from_user_id = from_user.id
- LEFT JOIN users to_user ON t.to_user_id = to_user.id
- WHERE ${whereClause}
- ORDER BY t.created_at DESC
- LIMIT ${limitNum} OFFSET ${offset}
- `;
- console.log(transfersQuery,queryParams);
- const transfers = await db.query(transfersQuery, queryParams);
-
- // 查询总数
- const totalQuery = `
- SELECT COUNT(*) as total
- FROM users am
- JOIN transfers t ON am.id = t.from_user_id
- WHERE ${whereClause}
- `;
- const totalResult = await db.query(totalQuery, queryParams);
- const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0;
-
- res.json({
- success: true,
- data: {
- transfers,
- total: parseInt(total)
- }
- });
- } catch (error) {
- console.error('1:', error);
- res.status(500).json({ success: false, message: '1获取代理商户转账记录失败' });
- }
-});
-
-/**
- * 修改代理密码
- */
-router.put('/:id/password', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
- const { password } = req.body;
-
- if (!password || password.length < 6) {
- return res.status(400).json({ success: false, message: '密码长度不能少于6位' });
- }
-
- // 检查代理是否存在且状态为激活
- const agentResult = await db.query(
- 'SELECT * FROM regional_agents WHERE id = ? AND status = "active"',
- [id]
- );
-
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在或状态不正确' });
- }
-
- // 加密新密码
- const hashedPassword = await bcrypt.hash(password, 10);
-
- // 更新用户表中的密码(与审核通过时的逻辑一致)
- await db.query(
- `UPDATE users SET password = ? WHERE id = (
- SELECT user_id FROM regional_agents WHERE id = ?
- )`,
- [hashedPassword, id]
- );
-
- res.json({
- success: true,
- message: '代理密码修改成功'
- });
- } catch (error) {
- console.error('修改代理密码失败:', error);
- res.status(500).json({ success: false, message: '修改代理密码失败' });
- }
-});
-
-/**
- * 删除代理
- */
-router.delete('/:id', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
- const { force = 'false' } = req.query; // 是否强制删除
- const forceDelete = force === 'true'
-
- // 检查代理是否存在
- const agentResult = await db.query(
- 'SELECT * FROM regional_agents WHERE id = ?',
- [id]
- );
-
- if (!agentResult || agentResult.length === 0) {
- return res.status(404).json({ success: false, message: '代理不存在' });
- }
-
- const agent = agentResult[0];
-
- // 检查代理是否有关联的商户
- const merchantCount = await db.query(
- 'SELECT COUNT(*) as count FROM agent_merchants WHERE agent_id = ?',
- [id]
- );
-
- const hasMerchants = merchantCount && merchantCount.length > 0 && merchantCount[0].count > 0;
-
- // 检查代理是否有佣金记录
- const commissionCount = await db.query(
- 'SELECT COUNT(*) as count FROM agent_commission_records WHERE agent_id = ?',
- [id]
- );
-
- const hasCommissions = commissionCount && commissionCount.length > 0 && commissionCount[0].count > 0;
-
- // 如果有关联数据且不是强制删除,则提示用户
- // if ((hasMerchants || hasCommissions) && !forceDelete) {
- // return res.status(400).json({
- // success: false,
- // message: '该代理存在关联数据(商户或佣金记录),请确认是否强制删除',
- // data: {
- // has_merchants: hasMerchants,
- // has_commissions: hasCommissions,
- // merchant_count: hasMerchants ? merchantCount[0].count : 0,
- // commission_count: hasCommissions ? commissionCount[0].count : 0
- // },
- // require_force: true
- // });
- // }
-
- // 开始事务删除
- const pool = getDB();
- const connection = await pool.getConnection();
- await connection.beginTransaction();
-
- try {
- // 删除代理商户关系
- if (hasMerchants) {
- await connection.execute('DELETE FROM agent_merchants WHERE agent_id = ?', [id]);
- }
-
- // 删除佣金记录(根据业务需求,可能需要保留历史记录)
- if (hasCommissions && forceDelete) {
- await connection.execute('DELETE FROM agent_commission_records WHERE agent_id = ?', [id]);
- }
-
- // 删除代理记录
- await connection.execute('DELETE FROM regional_agents WHERE id = ?', [id]);
-
- // 获取区域信息用于日志
- const [regionResult] = await connection.execute(
- 'SELECT * FROM zhejiang_regions WHERE id = ?',
- [agent.region_id]
- );
- const region = regionResult && regionResult.length > 0 ? regionResult[0] : null;
-
- await connection.commit();
-
- // 记录操作日志
- const logMessage = `删除代理: ${agent.user_id} (区域: ${region ? `${region.city_name} ${region.district_name}` : '未知区域'})`;
- console.log(`管理员 ${req.user.id} 执行操作: ${logMessage}`);
-
- res.json({
- success: true,
- message: '代理删除成功',
- data: {
- deleted_agent_id: id,
- deleted_merchants: hasMerchants ? merchantCount[0].count : 0,
- deleted_commissions: (hasCommissions && forceDelete) ? commissionCount[0].count : 0
- }
- });
- } catch (error) {
- await connection.rollback();
- throw error;
- } finally {
- connection.release();
- }
- } catch (error) {
- console.error('删除代理失败:', error);
- res.status(500).json({ success: false, message: '删除代理失败' });
- }
-});
-
-/**
- * 获取可用的城市区域列表(用于代理城市更换)
- */
-router.get('/available-regions', authenticateAdmin, async (req, res) => {
- try {
- // 查询所有区域,并标记是否已有激活代理
- const regionsQuery = `
- SELECT
- zr.id,
- zr.city_name,
- zr.district_name,
- CASE
- WHEN ra.id IS NOT NULL THEN 1
- ELSE 0
- END as has_active_agent,
- ra.id as agent_id,
- u.real_name as agent_name
- FROM zhejiang_regions zr
- LEFT JOIN regional_agents ra ON zr.id = ra.region_id AND ra.status = 'active'
- LEFT JOIN users u ON ra.user_id = u.id
- ORDER BY zr.city_name, zr.district_name
- `;
-
- const regions = await db.query(regionsQuery);
-
- res.json({
- success: true,
- data: regions
- });
- } catch (error) {
- console.error('获取可用区域列表失败:', error);
- res.status(500).json({ success: false, message: '获取可用区域列表失败' });
- }
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/agents/withdrawals.js b/routes/agents/withdrawals.js
deleted file mode 100644
index e67a63f..0000000
--- a/routes/agents/withdrawals.js
+++ /dev/null
@@ -1,274 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const { getDB } = require('../../database');
-const { auth, adminAuth } = require('../../middleware/auth');
-
-// 创建管理员认证中间件组合
-const authenticateAdmin = [auth, adminAuth];
-
-// 获取数据库连接
-const db = {
- query: async (sql, params = []) => {
- const connection = getDB();
- const [rows] = await connection.execute(sql, params);
- return rows;
- }
-};
-
-/**
- * 获取提现申请列表
- */
-router.get('/', authenticateAdmin, async (req, res) => {
- try {
- const { page = 1, limit = 20, status, agent_id } = req.query;
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(limit) || 20;
- const offset = (pageNum - 1) * limitNum;
-
- // 构建查询条件
- let whereConditions = [];
- let queryParams = [];
-
- if (status) {
- whereConditions.push('aw.status = ?');
- queryParams.push(status);
- }
-
- if (agent_id) {
- whereConditions.push('aw.agent_id = ?');
- queryParams.push(agent_id);
- }
-
- const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : '';
-
- // 查询提现申请列表
- const withdrawalsQuery = `
- SELECT
- aw.*,
- ra.agent_code,
- u.real_name as agent_name,
- u.phone as agent_phone,
- zr.city_name,
- zr.district_name,
- admin.real_name as processed_by_name
- FROM agent_withdrawals aw
- JOIN regional_agents ra ON aw.agent_id = ra.id
- JOIN users u ON ra.user_id = u.id
- LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id
- LEFT JOIN users admin ON aw.processed_by = admin.id
- ${whereClause}
- ORDER BY aw.created_at DESC
- LIMIT ${limitNum} OFFSET ${offset}
- `;
-
- const withdrawals = await db.query(withdrawalsQuery, queryParams);
-
- // 查询总数
- const countQuery = `
- SELECT COUNT(*) as total
- FROM agent_withdrawals aw
- ${whereClause}
- `;
-
- const totalResult = await db.query(countQuery, queryParams);
- const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0;
-
- // 查询统计信息
- const statsQuery = `
- SELECT
- COUNT(*) as total_applications,
- COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending_count,
- COUNT(CASE WHEN status = 'approved' THEN 1 END) as approved_count,
- COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed_count,
- COUNT(CASE WHEN status = 'rejected' THEN 1 END) as rejected_count,
- CAST(COALESCE(SUM(CASE WHEN status = 'pending' THEN amount END), 0) AS DECIMAL(10,2)) as pending_amount,
- CAST(COALESCE(SUM(CASE WHEN status = 'completed' THEN amount END), 0) AS DECIMAL(10,2)) as completed_amount
- FROM agent_withdrawals
- `;
-
- const statsResult = await db.query(statsQuery);
- const stats = statsResult && statsResult.length > 0 ? statsResult[0] : {
- total_applications: 0,
- pending_count: 0,
- approved_count: 0,
- completed_count: 0,
- rejected_count: 0,
- pending_amount: 0,
- completed_amount: 0
- };
-
- res.json({
- success: true,
- data: {
- withdrawals,
- total: parseInt(total),
- stats
- }
- });
- } catch (error) {
- console.error('获取提现申请列表失败:', error);
- res.status(500).json({ success: false, message: '获取提现申请列表失败' });
- }
-});
-
-/**
- * 审核提现申请
- */
-router.put('/:id/review', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
- const { action, admin_note } = req.body;
- const adminId = req.user.id;
-
- if (!['approve', 'reject'].includes(action)) {
- return res.status(400).json({ success: false, message: '无效的审核操作' });
- }
-
- // 检查提现申请是否存在且状态为待审核
- const withdrawalResult = await db.query(
- 'SELECT * FROM agent_withdrawals WHERE id = ? AND status = "pending"',
- [id]
- );
-
- if (!withdrawalResult || withdrawalResult.length === 0) {
- return res.status(404).json({ success: false, message: '提现申请不存在或已处理' });
- }
-
- const withdrawal = withdrawalResult[0];
- const newStatus = action === 'approve' ? 'approved' : 'rejected';
-
- // 开始事务
- const pool = getDB();
- const connection = await pool.getConnection();
- await connection.beginTransaction();
-
- try {
- // 更新提现申请状态
- await connection.execute(
- 'UPDATE agent_withdrawals SET status = ?, admin_note = ?, processed_by = ?, processed_at = NOW() WHERE id = ?',
- [newStatus, admin_note || null, adminId, id]
- );
-
- // 如果是拒绝,需要恢复代理的待提现金额
- if (action === 'reject') {
- await connection.execute(
- 'UPDATE regional_agents SET pending_withdrawal = pending_withdrawal - ? WHERE id = ?',
- [withdrawal.amount, withdrawal.agent_id]
- );
- }
-
- await connection.commit();
- connection.release(); // 释放连接回连接池
-
- res.json({
- success: true,
- message: action === 'approve' ? '提现申请已通过审核' : '提现申请已拒绝'
- });
- } catch (error) {
- await connection.rollback();
- connection.release(); // 释放连接回连接池
- throw error;
- }
- } catch (error) {
- console.error('审核提现申请失败:', error);
- res.status(500).json({ success: false, message: '审核提现申请失败' });
- }
-});
-
-/**
- * 标记提现完成
- */
-router.put('/:id/complete', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
- const adminId = req.user.id;
-
- // 检查提现申请是否存在且状态为已审核
- const withdrawalResult = await db.query(
- 'SELECT * FROM agent_withdrawals WHERE id = ? AND status = "approved"',
- [id]
- );
-
- if (!withdrawalResult || withdrawalResult.length === 0) {
- return res.status(404).json({ success: false, message: '提现申请不存在或状态不正确' });
- }
-
- const withdrawal = withdrawalResult[0];
-
- // 开始事务
- const pool = getDB();
- const connection = await pool.getConnection();
- await connection.beginTransaction();
-
- try {
- // 更新提现申请状态为已完成
- await connection.execute(
- 'UPDATE agent_withdrawals SET status = "completed", processed_by = ?, processed_at = NOW() WHERE id = ?',
- [adminId, id]
- );
-
- // 更新代理的已提现金额和待提现金额
- await connection.execute(
- 'UPDATE regional_agents SET withdrawn_amount = withdrawn_amount + ?, pending_withdrawal = pending_withdrawal - ? WHERE id = ?',
- [withdrawal.amount, withdrawal.amount, withdrawal.agent_id]
- );
-
- await connection.commit();
- connection.release(); // 释放连接回连接池
-
- res.json({
- success: true,
- message: '提现已标记为完成'
- });
- } catch (error) {
- await connection.rollback();
- connection.release(); // 释放连接回连接池
- throw error;
- }
- } catch (error) {
- console.error('标记提现完成失败:', error);
- res.status(500).json({ success: false, message: '标记提现完成失败' });
- }
-});
-
-/**
- * 获取提现申请详情
- */
-router.get('/:id', authenticateAdmin, async (req, res) => {
- try {
- const { id } = req.params;
-
- const withdrawalQuery = `
- SELECT
- aw.*,
- ra.agent_code,
- u.real_name as agent_name,
- u.phone as agent_phone,
- zr.city_name,
- zr.district_name,
- admin.real_name as processed_by_name
- FROM agent_withdrawals aw
- JOIN regional_agents ra ON aw.agent_id = ra.id
- JOIN users u ON ra.user_id = u.id
- LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id
- LEFT JOIN users admin ON aw.processed_by = admin.id
- WHERE aw.id = ?
- `;
-
- const withdrawalResult = await db.query(withdrawalQuery, [id]);
-
- if (!withdrawalResult || withdrawalResult.length === 0) {
- return res.status(404).json({ success: false, message: '提现申请不存在' });
- }
-
- res.json({
- success: true,
- data: withdrawalResult[0]
- });
- } 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/announcements.js b/routes/announcements.js
deleted file mode 100644
index ecbbb74..0000000
--- a/routes/announcements.js
+++ /dev/null
@@ -1,397 +0,0 @@
-const express = require('express');
-const { getDB } = require('../database');
-const { auth, adminAuth } = require('../middleware/auth');
-const dayjs = require('dayjs');
-
-const router = express.Router();
-
-
-
-router.get('/', auth, async (req, res) => {
- try {
- const db = getDB();
- const { page = 1, limit = 10, status, type, keyword } = req.query;
- const offset = (page - 1) * limit;
-
- let whereClause = 'WHERE 1=1';
- const params = [];
-
- if (status) {
- whereClause += ' AND status = ?';
- params.push(status);
- }
-
- if (type) {
- whereClause += ' AND type = ?';
- params.push(type);
- }
-
- if (keyword) {
- whereClause += ' AND (title LIKE ? OR content LIKE ?)';
- params.push(`%${keyword}%`, `%${keyword}%`);
- }
-
- // 获取总数
- const countQuery = `SELECT COUNT(*) as total FROM announcements ${whereClause}`;
- const [countResult] = await db.execute(countQuery, params);
- const total = countResult[0].total;
-
- // 获取公告列表(包含用户阅读状态)
- const limitValue = Math.max(1, Math.min(100, parseInt(limit)));
- const offsetValue = Math.max(0, parseInt(offset));
-
- const query = `
- SELECT a.*, u.username as creator_name,
- uar.is_read,
- uar.read_at,
- CASE
- WHEN a.expire_time IS NOT NULL AND a.expire_time < NOW() THEN 1
- ELSE 0
- END as is_expired
- FROM announcements a
- LEFT JOIN users u ON a.created_by = u.id
- LEFT JOIN user_announcement_reads uar ON a.id = uar.announcement_id AND uar.user_id = ?
- ${whereClause}
- ORDER BY a.is_pinned DESC, a.created_at DESC
- LIMIT ${limitValue} OFFSET ${offsetValue}
- `;
-
- const [announcements] = await db.execute(query, [req.user.id, ...params]);
-
-
-
- const expiredUnreadAnnouncements = announcements.filter(a => a.is_expired && !a.is_read);
-
- if (expiredUnreadAnnouncements.length > 0) {
- const expiredIds = expiredUnreadAnnouncements.map(a => a.id);
- await db.execute(`
- INSERT INTO user_announcement_reads (user_id, announcement_id, is_read, read_at)
- VALUES ${expiredIds.map(() => '(?, ?, TRUE, NOW())').join(', ')}
- ON DUPLICATE KEY UPDATE is_read = TRUE, read_at = NOW()
- `, expiredIds.flatMap(id => [req.user.id, id]));
-
- // 更新返回数据中的阅读状态
- expiredUnreadAnnouncements.forEach(a => {
- a.is_read = true;
- a.read_at = new Date();
- });
- }
-
- res.json({
- success: true,
- data: {
- announcements,
- total,
- page: parseInt(page),
- limit: parseInt(limit),
- totalPages: Math.ceil(total / limit)
- }
- });
- } catch (error) {
- console.error('获取公告列表失败:', error);
- res.status(500).json({ success: false, message: '获取公告列表失败' });
- }
-});
-
-
-router.get('/:id', auth, async (req, res) => {
- try {
- const db = getDB();
- const { id } = req.params;
-
- const query = `
- SELECT a.*, u.username as creator_name,
- uar.is_read,
- uar.read_at,
- CASE
- WHEN a.expire_time IS NOT NULL AND a.expire_time < NOW() THEN 1
- ELSE 0
- END as is_expired
- FROM announcements a
- LEFT JOIN users u ON a.created_by = u.id
- LEFT JOIN user_announcement_reads uar ON a.id = uar.announcement_id AND uar.user_id = ?
- WHERE a.id = ?
- `;
-
- const [result] = await db.execute(query, [req.user.id, id]);
-
- if (result.length === 0) {
- return res.status(404).json({ success: false, message: '公告不存在' });
- }
-
- const announcement = result[0];
-
- // 如果公告未读或已过期但未标记为已读,则标记为已读
- if (!announcement.is_read || (announcement.is_expired && !announcement.is_read)) {
- await db.execute(`
- INSERT INTO user_announcement_reads (user_id, announcement_id, is_read, read_at)
- VALUES (?, ?, TRUE, NOW())
- ON DUPLICATE KEY UPDATE is_read = TRUE, read_at = NOW()
- `, [req.user.id, id]);
-
- announcement.is_read = true;
- announcement.read_at = new Date();
- }
-
- res.json({ success: true, data: announcement });
- } catch (error) {
- console.error('获取公告详情失败:', error);
- res.status(500).json({ success: false, message: '获取公告详情失败' });
- }
-});
-
-
-router.post('/', auth, adminAuth, async (req, res) => {
- try {
- const db = getDB();
- const {
- title,
- content,
- type = 'system',
- priority = 'medium',
- status = 'draft',
- is_pinned = false,
- publish_time,
- expire_time
- } = req.body;
-
- if (!title || !content) {
- return res.status(400).json({ success: false, message: '标题和内容不能为空' });
- }
-
- const query = `
- INSERT INTO announcements (
- title, content, type, priority, status, is_pinned,
- publish_time, expire_time, created_by, created_at, updated_at
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
- `;
-
- const [result] = await db.execute(query, [
- title,
- content,
- type,
- priority,
- status,
- is_pinned,
- publish_time || null,
- expire_time || null,
- req.user.id
- ]);
-
- res.status(201).json({
- success: true,
- message: '公告创建成功',
- data: { id: result.insertId }
- });
- } catch (error) {
- console.error('创建公告失败:', error);
- res.status(500).json({ success: false, message: '创建公告失败' });
- }
-});
-
-
-router.put('/:id', auth, adminAuth, async (req, res) => {
- try {
- const db = getDB();
- const { id } = req.params;
- let {
- title,
- content,
- type,
- priority,
- status,
- is_pinned,
- publish_time,
- expire_time
- } = req.body;
-
- // 检查公告是否存在
- const [existing] = await db.execute('SELECT id FROM announcements WHERE id = ?', [id]);
- if (existing.length === 0) {
- return res.status(404).json({ success: false, message: '公告不存在' });
- }
-
- const updates = [];
- const params = [];
-
- if (title !== undefined) {
- updates.push('title = ?');
- params.push(title);
- }
- if (content !== undefined) {
- updates.push('content = ?');
- params.push(content);
- }
- if (type !== undefined) {
- updates.push('type = ?');
- params.push(type);
- }
- if (priority !== undefined) {
- updates.push('priority = ?');
- params.push(priority);
- }
- if (status !== undefined) {
- updates.push('status = ?');
- params.push(status);
- }
- if (is_pinned !== undefined) {
- updates.push('is_pinned = ?');
- params.push(is_pinned);
- }
- if (publish_time !== undefined) {
- updates.push('publish_time = ?');
- publish_time = dayjs(publish_time).format('YYYY-MM-DD');
- params.push(publish_time);
- }
- if (expire_time !== undefined) {
- updates.push('expire_time = ?');
- params.push(expire_time);
- }
-
- if (updates.length === 0) {
- return res.status(400).json({ success: false, message: '没有要更新的字段' });
- }
-
- updates.push('updated_at = NOW()');
- params.push(id);
-
- const query = `UPDATE announcements SET ${updates.join(', ')} WHERE id = ?`;
- await db.execute(query, params);
-
- res.json({ success: true, message: '公告更新成功' });
- } catch (error) {
- console.error('更新公告失败:', error);
- res.status(500).json({ success: false, message: '更新公告失败' });
- }
-});
-
-
-router.delete('/:id', auth, adminAuth, async (req, res) => {
- try {
- const db = getDB();
- const { id } = req.params;
-
- // 检查公告是否存在
- const [existing] = await db.execute('SELECT id FROM announcements WHERE id = ?', [id]);
- if (existing.length === 0) {
- return res.status(404).json({ success: false, message: '公告不存在' });
- }
-
- await db.execute('DELETE FROM announcements WHERE id = ?', [id]);
-
- res.json({ success: true, message: '公告删除成功' });
- } catch (error) {
- console.error('删除公告失败:', error);
- res.status(500).json({ success: false, message: '删除公告失败' });
- }
-});
-
-
-router.get('/public/list', async (req, res) => {
- try {
- const db = getDB();
- const { limit = 5 } = req.query;
- const limitValue = Math.max(1, Math.min(50, parseInt(limit)));
-
- const query = `
- SELECT id, title, content, type, priority, publish_time, created_at
- FROM announcements
- WHERE status = 'published'
- AND (expire_time IS NULL OR expire_time > NOW())
- AND (publish_time IS NULL OR publish_time <= NOW())
- ORDER BY is_pinned DESC, created_at DESC
- LIMIT ${limitValue}
- `;
-
- const [announcements] = await db.execute(query, []);
-
- res.json({ success: true, data: announcements });
- } catch (error) {
- console.error('获取公开公告失败:', error);
- res.status(500).json({ success: false, message: '获取公开公告失败' });
- }
-});
-
-// 标记公告为已读
-router.post('/:id/read', auth, async (req, res) => {
- try {
- const db = getDB();
- const { id } = req.params;
-
- // 检查公告是否存在
- const [existing] = await db.execute('SELECT id FROM announcements WHERE id = ?', [id]);
- if (existing.length === 0) {
- return res.status(404).json({ success: false, message: '公告不存在' });
- }
-
- // 标记为已读
- await db.execute(`
- INSERT INTO user_announcement_reads (user_id, announcement_id, is_read, read_at)
- VALUES (?, ?, TRUE, NOW())
- ON DUPLICATE KEY UPDATE is_read = TRUE, read_at = NOW()
- `, [req.user.id, id]);
-
- res.json({ success: true, message: '已标记为已读' });
- } catch (error) {
- console.error('标记公告已读失败:', error);
- res.status(500).json({ success: false, message: '标记公告已读失败' });
- }
-});
-
-// 获取用户未读公告数量
-router.get('/unread/count', auth, async (req, res) => {
- try {
- const db = getDB();
-
- const query = `
- SELECT COUNT(*) as unread_count
- FROM announcements a
- LEFT JOIN user_announcement_reads uar ON a.id = uar.announcement_id AND uar.user_id = ?
- WHERE a.status = 'published'
- AND (a.publish_time IS NULL OR a.publish_time <= NOW())
- AND (a.expire_time IS NULL OR a.expire_time > NOW())
- AND (uar.is_read IS NULL OR uar.is_read = FALSE)
- `;
-
- const [result] = await db.execute(query, [req.user.id]);
-
- res.json({
- success: true,
- data: {
- unread_count: result[0].unread_count
- }
- });
- } catch (error) {
- console.error('获取未读公告数量失败:', error);
- res.status(500).json({ success: false, message: '获取未读公告数量失败' });
- }
-});
-
-// 批量标记公告为已读
-router.post('/batch/read', auth, async (req, res) => {
- try {
- const db = getDB();
- const { announcement_ids } = req.body;
-
- if (!announcement_ids || !Array.isArray(announcement_ids) || announcement_ids.length === 0) {
- return res.status(400).json({ success: false, message: '请提供有效的公告ID列表' });
- }
-
- // 批量标记为已读
- const values = announcement_ids.map(() => '(?, ?, TRUE, NOW())').join(', ');
- const params = announcement_ids.flatMap(id => [req.user.id, id]);
-
- await db.execute(`
- INSERT INTO user_announcement_reads (user_id, announcement_id, is_read, read_at)
- VALUES ${values}
- ON DUPLICATE KEY UPDATE is_read = TRUE, read_at = NOW()
- `, params);
-
- res.json({ success: true, message: '批量标记已读成功' });
- } 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/auth.js b/routes/auth.js
deleted file mode 100644
index d65b95d..0000000
--- a/routes/auth.js
+++ /dev/null
@@ -1,345 +0,0 @@
-const express = require('express');
-const bcrypt = require('bcryptjs');
-const jwt = require('jsonwebtoken');
-const {getDB} = require('../database');
-
-const router = express.Router();
-const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
-router.post('/register', async (req, res) => {
- try {
- const db = getDB();
- await db.query('START TRANSACTION');
-
- const {
- username,
- phone,
- password,
- city,
- district_id: district,
- province,
- inviter = null,
- captchaId,
- captchaText,
- smsCode, // 短信验证码
- role = 'user'
- } = req.body;
-
- if (!username || !phone || !password || !city || !district || !province) {
- return res.status(400).json({success: false, message: '用户名、手机号、密码、城市和区域不能为空'});
- }
-
- if (!captchaId || !captchaText) {
- return res.status(400).json({success: false, message: '图形验证码不能为空'});
- }
- const storedCaptcha = global.captchaStore.get(captchaId);
- console.log(storedCaptcha);
-
- if (!storedCaptcha) {
- return res.status(400).json({
- success: false,
- message: '验证码不存在或已过期'
- });
- }
-
- // 检查是否过期
- if (Date.now() > storedCaptcha.expires) {
- global.captchaStore.delete(captchaId);
- return res.status(400).json({
- success: false,
- message: '验证码已过期'
- });
- }
-
- // 验证验证码(不区分大小写)
- const isValid = storedCaptcha.text === captchaText.toLowerCase();
-
- // 删除已验证的验证码
- global.captchaStore.delete(captchaId);
-
- if (!isValid) {
- return res.status(400).json({
- success: false,
- message: '验证码错误'
- });
- }
- if (!smsCode) {
- return res.status(400).json({success: false, message: '短信验证码不能为空'});
- }
- // 验证短信验证码
- const smsAPI = require('./sms');
- const smsValid = smsAPI.verifySMSCode(phone, smsCode);
- if (!smsValid) {
- return res.status(400).json({success: false, message: '短信验证码错误或已过期'});
- }
-
- // 验证手机号格式
- const phoneRegex = /^1[3-9]\d{9}$/;
- if (!phoneRegex.test(phone)) {
- return res.status(400).json({success: false, message: '手机号格式不正确'});
- }
-
-
- // 检查用户是否已存在
- const [existingUsers] = await db.execute(
- 'SELECT id, payment_status FROM users WHERE username = ? OR phone = ?',
- [username, phone]
- );
-
- if (existingUsers.length > 0) {
- return res.status(400).json({success: false, message: '用户名或手机号已存在'});
- }
-
- // 加密密码
- const hashedPassword = await bcrypt.hash(password, 10);
-
- // 创建用户(初始状态为未支付)
- const [result] = await db.execute(
- 'INSERT INTO users (username, phone, password, role, points, audit_status, city, district_id, payment_status, province, inviter) VALUES (?, ?, ?, ?, ?, ?, ?, ?, "unpaid", ?, ?)',
- [username, phone, hashedPassword, role, 0, 'pending', city, district, province, inviter]
- );
-
- const userId = result.insertId;
- await db.query('COMMIT');
-
- // 生成JWT token(用于支付流程)
- const token = jwt.sign(
- {userId: userId, username, role},
- JWT_SECRET,
- {expiresIn: '24h'}
- );
-
- res.status(201).json({
- success: true,
- message: '用户信息创建成功,请完成支付以激活账户',
- token,
- user: {
- id: userId,
- username,
- phone,
- role,
- points: 0,
- audit_status: 'pending',
- city,
- district,
- paymentStatus: 'unpaid'
- },
- needPayment: true
- });
- } catch (error) {
- try {
- // await getDB().query('ROLLBACK');
- } catch (rollbackError) {
- console.error('回滚错误:', rollbackError);
- }
- console.error('注册错误详情:', error);
- console.error('错误堆栈:', error.stack);
- res.status(500).json({
- success: false,
- message: '注册失败',
- error: process.env.NODE_ENV === 'development' ? error.message : undefined
- });
- }
-});
-
-
-router.post('/login', async (req, res) => {
- try {
- const db = getDB();
- const {username, password, captchaId, captchaText} = req.body;
-
- if (!username || !password) {
- return res.status(400).json({success: false, message: '用户名和密码不能为空'});
- }
-
- if (!captchaId || !captchaText) {
- return res.status(400).json({success: false, message: '验证码不能为空'});
- }
- // 获取存储的验证码
- const storedCaptcha = global.captchaStore.get(captchaId);
- console.log(storedCaptcha);
-
- if (!storedCaptcha) {
- return res.status(400).json({
- success: false,
- message: '验证码不存在或已过期'
- });
- }
-
- // 检查是否过期
- if (Date.now() > storedCaptcha.expires) {
- global.captchaStore.delete(captchaId);
- return res.status(400).json({
- success: false,
- message: '验证码已过期'
- });
- }
-
- // 验证验证码(不区分大小写)
- const isValid = storedCaptcha.text === captchaText.toLowerCase();
-
- // 删除已验证的验证码
- global.captchaStore.delete(captchaId);
-
- if (!isValid) {
- return res.status(400).json({
- success: false,
- message: '验证码错误'
- });
- }
-
- // 注意:验证码已在前端通过 /captcha/verify 接口验证过,这里不再重复验证
-
- // 查找用户(包含支付状态)
- console.log('登录尝试 - 用户名:', username);
- const [users] = await db.execute(
- 'SELECT * FROM users WHERE username = ?',
- [username]
- );
-
- console.log('查找到的用户数量:', users.length);
- if (users.length === 0) {
- console.log('用户不存在:', username);
- return res.status(401).json({success: false, message: '用户名或密码错误'});
- }
-
- const user = users[0];
- console.log('找到用户:', user.username, '密码长度:', user.password ? user.password.length : 'null');
-
- // 验证密码
- console.log('验证密码 - 输入密码:', password, '数据库密码前10位:', user.password ? user.password.substring(0, 10) : 'null');
- const isValidPassword = await bcrypt.compare(password, user.password);
- console.log('密码验证结果:', isValidPassword);
-
- if (!isValidPassword) {
- console.log('密码验证失败');
- return res.status(401).json({success: false, message: '用户名或密码错误'});
- }
-
- // 检查支付状态(管理员除外)
- if (user.role !== 'admin' && user.payment_status === 'unpaid') {
- const token = jwt.sign(
- {userId: user.id, username: user.username, role: user.role},
- JWT_SECRET,
- {expiresIn: '5m'}
- );
- return res.status(200).json({
- success: false,
- message: '您的账户尚未激活,请完成支付后再登录',
- needPayment: true,
- user: user[0],
- token
- });
- }
-
- // 检查用户审核状态(管理员除外,只阻止被拒绝的用户)
- if (user.role !== 'admin' && user.audit_status === 'rejected') {
- return res.status(403).json({success: false, message: '您的账户审核未通过,请联系管理员'});
- }
- // 待审核用户可以正常登录使用系统,但匹配功能会有限制
-
- // 生成JWT token
- const token = jwt.sign(
- {userId: user.id, username: user.username, role: user.role},
- JWT_SECRET,
- {expiresIn: '24h'}
- );
- const [is_distribution] = await db.execute(`
- SELECT *
- FROM distribution
- WHERE user_id = ?`, [user.id]);
- user.distribution = is_distribution.length > 0 ? true : false;
- res.json({
- success: true,
- message: '登录成功',
- token,
- user
- });
- } catch (error) {
- console.error('登录错误:', error);
- res.status(500).json({success: false, message: '登录失败'});
- }
-});
-
-// 验证token中间件
-const authenticateToken = (req, res, next) => {
- const authHeader = req.headers['authorization'];
- const token = authHeader && authHeader.split(' ')[1];
-
- if (!token) {
- return res.status(401).json({success: false, message: '访问令牌缺失'});
- }
-
- jwt.verify(token, JWT_SECRET, (err, user) => {
- if (err) {
- return res.status(403).json({success: false, message: '访问令牌无效'});
- }
- req.user = user;
- next();
- });
-};
-
-// 获取当前用户信息
-router.get('/me', authenticateToken, async (req, res) => {
- try {
- const db = getDB();
- const [users] = await db.execute(
- 'SELECT id, username, role, avatar, points, created_at FROM users WHERE id = ?',
- [req.user.userId]
- );
-
- if (users.length === 0) {
- return res.status(404).json({success: false, message: '用户不存在'});
- }
-
- res.json({success: true, user: users[0]});
- } catch (error) {
- console.error('获取用户信息错误:', error);
- res.status(500).json({success: false, message: '获取用户信息失败'});
- }
-});
-
-// 修改密码
-router.put('/change-password', authenticateToken, async (req, res) => {
- try {
- const db = getDB();
- const {currentPassword, newPassword} = req.body;
-
- if (!currentPassword || !newPassword) {
- return res.status(400).json({success: false, message: '旧密码和新密码不能为空'});
- }
-
- // 获取用户当前密码
- const [users] = await db.execute(
- 'SELECT password FROM users WHERE id = ?',
- [req.user.userId]
- );
-
- if (users.length === 0) {
- return res.status(404).json({success: false, message: '用户不存在'});
- }
-
- // 验证旧密码
- const isValidPassword = await bcrypt.compare(currentPassword, users[0].password);
-
- if (!isValidPassword) {
- return res.status(400).json({success: false, message: '旧密码错误'});
- }
-
- // 加密新密码
- const hashedNewPassword = await bcrypt.hash(newPassword, 10);
-
- // 更新密码
- await db.execute(
- 'UPDATE users SET password = ? WHERE id = ?',
- [hashedNewPassword, req.user.userId]
- );
-
- res.json({success: true, message: '密码修改成功'});
- } catch (error) {
- console.error('修改密码错误:', error);
- res.status(500).json({success: false, message: '修改密码失败'});
- }
-});
-
-module.exports = router;
-module.exports.authenticateToken = authenticateToken;
\ No newline at end of file
diff --git a/routes/captcha.js b/routes/captcha.js
deleted file mode 100644
index 4fb4f11..0000000
--- a/routes/captcha.js
+++ /dev/null
@@ -1,220 +0,0 @@
-const express = require('express');
-const crypto = require('crypto');
-const router = express.Router();
-
-
-
-// 内存存储验证码(生产环境建议使用Redis)
-
-
-/**
- * 生成随机验证码字符串
- * @param {number} length 验证码长度
- * @returns {string} 验证码字符串
- */
-function generateCaptchaText(length = 4) {
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
- let result = '';
- for (let i = 0; i < length; i++) {
- result += chars.charAt(Math.floor(Math.random() * chars.length));
- }
- return result;
-}
-
-/**
- * 生成SVG验证码图片
- * @param {string} text 验证码文本
- * @returns {string} SVG字符串
- */
-function generateCaptchaSVG(text) {
- const width = 120;
- const height = 40;
- const fontSize = 18;
-
- // 生成随机颜色
- const getRandomColor = () => {
- const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8'];
- return colors[Math.floor(Math.random() * colors.length)];
- };
-
- // 生成干扰线
- const generateNoise = () => {
- let noise = '';
- for (let i = 0; i < 3; i++) {
- const x1 = Math.random() * width;
- const y1 = Math.random() * height;
- const x2 = Math.random() * width;
- const y2 = Math.random() * height;
- noise += ``;
- }
- return noise;
- };
-
- // 生成干扰点
- const generateDots = () => {
- let dots = '';
- for (let i = 0; i < 20; i++) {
- const x = Math.random() * width;
- const y = Math.random() * height;
- const r = Math.random() * 2 + 1;
- dots += ``;
- }
- return dots;
- };
-
- // 生成文字
- let textElements = '';
- const charWidth = width / text.length;
-
- for (let i = 0; i < text.length; i++) {
- const char = text[i];
- const x = charWidth * i + charWidth / 2;
- const y = height / 2 + fontSize / 3;
- const rotation = (Math.random() - 0.5) * 30; // 随机旋转角度
- const color = getRandomColor();
-
- textElements += `
-
- ${char}
- `;
- }
-
- const svg = `
- `;
-
- return svg;
-}
-
-
-router.get('/generate', (req, res) => {
- try {
- // 生成验证码文本
- const captchaText = generateCaptchaText();
-
- // 生成唯一ID
- const captchaId = crypto.randomUUID();
-
- // 存储验证码(5分钟过期)
- global.captchaStore.set(captchaId, {
- text: captchaText.toLowerCase(), // 存储小写用于比较
- expires: Date.now() + 5 * 60 * 1000 // 5分钟过期
- });
-
- // 生成SVG图片
- const svgImage = generateCaptchaSVG(captchaText);
- res.json({
- success: true,
- data: {
- captchaId,
- image: `data:image/svg+xml;base64,${Buffer.from(svgImage).toString('base64')}`
- }
- });
- } catch (error) {
- console.error('生成验证码失败:', error);
- res.status(500).json({
- success: false,
- message: '生成验证码失败'
- });
- }
-});
-
-
-router.post('/verify', (req, res) => {
- try {
- const { captchaId, captchaText } = req.body;
-
- if (!captchaId || !captchaText) {
- return res.status(400).json({
- success: false,
- message: '验证码ID和验证码不能为空'
- });
- }
-
- // 获取存储的验证码
- const storedCaptcha = global.captchaStore.get(captchaId);
-
- if (!storedCaptcha) {
- return res.status(400).json({
- success: false,
- message: '验证码不存在或已过期'
- });
- }
-
- // 检查是否过期
- if (Date.now() > storedCaptcha.expires) {
- global.captchaStore.delete(captchaId);
- return res.status(400).json({
- success: false,
- message: '验证码已过期'
- });
- }
-
- // 验证验证码(不区分大小写)
- const isValid = storedCaptcha.text === captchaText.toLowerCase();
-
- // 验证后删除验证码(无论成功失败)
- global.captchaStore.delete(captchaId);
-
- if (isValid) {
- res.json({
- success: true,
- message: '验证码验证成功'
- });
- } else {
- res.status(400).json({
- success: false,
- message: '验证码错误'
- });
- }
- } catch (error) {
- console.error('验证验证码失败:', error);
- res.status(500).json({
- success: false,
- message: '验证验证码失败'
- });
- }
-});
-
-// 清理过期验证码的定时任务
-setInterval(() => {
- const now = Date.now();
- for (const [id, captcha] of global.captchaStore.entries()) {
- if (now > captcha.expires) {
- global.captchaStore.delete(id);
- }
- }
-}, 60 * 1000); // 每分钟清理一次
-
-// 导出验证函数供其他模块使用
-module.exports = router;
-module.exports.verifyCaptcha = (captchaId, captchaText) => {
- const captcha = global.captchaStore.get(captchaId);
- if (!captcha) {
- return false; // 验证码不存在或已过期
- }
-
- if (captcha.text.toLowerCase() !== captchaText.toLowerCase()) {
- return false; // 验证码错误
- }
-
- // 验证成功后删除验证码(一次性使用)
- global.captchaStore.delete(captchaId);
- return true;
-};
\ No newline at end of file
diff --git a/routes/cart.js b/routes/cart.js
deleted file mode 100644
index 6cb109f..0000000
--- a/routes/cart.js
+++ /dev/null
@@ -1,935 +0,0 @@
-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/matching.js b/routes/matching.js
deleted file mode 100644
index d9e1db5..0000000
--- a/routes/matching.js
+++ /dev/null
@@ -1,626 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const { getDB } = require('../database');
-const matchingService = require('../services/matchingService');
-const { auth } = require('../middleware/auth');
-const { default: axios } = require('axios');
-
-
-router.post('/create', auth, async (req, res) => {
- try {
- console.log('匹配订单创建请求 - 用户ID:', req.user.id);
- console.log('请求体:', req.body);
- const userId = req.user.id;
- const { matchingType = 'small', customAmount } = req.body;
- const [user_type] = await getDB().query(`SELECT count(*) as total FROM users WHERE id=${userId} and user_type='directly_operated'`);
- if(user_type[0].total > 0){
- return res.status(400).json({message: '平台暂不支持直营用户获得融豆'})
- }
- // 验证匹配类型
- if (!['small', 'large'].includes(matchingType)) {
- return res.status(400).json({ message: '无效的匹配类型' });
- }
-
- // 验证大额匹配的金额
- if (matchingType === 'large') {
- if (!customAmount || typeof customAmount !== 'number') {
- return res.status(400).json({ message: '大额匹配需要指定金额' });
- }
- if (customAmount < 3000 || customAmount > 50000) {
- return res.status(400).json({ message: '大额匹配金额必须在3000-50000之间' });
- }
- }
-
- // 检查用户是否有未完成的匹配订单(排除已失败的订单)
- const [existingOrders] = await getDB().execute(
- 'SELECT COUNT(*) as count FROM matching_orders WHERE initiator_id = ? AND status IN ("pending", "matching")',
- [userId]
- );
-
- if (existingOrders[0].count > 0) {
- return res.status(400).json({ message: '您有未完成的匹配订单,请等待完成后再创建新订单' });
- }
-
- // 校验用户是否已上传必要的证件和收款码
- const [userInfo] = await getDB().execute(
- 'SELECT business_license, id_card_front, id_card_back, wechat_qr, alipay_qr, bank_card, unionpay_qr FROM users WHERE id = ?',
- [userId]
- );
-
- if (userInfo.length === 0) {
- return res.status(404).json({ message: '用户不存在' });
- }
-
- const user = userInfo[0];
-
- // 检查证件是否已上传
- if (!user.business_license || !user.id_card_front || !user.id_card_back) {
- return res.status(400).json({
- message: '开始匹配前,请先在个人中心上传营业执照和身份证正反面',
- code: 'MISSING_DOCUMENTS'
- });
- }
-
- // 检查收款码是否已上传(至少需要一种收款方式)
- if (!user.wechat_qr && !user.alipay_qr && !user.bank_card && !user.unionpay_qr) {
- return res.status(400).json({
- message: '开始匹配前,请先在个人中心设置至少一种收款方式(微信、支付宝、银行卡或云闪付)',
- code: 'MISSING_PAYMENT_METHODS'
- });
- }
-
- // 创建匹配订单
- const result = await matchingService.createMatchingOrder(userId, matchingType, customAmount);
-
- const message = matchingType === 'small'
- ? '小额匹配成功!已为您生成3笔转账分配'
- : `大额匹配成功!已为您生成${result.totalAmount}笔转账分配`;
-
- res.json({
- success: true,
- message,
- data: {
- matchingOrderId: result.orderId,
- amounts: result.amounts,
- matchingType: result.matchingType,
- totalAmount: result.totalAmount
- }
- });
-
- } catch (error) {
- console.error('创建匹配订单失败:', error);
- res.status(500).json({ message: error.message || '匹配失败,请稍后重试' });
- }
-});
-
-
-router.get('/my-orders', auth, async (req, res) => {
- try {
- const userId = req.user.id;
- const page = parseInt(req.query.page) || 1;
- const limit = parseInt(req.query.limit) || 10;
-
- const orders = await matchingService.getUserMatchingOrders(userId, page, limit);
-
- res.json({
- success: true,
- data: orders
- });
-
- } catch (error) {
- console.error('获取匹配订单失败:', error);
- res.status(500).json({ message: '获取匹配订单失败' });
- }
-});
-
-/**
- * @swagger
- * /api/matching/pending-allocations:
- * get:
- * summary: 获取用户待处理的分配
- * tags: [Matching]
- * security:
- * - bearerAuth: []
- * responses:
- * 200:
- * description: 成功获取待处理分配
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: array
- * items:
- * $ref: '#/components/schemas/Allocation'
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-router.get('/pending-allocations', auth, async (req, res) => {
- try {
- const userId = req.user.id;
-
- const allocations = await matchingService.getUserPendingAllocations(userId);
-
- res.json({
- success: true,
- data: allocations
- });
-
- } catch (error) {
- console.error('获取待处理分配失败:', error);
- res.status(500).json({ message: '获取待处理分配失败' });
- }
-});
-
-/**
- * @swagger
- * /api/matching/allocation/{id}:
- * get:
- * summary: 获取分配详情
- * tags: [Matching]
- * 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:
- * $ref: '#/components/schemas/Allocation'
- * 401:
- * description: 未授权
- * 403:
- * description: 无权限访问
- * 404:
- * description: 分配不存在
- * 500:
- * description: 服务器错误
- */
-router.get('/allocation/:id', auth, async (req, res) => {
- try {
- const db = getDB();
- const allocationId = req.params.id;
- const userId = req.user.id;
-
- // 首先获取分配信息
- const [allocations] = await db.execute(`
- SELECT
- oa.id,
- oa.from_user_id,
- oa.to_user_id,
- oa.cycle_number,
- oa.amount,
- oa.status,
- oa.created_at,
- from_user.username as from_user_name,
- to_user.username as to_user_name
- FROM transfers oa
- JOIN users from_user ON oa.from_user_id = from_user.id
- JOIN users to_user ON oa.to_user_id = to_user.id
- WHERE oa.id = ?
- `, [allocationId]);
-
- if (allocations.length === 0) {
- return res.status(404).json({ success: false, message: '分配不存在' });
- }
-
- const allocation = allocations[0];
-
- // 检查权限:只有分配的发起人或接收人可以查看
- if (allocation.from_user_id !== userId && allocation.to_user_id !== userId) {
- return res.status(403).json({ success: false, message: '无权限访问此分配' });
- }
-
- res.json({
- success: true,
- data: allocation
- });
- } catch (error) {
- console.error('获取分配详情错误:', error);
- res.status(500).json({ success: false, message: '获取分配详情失败' });
- }
-});
-
-/**
- * @swagger
- * /api/matching/confirm-allocation/{allocationId}:
- * post:
- * summary: 确认分配(创建转账)
- * tags: [Matching]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: allocationId
- * required: true
- * schema:
- * type: integer
- * description: 分配ID
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * transferAmount:
- * type: number
- * description: 转账金额
- * description:
- * type: string
- * description: 转账描述
- * voucher:
- * type: string
- * description: 转账凭证(图片URL)
- * required:
- * - voucher
- * responses:
- * 200:
- * description: 转账凭证提交成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * message:
- * type: string
- * data:
- * type: object
- * properties:
- * transferId:
- * type: integer
- * 400:
- * description: 参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-router.post('/confirm-allocation/:allocationId', auth, async (req, res) => {
- try {
- const { allocationId } = req.params;
- const userId = req.user.id;
- const { transferAmount, description, voucher } = req.body; // 获取转账信息
-
- // 校验转账凭证是否存在
- if (!voucher) {
- return res.status(400).json({
- success: false,
- message: '请上传转账凭证'
- });
- }
-
- // 调用服务层方法,传递完整的转账信息
- const transferId = await matchingService.confirmAllocation(
- allocationId,
- userId,
- transferAmount,
- description,
- voucher
- );
-
- res.json({
- success: true,
- message: '转账凭证已提交,转账记录已创建',
- data: { transferId }
- });
-
- axios.post('http://localhost:5000/ocr',{
- id: allocationId
- }).then(res => {
- console.log(res.data)
- })
-
- } catch (error) {
- console.error('确认分配失败:', error);
- res.status(500).json({ message: error.message || '确认分配失败' });
- }
-});
-
-/**
- * @swagger
- * /api/matching/reject-allocation/{allocationId}:
- * post:
- * summary: 拒绝分配
- * tags: [Matching]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: allocationId
- * required: true
- * schema:
- * type: integer
- * description: 分配ID
- * requestBody:
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * reason:
- * type: string
- * description: 拒绝原因
- * responses:
- * 200:
- * description: 拒绝分配成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * message:
- * type: string
- * 401:
- * description: 未授权
- * 404:
- * description: 分配不存在或无权限
- * 500:
- * description: 服务器错误
- */
-router.post('/reject-allocation/:allocationId', auth, async (req, res) => {
- try {
- const { allocationId } = req.params;
- const userId = req.user.id;
- const { reason } = req.body;
-
- const db = getDB();
-
- // 获取分配信息
- const [allocations] = await db.execute(
- 'SELECT * FROM transfers WHERE id = ? AND from_user_id = ?',
- [allocationId, userId]
- );
-
- if (allocations.length === 0) {
- return res.status(404).json({ message: '分配不存在或无权限' });
- }
-
- const allocation = allocations[0];
-
- // 更新分配状态
- await db.execute(
- 'UPDATE transfers SET status = "rejected" WHERE id = ?',
- [allocationId]
- );
-
- // 记录拒绝动作
- await db.execute(
- 'INSERT INTO matching_records (matching_order_id, user_id, action, note) VALUES (?, ?, "reject", ?)',
- [allocation.matching_order_id, userId, reason || '用户拒绝']
- );
-
- // 检查订单状态是否需要更新
- const statusResult = await matchingService.checkOrderStatusAfterRejection(
- allocation.matching_order_id,
- allocation.cycle_number
- );
-
- let message = '已拒绝分配';
- if (statusResult === 'failed') {
- message = '已拒绝分配,该轮次所有分配均被拒绝,匹配订单已失败';
- }
-
- res.json({
- success: true,
- message
- });
-
- } catch (error) {
- console.error('拒绝分配失败:', error);
- res.status(500).json({ message: '拒绝分配失败' });
- }
-});
-
-/**
- * @swagger
- * /api/matching/order/{orderId}:
- * get:
- * summary: 获取匹配订单详情
- * tags: [Matching]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: orderId
- * 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/MatchingOrder'
- * allocations:
- * type: array
- * items:
- * $ref: '#/components/schemas/Allocation'
- * records:
- * type: array
- * items:
- * type: object
- * 401:
- * description: 未授权
- * 403:
- * description: 无权限查看
- * 404:
- * description: 订单不存在
- * 500:
- * description: 服务器错误
- */
-router.get('/order/:orderId', auth, async (req, res) => {
- try {
- const { orderId } = req.params;
- const userId = req.user.id;
-
- const db = getDB();
-
- // 获取订单基本信息
- const [orders] = await db.execute(
- `SELECT mo.*, u.username as initiator_name,u.real_name as initiator_real_name
- FROM matching_orders mo
- JOIN users u ON mo.initiator_id = u.id
- WHERE mo.id = ?`,
- [orderId]
- );
-
- if (orders.length === 0) {
- return res.status(404).json({ message: '匹配订单不存在' });
- }
-
- const order = orders[0];
-
- // 检查权限(订单发起人或参与者)
- const [userCheck] = await db.execute(
- `SELECT COUNT(*) as count FROM (
- SELECT initiator_id as user_id FROM matching_orders WHERE id = ?
- UNION
- SELECT from_user_id as user_id FROM transfers WHERE id = ?
- ) as participants WHERE user_id = ?`,
- [orderId, orderId, userId]
- );
-
- if (userCheck[0].count === 0) {
- return res.status(403).json({ message: '无权限查看此订单' });
- }
-
- // 获取分配信息
- const [allocations] = await db.execute(
- `SELECT oa.*,
- uf.username as from_user_name,
- ut.username as to_user_name
- FROM transfers oa
- JOIN users uf ON oa.from_user_id = uf.id
- JOIN users ut ON oa.to_user_id = ut.id
- WHERE oa.id = ?
- ORDER BY oa.cycle_number, oa.created_at`,
- [orderId]
- );
-
- // 获取匹配记录
- const [records] = await db.execute(
- `SELECT mr.*, u.username
- FROM matching_records mr
- JOIN users u ON mr.user_id = u.id
- WHERE mr.matching_order_id = ?
- ORDER BY mr.created_at`,
- [orderId]
- );
-
- res.json({
- success: true,
- data: {
- order,
- allocations,
- records
- }
- });
-
- } catch (error) {
- console.error('获取匹配订单详情失败:', error);
- res.status(500).json({ message: '获取匹配订单详情失败' });
- }
-});
-
-/**
- * @swagger
- * /api/matching/stats:
- * get:
- * summary: 获取匹配统计信息
- * tags: [Matching]
- * security:
- * - bearerAuth: []
- * responses:
- * 200:
- * description: 成功获取统计信息
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: object
- * properties:
- * userStats:
- * type: object
- * properties:
- * initiated_orders:
- * type: integer
- * participated_allocations:
- * type: integer
- * total_initiated_amount:
- * type: number
- * total_participated_amount:
- * type: number
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-router.get('/stats', auth, async (req, res) => {
- try {
- const userId = req.user.id;
- const db = getDB();
-
- // 获取用户统计
- const [userStats] = await db.execute(
- `SELECT
- COUNT(CASE WHEN mo.initiator_id = ? THEN 1 END) as initiated_orders,
- COUNT(CASE WHEN oa.from_user_id = ? THEN 1 END) as participated_allocations,
- SUM(CASE WHEN mo.initiator_id = ? AND mo.status = 'completed' THEN mo.amount ELSE 0 END) as total_initiated_amount,
- SUM(CASE WHEN oa.from_user_id = ? AND oa.status = 'completed' THEN oa.amount ELSE 0 END) as total_participated_amount
- FROM matching_orders mo
- LEFT JOIN transfers oa ON mo.id = oa.id`,
- [userId, userId, userId, userId]
- );
-
- res.json({
- success: true,
- data: {
- userStats: userStats[0]
- }
- });
-
- } catch (error) {
- console.error('获取匹配统计失败:', error);
- res.status(500).json({ message: '获取匹配统计失败' });
- }
-});
-
-
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/matchingAdmin.js b/routes/matchingAdmin.js
deleted file mode 100644
index cd47b51..0000000
--- a/routes/matchingAdmin.js
+++ /dev/null
@@ -1,621 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const db = require('../database');
-const { auth, adminAuth } = require('../middleware/auth');
-const logger = require('../config/logger');
-const matchingService = require('../services/matchingService');
-const dayjs = require('dayjs');
-
-/**
- * @swagger
- * tags:
- * name: MatchingAdmin
- * description: 匹配订单管理员相关接口
- */
-
-/**
- * @swagger
- * components:
- * schemas:
- * UnreasonableMatch:
- * type: object
- * properties:
- * allocation_id:
- * type: integer
- * description: 分配ID
- * from_user_id:
- * type: integer
- * description: 发送方用户ID
- * to_user_id:
- * type: integer
- * description: 接收方用户ID
- * amount:
- * type: number
- * description: 分配金额
- * status:
- * type: string
- * enum: [pending, confirmed, rejected, cancelled]
- * description: 分配状态
- * to_username:
- * type: string
- * description: 接收方用户名
- * to_user_balance:
- * type: number
- * description: 接收方用户余额
- * from_username:
- * type: string
- * description: 发送方用户名
- * from_user_balance:
- * type: number
- * description: 发送方用户余额
- */
-
-/**
- * @swagger
- * /api/matching-admin/unreasonable-matches:
- * get:
- * summary: 获取不合理的匹配记录(正余额用户被匹配的情况)
- * tags: [MatchingAdmin]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: query
- * name: page
- * schema:
- * type: integer
- * default: 1
- * description: 页码
- * - in: query
- * name: limit
- * schema:
- * type: integer
- * default: 20
- * description: 每页数量
- * responses:
- * 200:
- * description: 成功获取不合理匹配记录
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: object
- * properties:
- * matches:
- * type: array
- * items:
- * $ref: '#/components/schemas/UnreasonableMatch'
- * pagination:
- * type: object
- * properties:
- * page:
- * type: integer
- * limit:
- * type: integer
- * total:
- * type: integer
- * totalPages:
- * type: integer
- * 401:
- * description: 未授权
- * 403:
- * description: 无管理员权限
- * 500:
- * description: 服务器错误
- */
-router.get('/unreasonable-matches', auth, adminAuth, async (req, res) => {
- try {
- const page = parseInt(req.query.page) || 1;
- const limit = parseInt(req.query.limit) || 20;
- const offset = (page - 1) * limit;
-
- // 查找正余额用户被匹配的情况
- const query = `SELECT
- oa.id as allocation_id,
- oa.from_user_id,
- oa.to_user_id,
- oa.amount,
- oa.status,
- oa.outbound_date,
- oa.created_at,
- u_to.username as to_username,
- u_to.balance as to_user_balance,
- u_from.username as from_username,
- u_from.balance as from_user_balance,
- mo.amount as total_order_amount
- FROM transfers oa
- JOIN users u_to ON oa.to_user_id = u_to.id
- JOIN users u_from ON oa.from_user_id = u_from.id
- JOIN matching_orders mo ON oa.id = mo.id
- WHERE oa.source_type = 'allocation'
- AND u_to.balance > 0
- AND u_to.is_system_account = FALSE
- AND oa.status IN ('pending', 'confirmed')
- ORDER BY oa.created_at DESC
- LIMIT ${offset}, ${limit}`;
-
- const countQuery = `SELECT COUNT(*) as total
- FROM transfers oa
- JOIN users u_to ON oa.to_user_id = u_to.id
- WHERE oa.source_type = 'allocation'
- AND u_to.balance > 0
- AND u_to.is_system_account = FALSE
- AND oa.status IN ('pending', 'confirmed')`;
-
- const unreasonableMatches = await db.executeQuery(query);
-
- // 获取总数
- const countResult = await db.executeQuery(countQuery);
-
- const total = countResult[0].total;
-
- res.json({
- success: true,
- data: {
- matches: unreasonableMatches,
- pagination: {
- page,
- limit,
- total,
- totalPages: Math.ceil(total / limit)
- }
- }
- });
-
- } catch (error) {
- console.error('获取不合理匹配记录失败:', error);
- res.status(500).json({ message: '获取不合理匹配记录失败' });
- }
-});
-
-
-/**
- * @swagger
- * /api/matching-admin/matching-stats:
- * get:
- * summary: 获取匹配统计信息
- * tags: [MatchingAdmin]
- * security:
- * - bearerAuth: []
- * responses:
- * 200:
- * description: 成功获取匹配统计信息
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: object
- * properties:
- * currentStats:
- * type: object
- * properties:
- * unreasonable_matches:
- * type: integer
- * reasonable_matches:
- * type: integer
- * system_matches:
- * type: integer
- * unreasonable_amount:
- * type: number
- * reasonable_amount:
- * type: number
- * yesterdayStats:
- * type: object
- * properties:
- * total_outbound:
- * type: number
- * unique_amounts:
- * type: integer
- * 401:
- * description: 未授权
- * 403:
- * description: 无管理员权限
- * 500:
- * description: 服务器错误
- */
-router.get('/matching-stats', auth, adminAuth, async (req, res) => {
- try {
-
- // 获取各种统计数据
- const stats = await db.executeQuery(
- `SELECT
- COUNT(CASE WHEN u_to.balance > 0 AND u_to.is_system_account = FALSE AND oa.status IN ('pending', 'confirmed') THEN 1 END) as unreasonable_matches,
- COUNT(CASE WHEN u_to.balance < 0 AND u_to.is_system_account = FALSE AND oa.status IN ('pending', 'confirmed') THEN 1 END) as reasonable_matches,
- COUNT(CASE WHEN u_to.is_system_account = TRUE AND oa.status IN ('pending', 'confirmed') THEN 1 END) as system_matches,
- SUM(CASE WHEN u_to.balance > 0 AND u_to.is_system_account = FALSE AND oa.status IN ('pending', 'confirmed') THEN oa.amount ELSE 0 END) as unreasonable_amount,
- SUM(CASE WHEN u_to.balance < 0 AND u_to.is_system_account = FALSE AND oa.status IN ('pending', 'confirmed') THEN oa.amount ELSE 0 END) as reasonable_amount
- FROM transfers oa
- JOIN users u_to ON oa.to_user_id = u_to.id
- WHERE oa.source_type = 'allocation'`
- );
-
- // 获取昨天的匹配验证统计
- const yesterdayStr = dayjs().subtract(1, 'day').format('YYYY-MM-DD');
-
- const yesterdayStats = await db.executeQuery(
- `SELECT
- SUM(oa.amount) as total_outbound,
- COUNT(DISTINCT oa.amount) as unique_amounts
- FROM transfers oa
- JOIN users u ON oa.from_user_id = u.id
- WHERE oa.source_type = 'allocation' AND DATE(oa.outbound_date) = ? AND oa.status = 'confirmed' AND u.is_system_account = FALSE`,
- [yesterdayStr]
- );
-
- res.json({
- success: true,
- data: {
- currentStats: stats[0],
- yesterdayStats: yesterdayStats[0]
- }
- });
-
- } catch (error) {
- console.error('获取匹配统计失败:', error);
- res.status(500).json({ message: '获取匹配统计失败' });
- }
-});
-
-/**
- * @swagger
- * /api/matching-admin/fix-all-unreasonable:
- * post:
- * summary: 批量修复所有不合理匹配
- * tags: [MatchingAdmin]
- * security:
- * - bearerAuth: []
- * responses:
- * 200:
- * description: 批量修复完成
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * message:
- * type: string
- * data:
- * type: object
- * properties:
- * fixedCount:
- * type: integer
- * description: 成功修复的记录数
- * errorCount:
- * type: integer
- * description: 修复失败的记录数
- * errors:
- * type: array
- * items:
- * type: string
- * description: 错误信息列表(最多10条)
- * 401:
- * description: 未授权
- * 403:
- * description: 无管理员权限
- * 500:
- * description: 服务器错误
- */
-router.post('/fix-all-unreasonable', auth, adminAuth, async (req, res) => {
- try {
- let fixedCount = 0;
- let errorCount = 0;
- const errors = [];
-
- // 获取所有不合理的匹配记录
- const unreasonableMatches = await db.executeQuery(
- `SELECT oa.id, oa.from_user_id, oa.to_user_id, oa.amount, u_to.username, u_to.balance
- FROM transfers oa
- JOIN users u_to ON oa.to_user_id = u_to.id
- WHERE oa.source_type = 'allocation'
- AND u_to.balance > 0
- AND u_to.is_system_account = FALSE
- AND oa.status IN ('pending', 'confirmed')
- ORDER BY u_to.balance DESC`
- );
-
- for (const match of unreasonableMatches) {
- const connection = await db.getDB().getConnection();
- try {
- await connection.query('START TRANSACTION');
-
- // 尝试重新分配给负余额用户
- const usedTargetUsers = new Set([match.to_user_id]);
- const newTargetUser = await matchingService.getMatchingTargetExcluding(match.from_user_id, usedTargetUsers);
-
- // 获取当前时间
- const currentTime = new Date();
-
- if (newTargetUser) {
- // 更新分配目标
- await connection.execute(
- 'UPDATE transfers SET to_user_id = ?, updated_at = ? WHERE id = ?',
- [newTargetUser, currentTime, match.id]
- );
-
- // 记录操作日志
- await connection.execute(
- 'INSERT INTO admin_operation_logs (admin_id, operation_type, target_type, target_id, description, created_at) VALUES (?, "batch_fix_matching", "allocation", ?, ?, ?)',
- [req.user.id, match.id, `批量修复:从正余额用户${match.username}(余额${match.balance}元)重新分配${match.amount}元给负余额用户`, currentTime]
- );
-
- fixedCount++;
- } else {
- // 如果没有可用的负余额用户,取消分配
- await connection.execute(
- 'UPDATE transfers SET status = "cancelled", updated_at = ? WHERE id = ?',
- [currentTime, match.id]
- );
-
- await connection.execute(
- 'INSERT INTO admin_operation_logs (admin_id, operation_type, target_type, target_id, description, created_at) VALUES (?, "batch_fix_matching", "allocation", ?, ?, ?)',
- [req.user.id, match.id, `批量修复:取消正余额用户${match.username}(余额${match.balance}元)的${match.amount}元分配`, currentTime]
- );
-
- fixedCount++;
- }
-
- await connection.query('COMMIT');
- connection.release();
-
- } catch (error) {
- await connection.query('ROLLBACK');
- connection.release();
- errorCount++;
- errors.push(`分配ID ${match.id}: ${error.message}`);
- console.error(`修复分配${match.id}失败:`, error);
- }
- }
-
- res.json({
- success: true,
- message: `批量修复完成:成功修复${fixedCount}条记录,失败${errorCount}条记录`,
- data: {
- fixedCount,
- errorCount,
- errors: errors.slice(0, 10) // 只返回前10个错误
- }
- });
-
- } catch (error) {
- console.error('批量修复不合理匹配失败:', error);
- res.status(500).json({ message: '批量修复不合理匹配失败' });
- }
-});
-
-/**
- * @swagger
- * /api/matching-admin/confirm-allocation/{allocationId}:
- * post:
- * summary: 管理员确认分配
- * tags: [MatchingAdmin]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: allocationId
- * 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: 未授权
- * 403:
- * description: 无管理员权限
- * 404:
- * description: 分配不存在或状态不是待处理
- * 500:
- * description: 服务器错误
- */
-router.post('/confirm-allocation/:allocationId', auth, adminAuth, async (req, res) => {
- try {
- const { allocationId } = req.params;
- const adminId = req.user.id;
-
- const connection = await db.getDB().getConnection();
-
- try {
- await connection.query('START TRANSACTION');
-
- // 检查分配是否存在且状态为pending
- const [allocations] = await connection.execute(
- `SELECT oa.*, u_from.username as from_username, u_to.username as to_username
- FROM transfers oa
- JOIN users u_from ON oa.from_user_id = u_from.id
- JOIN users u_to ON oa.to_user_id = u_to.id
- WHERE oa.source_type = 'allocation' AND oa.id = ? AND oa.status = 'pending'`,
- [allocationId]
- );
-
- if (allocations.length === 0) {
- await connection.query('ROLLBACK');
- connection.release();
- return res.status(404).json({ message: '分配不存在或状态不是待处理' });
- }
-
- const allocation = allocations[0];
-
- // 获取当前时间
- const currentTime = new Date();
-
- // 计算3小时后的截止时间
- const deadline = new Date();
- deadline.setHours(deadline.getHours() + 3);
-
- // 创建转账记录,直接设置为confirmed状态
- const transferDescription = `匹配订单 ${allocation.matching_order_id} 第 ${allocation.cycle_number} 轮转账(管理员确认)`;
- const [transferResult] = await connection.execute(
- `INSERT INTO transfers (from_user_id, to_user_id, amount, transfer_type, status, description, deadline_at, confirmed_at, source_type) VALUES (?, ?, ?, "user_to_user", "confirmed", ?, ?, ?, 'allocation')`,
- [
- allocation.from_user_id,
- allocation.to_user_id,
- allocation.amount,
- transferDescription,
- deadline,
- currentTime
- ]
- );
-
- // 更新分配状态为已确认,并关联转账记录
- await connection.execute(
- 'UPDATE transfers SET status = "confirmed", transfer_id = ?, confirmed_at = ?, updated_at = ? WHERE id = ?',
- [transferResult.insertId, currentTime, currentTime, allocationId]
- );
-
- // 记录管理员操作日志
- await connection.execute(
- 'INSERT INTO admin_operation_logs (admin_id, operation_type, target_type, target_id, description, created_at) VALUES (?, "confirm_allocation", "allocation", ?, ?, ?)',
- [adminId, allocationId, `管理员确认分配:${allocation.from_username} -> ${allocation.to_username},金额:${allocation.amount}元`, currentTime]
- );
-
- // 记录确认动作到匹配记录
- await connection.execute(
- 'INSERT INTO matching_records (matching_order_id, user_id, action, amount, note) VALUES (?, ?, "confirm", ?, ?)',
- [
- allocation.matching_order_id,
- adminId,
- allocation.amount,
- '管理员确认分配'
- ]
- );
-
- await connection.query('COMMIT');
- connection.release();
-
- res.json({
- success: true,
- message: '分配已确认'
- });
-
- } catch (innerError) {
- await connection.query('ROLLBACK');
- connection.release();
- throw innerError;
- }
-
- } catch (error) {
- console.error('确认分配失败:', error);
- res.status(500).json({ message: error.message || '确认分配失败' });
- }
- });
-
- /**
- * @swagger
- * /api/matching-admin/cancel-allocation/{allocationId}:
- * post:
- * summary: 管理员取消分配
- * tags: [MatchingAdmin]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: allocationId
- * 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: 未授权
- * 403:
- * description: 无管理员权限
- * 404:
- * description: 分配不存在或状态不是待处理
- * 500:
- * description: 服务器错误
- */
-router.post('/cancel-allocation/:allocationId', auth, adminAuth, async (req, res) => {
- try {
- const { allocationId } = req.params;
- const adminId = req.user.id;
-
- const connection = await db.getDB().getConnection();
-
- try {
- await connection.query('START TRANSACTION');
-
- // 检查分配是否存在且状态为pending
- const [allocations] = await connection.execute(
- `SELECT oa.*, u_from.username as from_username, u_to.username as to_username
- FROM transfers oa
- JOIN users u_from ON oa.from_user_id = u_from.id
- JOIN users u_to ON oa.to_user_id = u_to.id
- WHERE oa.source_type = 'allocation' AND oa.id = ? AND oa.status = 'pending'`,
- [allocationId]
- );
-
- if (allocations.length === 0) {
- await connection.query('ROLLBACK');
- connection.release();
- return res.status(404).json({ message: '分配不存在或状态不是待处理' });
- }
-
- const allocation = allocations[0];
-
- // 获取当前时间
- const currentTime = new Date();
-
- // 更新分配状态为已取消
- await connection.execute(
- 'UPDATE transfers SET status = "cancelled", updated_at = ? WHERE id = ?',
- [currentTime, allocationId]
- );
-
- // 记录管理员操作日志
- await connection.execute(
- 'INSERT INTO admin_operation_logs (admin_id, operation_type, target_type, target_id, description, created_at) VALUES (?, "cancel_allocation", "allocation", ?, ?, ?)',
- [adminId, allocationId, `管理员取消分配:${allocation.from_username} -> ${allocation.to_username},金额:${allocation.amount}元`, currentTime]
- );
-
- await connection.query('COMMIT');
- connection.release();
-
- res.json({
- success: true,
- message: '分配已取消'
- });
-
- } catch (innerError) {
- await connection.query('ROLLBACK');
- connection.release();
- throw innerError;
- }
-
- } catch (error) {
- console.error('取消分配失败:', error);
- res.status(500).json({ message: error.message || '取消分配失败' });
- }
- });
-
- module.exports = router;
\ No newline at end of file
diff --git a/routes/payment.js b/routes/payment.js
deleted file mode 100644
index 72a82bd..0000000
--- a/routes/payment.js
+++ /dev/null
@@ -1,400 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const WechatPayService = require('../services/wechatPayService');
-const AlipayService = require('../services/alipayservice');
-const { getDB } = require('../database');
-const { auth, paymentAuth } = require('../middleware/auth');
-
-// 创建支付服务实例
-const wechatPayService = new WechatPayService();
-const alipayService = new AlipayService();
-
-/**
- * 获取支持的支付方式
- * GET /api/payment/methods
- */
-router.get('/methods', (req, res) => {
- res.json({
- success: true,
- data: {
- methods: [
- {
- code: 'wechat_h5',
- name: '微信支付',
- description: '微信H5支付',
- icon: 'wechat',
- enabled: true
- },
- {
- code: 'alipay_wap',
- name: '支付宝支付',
- description: '支付宝手机网站支付',
- icon: 'alipay',
- enabled: true
- }
- ]
- }
- });
-});
-
-/**
- * 创建统一支付订单
- * POST /api/payment/create-order
- */
-router.post('/create-order', paymentAuth, async (req, res) => {
- try {
- const { paymentMethod } = req.body;
- const userId = req.user.id;
- const username = req.user.username;
- const phone = req.user.phone;
-
- // 验证支付方式
- if (!paymentMethod || !['wechat_h5', 'alipay_wap'].includes(paymentMethod)) {
- return res.status(400).json({
- success: false,
- message: '不支持的支付方式'
- });
- }
-
- // 检查用户是否已经支付过
- const db = getDB();
- const [existingOrders] = await db.execute(
- 'SELECT id FROM payment_orders WHERE user_id = ? AND status = "paid"',
- [userId]
- );
-
- if (existingOrders.length > 0) {
- return res.status(400).json({
- success: false,
- message: '用户已完成支付,无需重复支付'
- });
- }
-
- let result;
-
- // 获取客户端IP
- const clientIp = req.headers['x-forwarded-for'] ||
- req.headers['x-real-ip'] ||
- req.connection.remoteAddress ||
- req.socket.remoteAddress ||
- (req.connection.socket ? req.connection.socket.remoteAddress : null) ||
- '127.0.0.1';
-
- // 根据支付方式创建订单
- if (paymentMethod === 'wechat_h5') {
- // 创建微信支付订单
- result = await wechatPayService.createRegistrationPayOrder({
- userId,
- username,
- phone,
- clientIp
- });
- } else if (paymentMethod === 'alipay_wap') {
- // 创建支付宝支付订单
- result = await alipayService.createRegistrationPayOrder({
- userId,
- username,
- phone,
- clientIp
- });
- }
-
- if (result && result.success) {
- res.json({
- success: true,
- data: {
- outTradeNo: result.data.outTradeNo,
- payUrl: result.data.h5Url || result.data.payUrl,
- paymentType: result.data.paymentType,
- paymentMethod
- }
- });
- } else {
- res.status(500).json({
- success: false,
- message: '创建支付订单失败'
- });
- }
- } catch (error) {
- console.error('创建统一支付订单异常:', error);
- res.status(500).json({
- success: false,
- message: error.message || '服务器内部错误'
- });
- }
-});
-
-/**
- * 查询支付状态
- * GET /api/payment/query-status/:outTradeNo
- */
-router.get('/query-status/:outTradeNo', paymentAuth, async (req, res) => {
- try {
- const { outTradeNo } = req.params;
- const userId = req.user.id;
-
- // 验证订单是否属于当前用户
- const db = getDB();
- const [orders] = await db.execute(
- 'SELECT id FROM payment_orders WHERE out_trade_no = ? AND user_id = ?',
- [outTradeNo, userId]
- );
-
- if (orders.length === 0) {
- return res.status(404).json({
- success: false,
- message: '订单不存在或无权限访问'
- });
- }
-
- // 获取订单详细信息,包括trade_type
- const [orderDetails] = await db.execute(
- 'SELECT id, trade_type FROM payment_orders WHERE id = ?',
- [orders[0].id]
- );
-
- if (orderDetails.length === 0) {
- return res.status(404).json({
- success: false,
- message: '订单详情不存在'
- });
- }
-
- let result;
- const tradeType = orderDetails[0].trade_type;
-
- // 根据交易类型查询支付状态
- if (tradeType === 'WECHAT_H5') {
- // 查询微信支付状态
- result = await wechatPayService.queryPaymentStatus(outTradeNo);
- } else if (tradeType === 'ALIPAY_WAP') {
- // 查询支付宝支付状态
- result = await alipayService.queryPaymentStatus(outTradeNo);
- } else {
- return res.status(400).json({
- success: false,
- message: '不支持的支付方式'
- });
- }
-
- res.json(result);
- } catch (error) {
- console.error('查询支付状态失败:', error);
- res.status(500).json({
- success: false,
- message: error.message || '查询支付状态失败'
- });
- }
-});
-
-/**
- * 获取用户支付记录
- * GET /api/payment/orders
- */
-router.get('/orders', paymentAuth, async (req, res) => {
- try {
- const userId = req.user.id;
- const { page = 1, limit = 10, status } = req.query;
-
- const offset = (page - 1) * limit;
- const db = getDB();
-
- let whereClause = 'WHERE user_id = ?';
- let params = [userId];
-
- if (status) {
- whereClause += ' AND status = ?';
- params.push(status);
- }
-
- // 查询订单列表
- const [orders] = await db.execute(
- `SELECT id, out_trade_no, transaction_id, total_fee, body, trade_type,
- status, paid_at, created_at
- FROM payment_orders
- ${whereClause}
- ORDER BY created_at DESC
- LIMIT ? OFFSET ?`,
- [...params, parseInt(limit), parseInt(offset)]
- );
-
- // 查询总数
- const [countResult] = await db.execute(
- `SELECT COUNT(*) as total FROM payment_orders ${whereClause}`,
- params
- );
-
- const total = countResult[0].total;
-
- res.json({
- success: true,
- data: {
- orders: orders.map(order => ({
- ...order,
- total_fee: order.total_fee / 100, // 转换为元
- payment_method_name: order.trade_type && order.trade_type.startsWith('ALIPAY') ? '支付宝支付' : '微信支付'
- })),
- pagination: {
- page: parseInt(page),
- limit: parseInt(limit),
- total,
- pages: Math.ceil(total / limit)
- }
- }
- });
- } catch (error) {
- console.error('获取支付记录失败:', error);
- res.status(500).json({
- success: false,
- message: '获取支付记录失败'
- });
- }
-});
-
-/**
- * 检查用户支付状态
- * GET /api/payment/check-status
- */
-router.get('/check-status', auth, async (req, res) => {
- try {
- const userId = req.user.id;
- const db = getDB();
-
- // 查询用户支付状态
- const [users] = await db.execute(
- 'SELECT payment_status FROM users WHERE id = ?',
- [userId]
- );
-
- if (users.length === 0) {
- return res.status(404).json({
- success: false,
- message: '用户不存在'
- });
- }
-
- const paymentStatus = users[0].payment_status;
-
- // 查询最近的支付订单
- const [recentOrders] = await db.execute(
- `SELECT out_trade_no, trade_type, status, total_fee, paid_at
- FROM payment_orders
- WHERE user_id = ?
- ORDER BY created_at DESC
- LIMIT 1`,
- [userId]
- );
-
- res.json({
- success: true,
- data: {
- paymentStatus,
- isPaid: paymentStatus === 'paid',
- recentOrder: recentOrders.length > 0 ? {
- ...recentOrders[0],
- total_fee: recentOrders[0].total_fee / 100,
- payment_method_name: recentOrders[0].trade_type.startsWith('ALIPAY') ? '支付宝支付' : '微信支付'
- } : null
- }
- });
- } catch (error) {
- console.error('检查用户支付状态失败:', error);
- res.status(500).json({
- success: false,
- message: '检查支付状态失败'
- });
- }
-});
-
-/**
- * 支付宝支付回调通知
- * POST /api/payment/alipay/notify
- */
-router.post('/alipay/notify', async (req, res) => {
- try {
- console.log('收到支付宝支付回调:', req.body);
-
- // 验证签名
- const isValid = alipayService.verifyNotifySign(req.body);
- if (!isValid) {
- console.error('支付宝回调签名验证失败');
- return res.status(400).send('FAIL');
- }
-
- const {
- out_trade_no: outTradeNo,
- trade_no: transactionId,
- trade_status: tradeStatus,
- total_amount: totalAmount
- } = req.body;
-
- // 只处理支付成功的回调
- if (tradeStatus === 'TRADE_SUCCESS') {
- const db = getDB();
-
- // 检查订单是否存在
- const [orders] = await db.execute(
- 'SELECT id, user_id, status FROM payment_orders WHERE out_trade_no = ?',
- [outTradeNo]
- );
-
- if (orders.length === 0) {
- console.error('支付宝回调:订单不存在', outTradeNo);
- return res.status(400).send('FAIL');
- }
-
- const order = orders[0];
-
- // 如果订单已经处理过,直接返回成功
- if (order.status === 'paid') {
- console.log('支付宝回调:订单已处理', outTradeNo);
- return res.send('SUCCESS');
- }
-
- // 更新订单状态
- await alipayService.updatePaymentStatus(outTradeNo, {
- status: 'paid',
- transactionId,
- paidAt: new Date()
- });
-
- console.log('支付宝支付成功处理完成:', {
- outTradeNo,
- transactionId,
- userId: order.user_id
- });
- }
-
- res.send('SUCCESS');
- } catch (error) {
- console.error('处理支付宝支付回调失败:', error);
- res.status(500).send('FAIL');
- }
-});
-
-/**
- * 支付宝支付返回页面处理
- * GET /api/payment/alipay/return
- */
-router.get('/alipay/return', async (req, res) => {
- try {
- console.log('支付宝支付返回:', req.query);
-
- // 验证签名
- const isValid = alipayService.verifyNotifySign(req.query);
- if (!isValid) {
- console.error('支付宝返回签名验证失败');
- return res.redirect('/payment/failed');
- }
-
- const { out_trade_no: outTradeNo } = req.query;
-
- // 重定向到支付成功页面
- res.redirect(`/payment/success?outTradeNo=${outTradeNo}`);
- } catch (error) {
- console.error('处理支付宝支付返回失败:', error);
- res.redirect('/payment/failed');
- }
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/points.js b/routes/points.js
deleted file mode 100644
index 99315d3..0000000
--- a/routes/points.js
+++ /dev/null
@@ -1,697 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const { getDB } = require('../database');
-const { auth, adminAuth } = require('../middleware/auth');
-
-/**
- * @swagger
- * tags:
- * name: Points
- * description: 积分管理相关接口
- */
-
-/**
- * @swagger
- * components:
- * schemas:
- * PointsHistory:
- * type: object
- * properties:
- * id:
- * type: integer
- * description: 积分历史记录ID
- * points_change:
- * type: integer
- * description: 积分变动数量
- * type:
- * type: string
- * description: 积分变动类型(earn-获得, spend-消费, admin_adjust-管理员调整)
- * description:
- * type: string
- * description: 积分变动描述
- * created_at:
- * type: string
- * format: date-time
- * description: 创建时间
- */
-
-/**
- * @swagger
- * /api/points/balance:
- * get:
- * summary: 获取用户当前积分余额
- * tags: [Points]
- * security:
- * - bearerAuth: []
- * responses:
- * 200:
- * description: 成功获取积分余额
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: object
- * properties:
- * points:
- * type: integer
- * description: 用户当前积分
- * 401:
- * description: 未授权,需要登录
- * 404:
- * description: 用户不存在
- * 500:
- * description: 服务器错误
- */
-router.get('/balance', 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,
- data: {
- points: users[0].points
- }
- });
- } catch (error) {
- console.error('获取积分余额失败:', error);
- res.status(500).json({ success: false, message: '获取积分余额失败' });
- }
-});
-
-/**
- * @swagger
- * /api/points/history:
- * get:
- * summary: 获取用户积分历史记录
- * tags: [Points]
- * 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: type
- * schema:
- * type: string
- * enum: [earn, spend, admin_adjust]
- * description: 积分变动类型
- * - in: query
- * name: username
- * schema:
- * type: string
- * description: 用户名(仅管理员可用)
- * - in: query
- * name: change
- * schema:
- * type: string
- * enum: [positive, negative]
- * 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
- * example: true
- * data:
- * type: object
- * properties:
- * records:
- * type: array
- * items:
- * $ref: '#/components/schemas/PointsHistory'
- * pagination:
- * type: object
- * properties:
- * page:
- * type: integer
- * limit:
- * type: integer
- * total:
- * type: integer
- * totalPages:
- * type: integer
- * 401:
- * description: 未授权,需要登录
- * 500:
- * description: 服务器错误
- */
-router.get('/history', auth, async (req, res) => {
- try {
- const { page = 1, limit = 10, type, username, change, startDate, endDate } = req.query;
-
- // 确保参数为有效数字
- const pageNum = parseInt(page) || 1;
- const limitNum = parseInt(limit) || 10;
- const offset = (pageNum - 1) * limitNum;
-
- let whereClause = '';
- let queryParams = [];
-
- // 如果是管理员,可以查看所有用户的积分历史
- if (req.user.role === 'admin') {
- whereClause = 'WHERE 1=1';
-
- // 按用户名筛选
- if (username) {
- whereClause += ' AND u.username LIKE ?';
- queryParams.push(`%${username}%`);
- }
-
- // 按类型筛选
- if (type) {
- whereClause += ' AND ph.type = ?';
- queryParams.push(type);
- }
-
- // 按积分变化筛选
- if (change === 'positive') {
- whereClause += ' AND ph.amount > 0';
- } else if (change === 'negative') {
- whereClause += ' AND ph.amount < 0';
- }
-
- // 按时间范围筛选
- if (startDate) {
- whereClause += ' AND DATE(ph.created_at) >= ?';
- queryParams.push(startDate);
- }
- if (endDate) {
- whereClause += ' AND DATE(ph.created_at) <= ?';
- queryParams.push(endDate);
- }
- } else {
- // 普通用户只能查看自己的积分历史
- whereClause = 'WHERE ph.user_id = ?';
- queryParams.push(req.user.id);
-
- if (type && ['earn', 'spend'].includes(type)) {
- whereClause += ' AND ph.type = ?';
- queryParams.push(type);
- }
- }
-
- // 获取总数
- const countQuery = req.user.role === 'admin'
- ? `SELECT COUNT(*) as total FROM points_history ph JOIN users u ON ph.user_id = u.id ${whereClause}`
- : `SELECT COUNT(*) as total FROM points_history ph ${whereClause}`;
-
- const [countResult] = await getDB().execute(countQuery, queryParams);
-
- // 获取历史记录
- const historyQuery = req.user.role === 'admin'
- ? `SELECT ph.id, ph.amount as points, ph.type, ph.description, ph.created_at,
- u.username,
- (SELECT points FROM users WHERE id = ph.user_id) as balance_after
- FROM points_history ph
- JOIN users u ON ph.user_id = u.id
- ${whereClause}
- ORDER BY ph.created_at DESC
- LIMIT ${limitNum} OFFSET ${offset}`
- : `SELECT id, amount as points_change, type, description, created_at
- FROM points_history ph
- ${whereClause}
- ORDER BY created_at DESC
- LIMIT ${limitNum} OFFSET ${offset}`;
-
- const [records] = await getDB().execute(historyQuery, queryParams);
-
- const responseData = req.user.role === 'admin'
- ? {
- history: records,
- total: countResult[0].total
- }
- : {
- records,
- pagination: {
- page: pageNum,
- limit: limitNum,
- total: countResult[0].total,
- totalPages: Math.ceil(countResult[0].total / limitNum)
- }
- };
-
- res.json({
- success: true,
- data: responseData
- });
- } catch (error) {
- console.error('获取积分历史失败:', error);
- res.status(500).json({ success: false, message: '获取积分历史失败' });
- }
-});
-
-/**
- * @swagger
- * /api/points/adjust:
- * post:
- * summary: 管理员调整用户积分
- * tags: [Points]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - userId
- * - points
- * - reason
- * properties:
- * userId:
- * type: integer
- * description: 用户ID
- * points:
- * type: integer
- * description: 调整的积分数量(正数为增加,负数为减少)
- * reason:
- * type: string
- * description: 调整原因
- * responses:
- * 200:
- * description: 积分调整成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 积分调整成功
- * data:
- * type: object
- * properties:
- * userId:
- * type: integer
- * pointsChanged:
- * type: integer
- * newBalance:
- * type: integer
- * 400:
- * description: 参数错误或积分不足
- * 401:
- * description: 未授权,需要管理员权限
- * 404:
- * description: 用户不存在
- * 500:
- * description: 服务器错误
- */
-router.post('/adjust', auth, adminAuth, async (req, res) => {
- const connection = await getDB().getConnection();
-
- try {
- await connection.beginTransaction();
-
- const { userId, points, reason } = req.body;
-
- if (!userId || points === undefined || points === null || !reason) {
- await connection.rollback();
- return res.status(400).json({ success: false, message: '请提供有效的用户ID、积分数量和调整原因' });
- }
-
- // 检查用户是否存在
- const [users] = await connection.execute(
- 'SELECT id, username, points FROM users WHERE id = ?',
- [userId]
- );
-
- if (users.length === 0) {
- await connection.rollback();
- return res.status(404).json({ success: false, message: '用户不存在' });
- }
-
- const currentPoints = users[0].points;
- const newPoints = currentPoints + points;
-
- // 检查积分是否会变为负数
- if (newPoints < 0) {
- await connection.rollback();
- return res.status(400).json({ success: false, message: '用户积分不足,无法扣除' });
- }
-
- // 更新用户积分
- await connection.execute(
- 'UPDATE users SET points = ? WHERE id = ?',
- [newPoints, userId]
- );
-
- // 记录积分历史
- await connection.execute(
- `INSERT INTO points_history (user_id, amount, type, description, created_at)
- VALUES (?, ?, 'admin_adjust', ?, NOW())`,
- [userId, points, reason]
- );
-
- await connection.commit();
-
- res.json({
- success: true,
- message: '积分调整成功',
- data: {
- userId: userId,
- pointsChanged: points,
- newBalance: newPoints
- }
- });
- } catch (error) {
- await connection.rollback();
- console.error('积分调整失败:', error);
- res.status(500).json({ success: false, message: '积分调整失败' });
- } finally {
- connection.release();
- }
-});
-
-/**
- * @swagger
- * /api/points/recharge:
- * post:
- * summary: 管理员给用户充值积分
- * tags: [Points]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - user_id
- * - points
- * properties:
- * user_id:
- * type: integer
- * description: 用户ID
- * points:
- * type: integer
- * description: 充值的积分数量(必须为正数)
- * description:
- * type: string
- * description: 充值描述
- * default: 管理员充值
- * responses:
- * 200:
- * description: 积分充值成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 积分充值成功
- * data:
- * type: object
- * properties:
- * userId:
- * type: integer
- * pointsAdded:
- * type: integer
- * 400:
- * description: 参数错误
- * 401:
- * description: 未授权,需要管理员权限
- * 404:
- * description: 用户不存在
- * 500:
- * description: 服务器错误
- */
-router.post('/recharge', auth, adminAuth, async (req, res) => {
- const connection = await getDB().getConnection();
-
- try {
- await connection.beginTransaction();
-
- const { user_id, points, description = '管理员充值' } = req.body;
-
- if (!user_id || !points || points <= 0) {
- await connection.rollback();
- return res.status(400).json({ success: false, message: '请提供有效的用户ID和积分数量' });
- }
-
- // 检查用户是否存在
- const [users] = await connection.execute(
- 'SELECT id, username FROM users WHERE id = ?',
- [user_id]
- );
-
- if (users.length === 0) {
- await connection.rollback();
- return res.status(404).json({ success: false, message: '用户不存在' });
- }
-
- // 增加用户积分
- await connection.execute(
- 'UPDATE users SET points = points + ? WHERE id = ?',
- [points, user_id]
- );
-
- // 记录积分历史
- await connection.execute(
- `INSERT INTO points_history (user_id, amount, type, description, created_at)
- VALUES (?, ?, 'earn', ?, NOW())`,
- [user_id, points, description]
- );
-
- await connection.commit();
-
- res.json({
- success: true,
- message: '积分充值成功',
- data: {
- userId: user_id,
- pointsAdded: points
- }
- });
- } catch (error) {
- await connection.rollback();
- console.error('积分充值失败:', error);
- res.status(500).json({ success: false, message: '积分充值失败' });
- } finally {
- connection.release();
- }
-});
-
-
-
-/**
- * @swagger
- * /api/points/leaderboard:
- * get:
- * summary: 获取积分排行榜
- * tags: [Points]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: query
- * name: limit
- * schema:
- * type: integer
- * default: 10
- * description: 返回的排行榜数量
- * responses:
- * 200:
- * description: 成功获取积分排行榜
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: object
- * properties:
- * leaderboard:
- * type: array
- * items:
- * type: object
- * properties:
- * rank:
- * type: integer
- * userId:
- * type: integer
- * username:
- * type: string
- * points:
- * type: integer
- * 401:
- * description: 未授权,需要登录
- * 500:
- * description: 服务器错误
- */
-router.get('/leaderboard', auth, async (req, res) => {
- try {
- const { limit = 10 } = req.query;
-
- const [users] = await getDB().execute(
- `SELECT id, username, points
- FROM users
- WHERE points > 0
- ORDER BY points DESC
- LIMIT ?`,
- [parseInt(limit)]
- );
-
- res.json({
- success: true,
- data: {
- leaderboard: users.map((user, index) => ({
- rank: index + 1,
- userId: user.id,
- username: user.username,
- points: user.points
- }))
- }
- });
- } catch (error) {
- console.error('获取积分排行榜失败:', error);
- res.status(500).json({ success: false, message: '获取积分排行榜失败' });
- }
-});
-
-/**
- * @swagger
- * /api/points/stats:
- * get:
- * summary: 获取积分统计信息(管理员权限)
- * tags: [Points]
- * security:
- * - bearerAuth: []
- * responses:
- * 200:
- * description: 成功获取积分统计信息
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: object
- * properties:
- * stats:
- * type: object
- * properties:
- * totalPoints:
- * type: integer
- * description: 系统中总积分数量
- * totalEarned:
- * type: integer
- * description: 总积分发放量
- * totalSpent:
- * type: integer
- * description: 总积分消费量
- * activeUsers:
- * type: integer
- * description: 活跃用户数
- * 401:
- * description: 未授权,需要管理员权限
- * 500:
- * description: 服务器错误
- */
-router.get('/stats', auth, adminAuth, async (req, res) => {
- try {
- // 总积分发放量
- const [totalEarned] = await getDB().execute(
- 'SELECT SUM(amount) as total FROM points_history WHERE type = "earn"'
- );
-
- // 总积分消费量
- const [totalConsumed] = await getDB().execute(
- 'SELECT SUM(ABS(amount)) as total FROM points_history WHERE type = "spend"'
- );
-
- // 本月积分发放
- const [monthEarned] = await getDB().execute(
- 'SELECT SUM(amount) as total FROM points_history WHERE type = "earn" AND YEAR(created_at) = YEAR(NOW()) AND MONTH(created_at) = MONTH(NOW())'
- );
-
- // 上月积分发放(用于计算增长率)
- const [lastMonthEarned] = await getDB().execute(
- 'SELECT SUM(amount) as total FROM points_history WHERE type = "earn" AND YEAR(created_at) = YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AND MONTH(created_at) = MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH))'
- );
-
- // 计算月增长率
- const lastMonthTotal = lastMonthEarned[0].total || 0;
- const currentMonthTotal = monthEarned[0].total || 0;
- let monthGrowthRate = 0;
- if (lastMonthTotal > 0) {
- monthGrowthRate = ((currentMonthTotal - lastMonthTotal) / lastMonthTotal * 100).toFixed(1);
- }
-
- // 活跃用户数(有积分记录的用户)
- const [activeUsers] = await getDB().execute(
- 'SELECT COUNT(DISTINCT user_id) as count FROM points_history'
- );
-
- res.json({
- success: true,
- data: {
- stats: {
- totalPoints: totalEarned[0].total || 0,
- totalEarned: totalEarned[0].total || 0,
- totalSpent: totalConsumed[0].total || 0,
- activeUsers: activeUsers[0].count
- }
- }
- });
- } 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/products.js b/routes/products.js
index 1b6880f..287a3d2 100644
--- a/routes/products.js
+++ b/routes/products.js
@@ -43,7 +43,7 @@ router.get('/', async (req, res) => {
// 获取商品列表
const query = `
- SELECT id, name, shop_name, rongdou_price, category, points_price, stock, sales, image_url as image, description, status, payment_methods, created_at, updated_at
+ SELECT id, name, rongdou_price, category, points_price, stock, image_url as image, description, status, payment_methods, created_at, updated_at
FROM products
${whereClause}
ORDER BY created_at DESC
@@ -130,30 +130,6 @@ router.get('/hot', async (req, res) => {
}
});
-/**
- * @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个商品作为秒杀商品
@@ -193,143 +169,6 @@ router.get('/cheap', async (req, res) => {
res.status(500).json({ success: false, message: '获取秒杀商品失败' });
}
});
-
-/**
- * @swagger
- * /products/{id}:
- * get:
- * summary: 获取单个商品详情(包含增强规格信息)
- * tags: [Products]
- * parameters:
- * - in: path
- * name: id
- * schema:
- * type: integer
- * required: true
- * description: 商品ID
- * responses:
- * 200:
- * description: 成功获取商品详情,包含完整的规格信息
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * 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: 商品不存在
- */
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
@@ -1048,14 +887,6 @@ router.get('/favorites', auth, async (req, res) => {
}
});
-
-
-
-
-
-
-
-
// 获取商品属性
router.get('/:id/attributes', async (req, res) => {
try {
diff --git a/routes/regions.js b/routes/regions.js
deleted file mode 100644
index 6bded1a..0000000
--- a/routes/regions.js
+++ /dev/null
@@ -1,436 +0,0 @@
-const express = require('express')
-const router = express.Router()
-const {getDB} = require('../database')
-
-/**
- * @swagger
- * tags:
- * name: Regions
- * description: 地区数据API
- */
-
-/**
- * @swagger
- * components:
- * schemas:
- * Region:
- * type: object
- * properties:
- * code:
- * type: string
- * description: 地区编码
- * name:
- * type: string
- * description: 地区名称
- * ZhejiangRegion:
- * type: object
- * properties:
- * id:
- * type: integer
- * description: 地区ID
- * city_name:
- * type: string
- * description: 城市名称
- * district_name:
- * type: string
- * description: 区县名称
- * region_code:
- * type: string
- * description: 地区编码
- * is_available:
- * type: integer
- * description: 是否可用(1:可用 0:不可用)
- */
-
-/**
- * @swagger
- * /regions/zhejiang:
- * get:
- * summary: 获取浙江省所有地区数据
- * tags: [Regions]
- * responses:
- * 200:
- * description: 成功获取浙江省地区数据
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: array
- * items:
- * $ref: '#/components/schemas/ZhejiangRegion'
- * message:
- * type: string
- * example: 获取地区数据成功
- * 500:
- * description: 服务器错误
- */
-router.get('/zhejiang', async (req, res) => {
- try {
- const query = `
- SELECT id, city_name, district_name, region_code, is_available
- FROM zhejiang_regions
- WHERE is_available = 1
- ORDER BY city_name, district_name
- `
-
- const [rows] = await getDB().execute(query)
-
- res.json({
- success: true,
- data: rows,
- message: '获取地区数据成功'
- })
- } catch (error) {
- console.error('获取浙江省地区数据失败:', error)
- res.status(500).json({
- success: false,
- message: '获取地区数据失败'
- })
- }
-})
-
-/**
- * @swagger
- * /regions/provinces:
- * get:
- * summary: 获取所有省份
- * tags: [Regions]
- * responses:
- * 200:
- * description: 成功获取省份列表
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: array
- * items:
- * $ref: '#/components/schemas/Region'
- * 500:
- * description: 服务器错误
- */
-router.get('/provinces', async (req, res) => {
- try {
- // 按level分组数据
- const regionsByLevel = {
- 1: [], // 省份
- 2: [], // 城市
- 3: [] // 区县
- };
- if (!global.provinces) {
- // 一次性获取所有区域数据(省、市、区县)
- const [allRegions] = await getDB().execute(
- `SELECT code, name as label, level, parent_code
- FROM china_regions
- WHERE level <= 3
- ORDER BY level, code`
- );
-
-
- // 创建code到region的映射,便于快速查找
- const regionMap = {};
-
- // 分组并建立映射
- allRegions.forEach(region => {
- region.children = []; // 初始化children数组
- regionsByLevel[region.level].push(region);
- regionMap[region.code] = region;
- });
-
- // 构建层级关系:先处理区县到城市的关系
- regionsByLevel[3].forEach(district => {
- const parentCity = regionMap[district.parent_code];
- if (parentCity) {
- parentCity.children.push(district);
- }
- });
-
- // 再处理城市到省份的关系
- regionsByLevel[2].forEach(city => {
- const parentProvince = regionMap[city.parent_code];
- if (parentProvince) {
- parentProvince.children.push(city);
- }
- });
- global.provinces = regionsByLevel[1];
- }else {
- console.log('1111')
- regionsByLevel[1] = global.provinces;
- }
-
-
- // 返回省份数据(已包含完整的层级结构)
- res.json({
- success: true,
- data: regionsByLevel[1]
- });
- } catch (error) {
- console.error('获取省份列表错误:', error);
- res.status(500).json({message: '获取省份列表失败'});
- }
-});
-
-/**
- * @swagger
- * /regions/cities/{provinceCode}:
- * get:
- * summary: 根据省份代码获取城市列表
- * tags: [Regions]
- * parameters:
- * - in: path
- * name: provinceCode
- * required: true
- * schema:
- * type: string
- * description: 省份代码
- * responses:
- * 200:
- * description: 成功获取城市列表
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: array
- * items:
- * $ref: '#/components/schemas/Region'
- * 500:
- * description: 服务器错误
- */
-router.get('/cities/:provinceCode', async (req, res) => {
- try {
- const provinceCode = req.params.provinceCode;
-
- const [cities] = await getDB().execute(
- `SELECT code, name
- FROM china_regions
- WHERE level = 2
- AND parent_code = ?
- ORDER BY code`,
- [provinceCode]
- );
-
- res.json({
- success: true,
- data: cities
- });
- } catch (error) {
- console.error('获取城市列表错误:', error);
- res.status(500).json({message: '获取城市列表失败'});
- }
-});
-
-/**
- * @swagger
- * /regions/districts/{cityCode}:
- * get:
- * summary: 根据城市代码获取区县列表
- * tags: [Regions]
- * parameters:
- * - in: path
- * name: cityCode
- * required: true
- * schema:
- * type: string
- * description: 城市代码
- * responses:
- * 200:
- * description: 成功获取区县列表
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: array
- * items:
- * $ref: '#/components/schemas/Region'
- * 500:
- * description: 服务器错误
- */
-router.get('/districts/:cityCode', async (req, res) => {
- try {
- const cityCode = req.params.cityCode;
-
- const [districts] = await getDB().execute(
- `SELECT code, name
- FROM china_regions
- WHERE level = 3
- AND parent_code = ?
- ORDER BY code`,
- [cityCode]
- );
-
- res.json({
- success: true,
- data: districts
- });
- } catch (error) {
- console.error('获取区县列表错误:', error);
- res.status(500).json({message: '获取区县列表失败'});
- }
-});
-
-/**
- * @swagger
- * /regions/path/{regionCode}:
- * get:
- * summary: 根据区域代码获取完整路径(省-市-区)
- * tags: [Regions]
- * parameters:
- * - in: path
- * name: regionCode
- * required: true
- * schema:
- * type: string
- * description: 区域代码
- * responses:
- * 200:
- * description: 成功获取区域完整路径
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * data:
- * type: object
- * properties:
- * province:
- * $ref: '#/components/schemas/Region'
- * city:
- * $ref: '#/components/schemas/Region'
- * district:
- * $ref: '#/components/schemas/Region'
- * 404:
- * description: 区域不存在
- * 500:
- * description: 服务器错误
- */
-router.get('/path/:regionCode', async (req, res) => {
- try {
- const regionCode = req.params.regionCode;
-
- // 获取当前区域信息
- const [currentRegion] = await getDB().execute(
- 'SELECT code, name, level, parent_code FROM china_regions WHERE code = ?',
- [regionCode]
- );
-
- if (currentRegion.length === 0) {
- return res.status(404).json({message: '区域不存在'});
- }
-
- const region = currentRegion[0];
- const path = [region];
-
- // 递归获取父级区域
- let parentCode = region.parent_code;
- while (parentCode) {
- const [parentRegion] = await getDB().execute(
- 'SELECT code, name, level, parent_code FROM china_regions WHERE code = ? AND status = "active"',
- [parentCode]
- );
-
- if (parentRegion.length > 0) {
- path.unshift(parentRegion[0]);
- parentCode = parentRegion[0].parent_code;
- } else {
- break;
- }
- }
-
- res.json({
- success: true,
- data: {
- path,
- province: path.find(r => r.level === 1) || null,
- city: path.find(r => r.level === 2) || null,
- district: path.find(r => r.level === 3) || null
- }
- });
- } catch (error) {
- console.error('获取区域路径错误:', error);
- res.status(500).json({message: '获取区域路径失败'});
- }
-});
-
-// 搜索区域(支持模糊搜索)
-router.get('/search', async (req, res) => {
- try {
- const {keyword, level} = req.query;
-
- if (!keyword || keyword.trim() === '') {
- return res.status(400).json({message: '搜索关键词不能为空'});
- }
-
- let sql = `SELECT code, name, level, parent_code
- FROM china_regions
- WHERE name LIKE ?`;
- const params = [`%${keyword.trim()}%`];
-
- if (level) {
- sql += ' AND level = ?';
- params.push(parseInt(level));
- }
-
- sql += ' ORDER BY level, code LIMIT 50';
-
- const [regions] = await getDB().execute(sql, params);
-
- // 为每个搜索结果获取完整路径
- const results = [];
- for (const region of regions) {
- const path = [region];
- let parentCode = region.parent_code;
-
- while (parentCode) {
- const [parentRegion] = await getDB().execute(
- 'SELECT code, name, level, parent_code FROM china_regions WHERE code = ? AND status = "active"',
- [parentCode]
- );
-
- if (parentRegion.length > 0) {
- path.unshift(parentRegion[0]);
- parentCode = parentRegion[0].parent_code;
- } else {
- break;
- }
- }
-
- results.push({
- ...region,
- path,
- fullName: path.map(r => r.name).join(' - ')
- });
- }
-
- res.json({
- success: true,
- data: results
- });
- } catch (error) {
- console.error('搜索区域错误:', error);
- res.status(500).json({message: '搜索区域失败'});
- }
-});
-
-module.exports = router
\ No newline at end of file
diff --git a/routes/riskManagement.js b/routes/riskManagement.js
deleted file mode 100644
index 967ecb6..0000000
--- a/routes/riskManagement.js
+++ /dev/null
@@ -1,440 +0,0 @@
-const express = require('express');
-router = express.Router();
-
-/**
- * @swagger
- * tags:
- * name: RiskManagement
- * description: 风险管理API
- */
-const { auth } = require('../middleware/auth');
-const timeoutService = require('../services/timeoutService');
-const { getDB } = require('../database');
-
-/**
- * 检查管理员权限
- */
-const requireAdmin = (req, res, next) => {
- if (req.user.role !== 'admin') {
- return res.status(403).json({ success: false, message: '需要管理员权限' });
- }
- next();
-};
-
-/**
- * @swagger
- * /risk-management/users:
- * get:
- * summary: 获取风险用户列表
- * tags: [RiskManagement]
- * 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: is_blacklisted
- * schema:
- * type: integer
- * enum: [0, 1]
- * description: 是否被拉黑
- * - in: query
- * name: username
- * schema:
- * type: string
- * description: 用户名
- * responses:
- * 200:
- * description: 成功获取风险用户列表
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * data:
- * type: object
- * properties:
- * users:
- * type: array
- * items:
- * type: object
- * properties:
- * id:
- * type: integer
- * username:
- * type: string
- * real_name:
- * type: string
- * is_blacklisted:
- * type: boolean
- * blacklist_reason:
- * type: string
- * blacklisted_at:
- * type: string
- * format: date-time
- * pagination:
- * type: object
- * properties:
- * total:
- * type: integer
- * page:
- * type: integer
- * limit:
- * type: integer
- * pages:
- * type: integer
- * 401:
- * description: 未授权
- * 403:
- * description: 权限不足
- * 500:
- * description: 服务器错误
- */
-router.get('/users', auth, requireAdmin, async (req, res) => {
- try {
- const { page = 1, limit = 10, is_blacklisted, username } = req.query;
-
- const filters = {};
- if (is_blacklisted !== undefined) {
- filters.is_blacklisted = parseInt(is_blacklisted);
- }
- if (username) {
- filters.username = username;
- }
-
- const result = await timeoutService.getRiskUsers(filters, { page, limit });
-
- res.json({
- success: true,
- data: result
- });
- } catch (error) {
- console.error('获取风险用户列表失败:', error);
- res.status(500).json({ success: false, message: '获取风险用户列表失败' });
- }
-});
-
-/**
- * @swagger
- * /risk-management/blacklist/{userId}:
- * post:
- * summary: 拉黑用户
- * tags: [RiskManagement]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: userId
- * schema:
- * type: integer
- * required: true
- * description: 用户ID
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - reason
- * properties:
- * reason:
- * type: string
- * description: 拉黑原因
- * responses:
- * 200:
- * description: 用户已被拉黑
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 用户已被拉黑
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 403:
- * description: 权限不足
- * 500:
- * description: 服务器错误
- */
-router.post('/blacklist/:userId', auth, requireAdmin, async (req, res) => {
- try {
- const { userId } = req.params;
- const { reason } = req.body;
- const operatorId = req.user.id;
-
- if (!reason || reason.trim() === '') {
- return res.status(400).json({ success: false, message: '请提供拉黑原因' });
- }
-
- await timeoutService.blacklistUser(parseInt(userId), reason.trim(), operatorId);
-
- res.json({
- success: true,
- message: '用户已被拉黑'
- });
- } catch (error) {
- console.error('拉黑用户失败:', error);
- res.status(500).json({ success: false, message: error.message || '拉黑用户失败' });
- }
-});
-
-/**
- * @swagger
- * /risk-management/unblacklist/{userId}:
- * post:
- * summary: 解除拉黑
- * tags: [RiskManagement]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: path
- * name: userId
- * schema:
- * type: integer
- * required: true
- * description: 用户ID
- * responses:
- * 200:
- * description: 已解除拉黑
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 已解除拉黑
- * 401:
- * description: 未授权
- * 403:
- * description: 权限不足
- * 500:
- * description: 服务器错误
- */
-router.post('/unblacklist/:userId', auth, requireAdmin, async (req, res) => {
- try {
- const { userId } = req.params;
- const operatorId = req.user.id;
-
- await timeoutService.unblacklistUser(parseInt(userId), operatorId);
-
- res.json({
- success: true,
- message: '已解除拉黑'
- });
- } catch (error) {
- console.error('解除拉黑失败:', error);
- res.status(500).json({ success: false, message: error.message || '解除拉黑失败' });
- }
-});
-
-/**
- * @swagger
- * /risk-management/overdue-transfers:
- * get:
- * summary: 获取超时转账列表
- * tags: [RiskManagement]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: query
- * name: page
- * schema:
- * type: integer
- * default: 1
- * description: 页码
- * - 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:
- * transfers:
- * type: array
- * items:
- * type: object
- * properties:
- * id:
- * type: integer
- * user_id:
- * type: integer
- * recipient_id:
- * type: integer
- * amount:
- * type: number
- * status:
- * type: string
- * created_at:
- * type: string
- * format: date-time
- * username:
- * type: string
- * recipient_name:
- * type: string
- * overdue_hours:
- * type: number
- * pagination:
- * type: object
- * properties:
- * total:
- * type: integer
- * page:
- * type: integer
- * limit:
- * type: integer
- * pages:
- * type: integer
- * 401:
- * description: 未授权
- * 403:
- * description: 权限不足
- * 500:
- * description: 服务器错误
- */
-router.get('/overdue-transfers', auth, requireAdmin, async (req, res) => {
- try {
- const { page = 1, limit = 10 } = req.query;
- const pageNum = parseInt(page, 10) || 1;
- const limitNum = parseInt(limit, 10) || 10;
- const offset = (pageNum - 1) * limitNum;
-
- const db = getDB();
-
- // 获取总数
- const [countResult] = await db.execute(
- 'SELECT COUNT(*) as total FROM transfers WHERE is_overdue = 1'
- );
- const total = countResult[0].total;
-
- // 获取数据
- const [transfers] = await db.execute(
- `SELECT t.*,
- fu.username as from_username, fu.real_name as from_real_name,
- tu.username as to_username, tu.real_name as to_real_name
- FROM transfers t
- LEFT JOIN users fu ON t.from_user_id = fu.id
- LEFT JOIN users tu ON t.to_user_id = tu.id
- WHERE t.is_overdue = 1
- ORDER BY t.overdue_at DESC
- LIMIT ${limitNum} OFFSET ${offset}`
- );
-
- res.json({
- success: true,
- data: {
- transfers,
- pagination: {
- page: pageNum,
- limit: limitNum,
- total,
- pages: Math.ceil(total / limitNum)
- }
- }
- });
- } catch (error) {
- console.error('获取超时转账列表失败:', error);
- res.status(500).json({ success: false, message: '获取超时转账列表失败' });
- }
-});
-
-/**
- * 手动检查转账超时
- */
-router.post('/check-timeouts', auth, requireAdmin, async (req, res) => {
- try {
- await timeoutService.checkTransferTimeouts();
-
- res.json({
- success: true,
- message: '转账超时检查已完成'
- });
- } catch (error) {
- console.error('手动检查转账超时失败:', error);
- res.status(500).json({ success: false, message: '检查转账超时失败' });
- }
-});
-
-/**
- * 获取风险管理统计信息
- */
-router.get('/stats', auth, requireAdmin, async (req, res) => {
- try {
- const db = getDB();
-
- // 获取统计数据
- const [stats] = await db.execute(
- `SELECT
- COUNT(CASE WHEN is_risk_user = 1 THEN 1 END) as risk_users_count,
- COUNT(CASE WHEN is_blacklisted = 1 THEN 1 END) as blacklisted_users_count,
- COUNT(CASE WHEN is_risk_user = 1 AND is_blacklisted = 0 THEN 1 END) as risk_not_blacklisted_count
- FROM users`
- );
-
- const [overdueStats] = await db.execute(
- `SELECT
- COUNT(*) as overdue_transfers_count,
- SUM(amount) as overdue_amount_total
- FROM transfers
- WHERE is_overdue = 1`
- );
-
- const [todayOverdue] = await db.execute(
- `SELECT COUNT(*) as today_overdue_count
- FROM transfers
- WHERE is_overdue = 1 AND DATE(overdue_at) = CURDATE()`
- );
-
- res.json({
- success: true,
- data: {
- riskUsersCount: stats[0].risk_users_count,
- blacklistedUsersCount: stats[0].blacklisted_users_count,
- riskNotBlacklistedCount: stats[0].risk_not_blacklisted_count,
- overdueTransfersCount: overdueStats[0].overdue_transfers_count,
- overdueAmountTotal: overdueStats[0].overdue_amount_total || 0,
- todayOverdueCount: todayOverdue[0].today_overdue_count
- }
- });
- } 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/sms.js b/routes/sms.js
deleted file mode 100644
index 787bd56..0000000
--- a/routes/sms.js
+++ /dev/null
@@ -1,340 +0,0 @@
-const express = require('express')
-const router = express.Router()
-const { getDB } = require('../database')
-const Dysmsapi20170525 = require('@alicloud/dysmsapi20170525')
-const OpenApi = require('@alicloud/openapi-client')
-const { Config } = require('@alicloud/openapi-client')
-
-/**
- * @swagger
- * tags:
- * name: SMS
- * description: 短信验证码相关接口
- */
-
-/**
- * @swagger
- * components:
- * schemas:
- * SMSVerification:
- * type: object
- * properties:
- * phone:
- * type: string
- * description: 手机号码
- * code:
- * type: string
- * description: 验证码
- */
-
-// 阿里云短信配置
-const config = new Config({
- // 您的AccessKey ID
- accessKeyId: process.env.ALIYUN_ACCESS_KEY_ID || 'your_access_key_id',
- // 您的AccessKey Secret
- accessKeySecret: process.env.ALIYUN_ACCESS_KEY_SECRET || 'your_access_key_secret',
- // 访问的域名
- endpoint: 'dysmsapi.aliyuncs.com'
-})
-
-// 创建短信客户端
-const client = new Dysmsapi20170525.default(config)
-
-// 短信模板配置
-const SMS_CONFIG = {
- signName: process.env.ALIYUN_SMS_SIGN_NAME || '您的签名', // 短信签名
- templateCode: process.env.ALIYUN_SMS_TEMPLATE_CODE || 'SMS_XXXXXX', // 短信模板CODE
- // 开发环境标识
- isDevelopment: process.env.NODE_ENV !== 'production'
-}
-
-// 存储验证码的内存对象(生产环境建议使用Redis)
-const smsCodeStore = new Map()
-
-// 验证码有效期(5分钟)
-const CODE_EXPIRE_TIME = 5 * 60 * 1000
-// 最大尝试次数
-const MAX_ATTEMPTS = 3
-// 发送频率限制(60秒)
-const SEND_INTERVAL = 60 * 1000
-
-/**
- * 生成6位数字验证码
- * @returns {string} 验证码
- */
-function generateSMSCode() {
- return Math.floor(100000 + Math.random() * 900000).toString();
-}
-
-/**
- * @swagger
- * /api/sms/send:
- * post:
- * summary: 发送短信验证码
- * tags: [SMS]
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - phone
- * properties:
- * phone:
- * type: string
- * description: 手机号码
- * responses:
- * 200:
- * description: 验证码发送成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 验证码发送成功
- * 400:
- * description: 参数错误或发送频率限制
- * 500:
- * description: 服务器错误
- */
-router.post('/send', async (req, res) => {
- try {
- const { phone } = req.body
- console.log(phone)
- // 验证手机号格式
- const phoneRegex = /^1[3-9]\d{9}$/
- if (!phoneRegex.test(phone)) {
- return res.json({
- success: false,
- message: '手机号格式不正确'
- })
- }
-
- // 检查发送频率限制
- const lastSendTime = smsCodeStore.get(`last_send_${phone}`)
- if (lastSendTime && Date.now() - lastSendTime < SEND_INTERVAL) {
- const remainingTime = Math.ceil((SEND_INTERVAL - (Date.now() - lastSendTime)) / 1000)
- return res.json({
- success: false,
- message: `请等待${remainingTime}秒后再发送`
- })
- }
-
- // 生成6位数字验证码
- const code = Math.random().toString().slice(-6)
-
- // 存储验证码信息
- smsCodeStore.set(phone, {
- code,
- timestamp: Date.now(),
- attempts: 0
- })
-
- // 记录发送时间
- smsCodeStore.set(`last_send_${phone}`, Date.now())
- // 生产环境发送真实短信
- try {
- console.log(code);
- res.json({
- success: true,
- message: '验证码发送成功'
- })
- return
- const sendSmsRequest = new Dysmsapi20170525.SendSmsRequest({
- phoneNumbers: phone,
- signName: SMS_CONFIG.signName,
- templateCode: SMS_CONFIG.templateCode,
- templateParam: JSON.stringify({ code })
- })
-
- const response = await client.sendSms(sendSmsRequest)
- console.log(response.body);
-
- if (response.body.code === 'OK') {
- res.json({
- success: true,
- message: '验证码发送成功'
- })
- } else {
- console.error('阿里云短信发送失败:', response.body)
- res.json({
- success: false,
- message: '发送失败,请稍后重试'
- })
- }
- } catch (smsError) {
- console.error('阿里云短信API调用失败:', smsError)
- res.json({
- success: false,
- message: '发送失败,请稍后重试'
- })
- }
-
- } catch (error) {
- console.error('发送短信验证码失败:', error)
- res.status(500).json({
- success: false,
- message: '发送失败,请稍后重试'
- })
- }
-});
-
-/**
- * @swagger
- * /api/sms/verify:
- * post:
- * summary: 验证短信验证码
- * tags: [SMS]
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * type: object
- * required:
- * - phone
- * - code
- * properties:
- * phone:
- * type: string
- * description: 手机号码
- * code:
- * type: string
- * description: 验证码
- * responses:
- * 200:
- * description: 验证码验证成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 手机号验证成功
- * data:
- * type: object
- * properties:
- * phone:
- * type: string
- * verified:
- * type: boolean
- * 400:
- * description: 参数错误或验证码错误
- * 500:
- * description: 服务器错误
- */
-router.post('/verify', async (req, res) => {
- try {
- const { phone, code } = req.body;
-
- if (!phone || !code) {
- return res.status(400).json({ success: false, message: '手机号和验证码不能为空' });
- }
-
- const storedData = smsCodeStore.get(phone);
-
- if (!storedData) {
- return res.status(400).json({ success: false, message: '验证码不存在或已过期' });
- }
-
- // 检查验证码是否过期(5分钟)
- if (Date.now() - storedData.timestamp > 300000) {
- smsCodeStore.delete(phone);
- return res.status(400).json({ success: false, message: '验证码已过期' });
- }
-
- // 检查尝试次数(最多3次)
- if (storedData.attempts >= 3) {
- smsCodeStore.delete(phone);
- return res.status(400).json({ success: false, message: '验证码错误次数过多,请重新获取' });
- }
-
- // 验证验证码
- if (storedData.code !== code) {
- storedData.attempts++;
- smsCodeStore.set(phone, storedData);
- return res.status(400).json({
- success: false,
- message: `验证码错误,还可尝试${3 - storedData.attempts}次`
- });
- }
-
- // 验证成功,删除验证码
- smsCodeStore.delete(phone);
- smsCodeStore.delete(`time_${phone}`);
-
- res.json({
- success: true,
- message: '手机号验证成功',
- data: {
- phone: phone,
- verified: true
- }
- });
-
- } catch (error) {
- console.error('验证短信验证码错误:', error);
- res.status(500).json({ success: false, message: '验证失败' });
- }
-});
-
-/**
- * 导出验证手机号的函数供其他模块使用
- * @param {string} phone 手机号
- * @param {string} code 验证码
- * @returns {boolean} 验证结果
- */
-function verifySMSCode(phone, code) {
- const storedData = smsCodeStore.get(phone);
-
- if (!storedData) {
- return false;
- }
-
- // 检查是否过期
- if (Date.now() - storedData.timestamp > 300000) {
- smsCodeStore.delete(phone);
- return false;
- }
-
- // 检查尝试次数
- if (storedData.attempts >= 3) {
- smsCodeStore.delete(phone);
- return false;
- }
-
- // 验证验证码
- if (storedData.code === code) {
- smsCodeStore.delete(phone);
- smsCodeStore.delete(`time_${phone}`);
- return true;
- }
-
- return false;
-}
-
-// 清理过期验证码的定时任务
-setInterval(() => {
- const now = Date.now();
- for (const [key, value] of smsCodeStore.entries()) {
- if (key.startsWith('time_')) continue;
-
- if (value.timestamp && now - value.timestamp > 300000) {
- smsCodeStore.delete(key);
- smsCodeStore.delete(`time_${key}`);
- }
- }
-}, 60000); // 每分钟清理一次
-
-module.exports = router;
-module.exports.verifySMSCode = verifySMSCode;
\ No newline at end of file
diff --git a/routes/specifications.js b/routes/specifications.js
index 3563d10..4304168 100644
--- a/routes/specifications.js
+++ b/routes/specifications.js
@@ -1,1096 +1,654 @@
const express = require('express');
const router = express.Router();
-const { getDB } = require('../database');
-const { auth, adminAuth } = require('../middleware/auth');
+const {getDB} = require('../database');
+const {auth, adminAuth} = require('../middleware/auth');
+const {SelectBuilder} = require('../config/dbv2')
+const {db} = require("../server");
+const sql = require("../config/config");
-/**
- * @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;
+router.get('/names', auth, async (req, res) => {
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));
+ const {status = 'active'} = req.query;
+ const {id: created_id} = req.user;
+ let query = `SELECT *
+ FROM spec_names
+ WHERE created_id = ${created_id}`;
+ const params = [];
+
+ if (status) {
+ query += ' and status = ?';
+ params.push(status);
}
- } 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 : [];
- }
+
+ query += ' ORDER BY sort_order, id';
+
+ const [specNames] = await getDB().execute(query, params);
+
+ res.json({
+ success: true,
+ data: specNames
+ });
} catch (error) {
- console.error('解析规格值失败:', combination.spec_values, error);
- specValueIds = [];
+ console.error('获取规格名称失败:', error);
+ res.status(500).json({success: false, message: '获取规格名称失败'});
}
-
- 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
+router.post('/names', auth, adminAuth, async (req, res) => {
+ const db = getDB();
+ try {
+ const {name, display_name, sort_order = 0} = req.body;
+ const {id: created_id} = req.user;
+ if (!name || !display_name) {
+ return res.status(400).json({success: false, message: '规格名称和显示名称不能为空'});
+ }
+ let spec_name = new SelectBuilder().from()
+ const [result] = await getDB().execute(
+ 'INSERT INTO spec_names (name, display_name, sort_order,created_id) VALUES (?, ?, ?,?)',
+ [name, display_name, sort_order,created_id]
);
- 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: {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: '创建规格名称失败'});
+ }
+});
+
+
+router.delete('/names/:id', auth, adminAuth, async (req, res) => {
+ try {
+ const {id} = req.params;
+ const {id:user_id,role} = req.user
+
+ let specCountQuery = new SelectBuilder()
+ .from('spec_names')
+ .select('COUNT(*) as total')
+ .where('id=?',id)
+ if(role !== 'admin'){
+ specCountQuery.where('created_id=?',user_id)
+ }
+ let [spec] = await specCountQuery.execute(db)
+ if (spec.total === 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: '删除规格名称失败'});
+ }
+});
+
+
+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: '获取规格值失败'});
+ }
+});
+
+
+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: '创建规格值失败'});
+ }
+});
+
+
+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: '获取规格组合失败'});
+ }
+});
+
+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: '获取规格组合详情失败'});
+ }
+});
+
+
+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: '删除规格组合失败'});
+ }
+});
+
+
+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: '更新规格组合失败'});
+ }
+});
+
+
+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: '创建规格组合失败'});
+ }
+});
+
+
+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: '生成规格组合失败'});
}
-
- 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/supplier.js b/routes/supplier.js
new file mode 100644
index 0000000..16c345d
--- /dev/null
+++ b/routes/supplier.js
@@ -0,0 +1,255 @@
+//供应商接口
+const express = require('express');
+const {getDB} = require('../database');
+const {auth, adminAuth} = require('../middleware/auth');
+const router = express.Router();
+const {SelectBuilder, InsertBuilder, UpdateBuilder} = require('../config/dbv2')
+const bcrypt = require('bcryptjs');
+
+
+/*
+* username 账号
+* password 密码
+* avatar 头像
+* real_name 姓名
+* id_card 身份证号
+* wechat_qr 微信二维码
+* alipay_qr 支付宝收款码
+* bank_card 银行卡号
+* unionpay_qr 云闪付收款码
+* phone 手机号
+*
+* */
+router.post('/add', auth, adminAuth, async (req, res) => {
+ const db = getDB();
+ try {
+ const {
+ username,
+ password,
+ avatar,
+ real_name,
+ id_card,
+ wechat_qr,
+ alipay_qr,
+ bank_card,
+ unionpay_qr,
+ phone
+ } = req.body;
+ console.log('12333333')
+ // 验证手机号格式
+ const phoneRegex = /^1[3-9]\d{9}$/;
+ if (!phoneRegex.test(phone)) {
+ return res.status(400).json({success: false, message: '手机号格式不正确'});
+ }
+ let userCountQuery = new SelectBuilder()
+ .from('users')
+ .select('COUNT(*) as total')
+ .where(`(username=? or phone=?)`, username, phone)
+ .where('is_delete=?', false)
+ let [user] = await userCountQuery.execute(db);
+ if (user.total > 0) {
+ res.status(400).send({
+ success: false, message: '手机号或者用户名重复'
+ })
+ }
+ const hashedPassword = await bcrypt.hash(password, 10);
+ let insetObj = {
+ username,
+ password: hashedPassword,
+ real_name,
+ id_card,
+ wechat_qr,
+ alipay_qr,
+ bank_card,
+ unionpay_qr,
+ avatar,
+ phone,
+ user_type: 'supplier',
+ audit_status: 'approved'
+ }
+ console.log(insetObj, '111')
+ await db.query('START TRANSACTION');
+ let sqlResult = new InsertBuilder()
+ .into('users')
+ .values(insetObj)
+ let result = await sqlResult.execute(db);
+ console.log(result)
+ await db.query('COMMIT');
+ if (result.affectedRows > 0) {
+ res.json({success: true, message: '创建成功'})
+ } else {
+ res.json({success: false, message: '系统错误请联系管理员'})
+ await db.query('ROLLBACK');
+ }
+
+ } catch (err) {
+ console.log(err)
+ await db.query('ROLLBACK');
+ }
+
+})
+/*
+* 修改供应商
+* username 账号
+* password 密码
+* avatar 头像
+* real_name 姓名
+* id_card 身份证号
+* wechat_qr 微信二维码
+* alipay_qr 支付宝收款码
+* bank_card 银行卡号
+* unionpay_qr 云闪付收款码
+* phone 手机号
+* id 供应商id
+* audit_status 审核状态 'pending','approved','rejected'
+* */
+router.put('/edit', auth, adminAuth, async (req, res) => {
+ const db = getDB();
+ try {
+ const {
+ username,
+ password,
+ id,
+ avatar,
+ real_name,
+ id_card,
+ wechat_qr,
+ bank_card,
+ unionpay_qr,
+ phone,
+ audit_status
+ } = req.body;
+ if (!id) {
+ return res.status(400).send({
+ success: false,
+ message: '供应商id未填写'
+ })
+ }
+ let userCountQuery = new SelectBuilder()
+ .from('users')
+ .select('COUNT(*) as total')
+ .where(`(username=? or phone=?)`, username, phone)
+ .where('id!=?', id)
+ .where('is_delete=?', false)
+ let userInfo = await userCountQuery.execute(db);
+ if (userInfo.total > 0) {
+ res.status(400).send({
+ success: false,
+ message: '手机号或用户名重复请重新填写'
+ })
+ }
+ // 动态组装更新对象
+
+ let upUser = new UpdateBuilder()
+ .update('users')
+ .where('id=?', id)
+ const fields = [
+ 'username',
+ 'avatar',
+ 'real_name',
+ 'id_card',
+ 'wechat_qr',
+ 'bank_card',
+ 'unionpay_qr',
+ 'phone',
+ 'audit_status'
+ ];
+ // 遍历允许的字段,存在才 set
+ for (const field of fields) {
+ if (req.body[field]) {
+ upUser.set(field, req.body[field]);
+ }
+ }
+
+ // 密码单独处理(异步 hash)
+ if (password) {
+ upUser.set('password', await bcrypt.hash(password, 10));
+ }
+ await upUser.execute(db);
+ res.json({success: true, message: '更新成功'});
+ } catch (e) {
+ console.log(e)
+ res.status(500).send({
+ success: false,
+ message: '系统错误请联系管理员'
+ })
+ }
+
+})
+/*
+* 供应商详情
+* id 供应商id
+* */
+router.get('/details', async (req, res) => {
+ const {id} = req.query;
+ const db = getDB();
+ try {
+ if (!id) {
+ return res.status(400).send({
+ success: false,
+ message: '供应商id不能为空'
+ })
+ }
+ let [user] = await new SelectBuilder()
+ .from('users')
+ .where('id=?', id)
+ .where('user_type=?', 'supplier')
+ .where('is_delete=?', false)
+ .execute(db)
+ if (user) {
+ delete user.password
+ } else {
+ return res.status(400).json({success: false, message: '无此用户'})
+ }
+
+ res.json({data: user, success: true, message: '查询成功'})
+ } catch (err) {
+ return res.status(500).json({success: false, message: '系统错误,请联系管理员'})
+ }
+
+})
+/*
+* 供应商列表
+* page 1 页数
+* limit 20 每页数量
+* name 手机号,账号
+* */
+router.get('/list', async (req, res) => {
+ const db = getDB();
+ try {
+ let {page = 1, limit = 20, name} = req.query;
+ console.log(page, limit, name);
+ let userCountQuery = new SelectBuilder()
+ .from('users')
+ .where('user_type=?', 'supplier')
+ .where('is_delete=?', false)
+ if (name) {
+ userCountQuery.where('(username LIKE ? or phone LIKE ?)', name, name)
+ }
+ const data = await userCountQuery.paginateWithCount(db, page, limit);
+ return res.json(data)
+ } catch (err) {
+ console.log(err)
+ return res.status(500).json({success: false, message: err})
+ }
+})
+/*
+* 删除供应商
+* id
+* */
+router.delete('/delete', auth, adminAuth, async (req, res) => {
+ const db = getDB();
+ try {
+ const {id} = req.query;
+ await new UpdateBuilder()
+ .update('users')
+ .set('is_delete', true)
+ .where('id=?', id)
+ .execute(db)
+ res.json({success: true, message: '删除成功'})
+ } catch (err) {
+ res.status(500).send({error: err})
+ }
+
+})
+module.exports = router;
diff --git a/routes/system.js b/routes/system.js
deleted file mode 100644
index ffdaea0..0000000
--- a/routes/system.js
+++ /dev/null
@@ -1,321 +0,0 @@
-const express = require('express');
-const { auth } = require('../middleware/auth');
-const { validateQuery, validate } = require('../middleware/validation');
-const { logger } = require('../config/logger');
-const { HTTP_STATUS } = require('../config/constants');
-const { getDB } = require('../database');
-const Joi = require('joi');
-
-const router = express.Router();
-
-/**
- * 系统设置验证规则
- */
-const systemSchemas = {
- // 更新系统设置
- updateSettings: Joi.object({
- basic: Joi.object({
- siteName: Joi.string().max(100).allow(''),
- siteDescription: Joi.string().max(500).allow(''),
- siteKeywords: Joi.string().max(200).allow(''),
- siteLogo: Joi.string().allow(''),
- siteFavicon: Joi.string().allow(''),
-
- icp: Joi.string().max(100).allow('')
- }).optional(),
- features: Joi.object({
- allowRegister: Joi.boolean(),
- allowTransfer: Joi.boolean(),
- allowExchange: Joi.boolean(),
- allowReview: Joi.boolean(),
- allowComment: Joi.boolean()
- }).optional(),
-
- security: Joi.object({
- maxLoginAttempts: Joi.number().integer().min(1).max(100),
- lockoutDuration: Joi.number().integer().min(1).max(86400),
- ipWhitelist: Joi.string().allow('')
- }).optional()
- })
-};
-
-/**
- * 获取系统设置 (公开接口,不需要认证)
- * GET /api/system/settings
- */
-router.get('/settings', async (req, res, next) => {
- try {
-
- const db = getDB();
-
- // 获取系统设置
- const [settings] = await db.execute(
- 'SELECT setting_key, setting_value FROM system_settings'
- );
-
- // 组织设置数据
- const settingsData = {
- basic: {
- siteName: '',
- siteDescription: '',
- siteKeywords: '',
- siteLogo: '',
- siteFavicon: '',
-
- icp: ''
- },
- features: {
- allowRegister: true,
- allowTransfer: true,
- allowExchange: true,
- allowReview: true,
- allowComment: true
- },
-
- security: {
- maxLoginAttempts: 5,
- lockoutDuration: 300,
- ipWhitelist: ''
- }
- };
-
- // 填充数据库中的设置
- settings.forEach(setting => {
- const keys = setting.setting_key.split('.');
- if (keys.length === 2 && settingsData[keys[0]]) {
- try {
- // 尝试解析JSON值,如果失败则使用原始值
- settingsData[keys[0]][keys[1]] = JSON.parse(setting.setting_value);
- } catch {
- settingsData[keys[0]][keys[1]] = setting.setting_value;
- }
- }
- });
-
- logger.info('System settings retrieved', {
- settingsCount: settings.length
- });
-
- res.json({
- success: true,
- data: settingsData
- });
- } catch (error) {
- next(error);
- }
-});
-
-/**
- * 更新系统设置
- * PUT /api/system/settings
- */
-router.put('/settings',
- auth,
- validate(systemSchemas.updateSettings),
- async (req, res, next) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(HTTP_STATUS.FORBIDDEN).json({
- success: false,
- message: '权限不足'
- });
- }
-
- const db = getDB();
- const settings = req.body;
-
- // 开始事务
- await db.beginTransaction();
-
- try {
- // 更新设置
- for (const [category, categorySettings] of Object.entries(settings)) {
- for (const [key, value] of Object.entries(categorySettings)) {
- const settingKey = `${category}.${key}`;
- const settingValue = JSON.stringify(value);
-
- await db.execute(
- `INSERT INTO system_settings (setting_key, setting_value, updated_at)
- VALUES (?, ?, NOW())
- ON DUPLICATE KEY UPDATE
- setting_value = VALUES(setting_value),
- updated_at = VALUES(updated_at)`,
- [settingKey, settingValue]
- );
- }
- }
-
- await db.commit();
-
- logger.info('System settings updated', {
- userId: req.user.id,
- categories: Object.keys(settings)
- });
-
- res.json({
- success: true,
- message: '系统设置更新成功'
- });
- } catch (error) {
- await db.rollback();
- throw error;
- }
- } catch (error) {
- next(error);
- }
- }
-);
-
-/**
- * 获取系统信息
- * GET /api/system/info
- */
-router.get('/info', auth, async (req, res, next) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(HTTP_STATUS.FORBIDDEN).json({
- success: false,
- message: '权限不足'
- });
- }
-
- const systemInfo = {
- version: '1.0.0',
- nodeVersion: process.version,
- platform: process.platform,
- uptime: process.uptime(),
- memoryUsage: process.memoryUsage(),
- timestamp: new Date().toISOString()
- };
-
- res.json({
- success: true,
- data: systemInfo
- });
- } catch (error) {
- next(error);
- }
-});
-
-/**
- * 获取维护模式状态(公开接口)
- * GET /api/system/maintenance-status
- */
-router.get('/maintenance-status', async (req, res, next) => {
- try {
- const db = getDB();
-
- // 从系统设置表获取维护模式状态
- const [rows] = await db.execute(
- 'SELECT setting_value FROM system_settings WHERE setting_key = ?',
- ['maintenance_mode']
- );
-
- const maintenanceMode = rows.length > 0 ? rows[0].setting_value === 'true' : false;
-
- res.json({
- success: true,
- data: {
- maintenance_mode: maintenanceMode
- }
- });
-
- } catch (error) {
- console.error('获取维护模式状态失败:', error);
- next(error);
- }
-});
-
-/**
- * 获取维护模式状态(管理员接口)
- * GET /api/system/admin/maintenance-status
- */
-router.get('/admin/maintenance-status', auth, async (req, res, next) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({
- success: false,
- error: {
- code: 'FORBIDDEN',
- message: '权限不足'
- }
- });
- }
-
- const db = getDB();
-
- // 从系统设置表获取维护模式状态
- const [rows] = await db.execute(
- 'SELECT setting_value FROM system_settings WHERE setting_key = ?',
- ['maintenance_mode']
- );
-
- const maintenanceMode = rows.length > 0 ? rows[0].setting_value === 'true' : false;
-
- res.json({
- success: true,
- data: {
- maintenance_mode: maintenanceMode
- }
- });
-
- } catch (error) {
- console.error('获取维护模式状态失败:', error);
- next(error);
- }
-});
-
-/**
- * 切换维护模式
- * POST /api/system/toggle-maintenance
- */
-router.post('/toggle-maintenance', auth, async (req, res, next) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({
- success: false,
- error: {
- code: 'FORBIDDEN',
- message: '权限不足'
- }
- });
- }
-
- const { maintenance_mode } = req.body;
- const db = getDB();
-
- // 更新或插入维护模式设置
- await db.execute(
- `INSERT INTO system_settings (setting_key, setting_value, updated_at)
- VALUES ('maintenance_mode', ?, NOW())
- ON DUPLICATE KEY UPDATE
- setting_value = VALUES(setting_value),
- updated_at = NOW()`,
- [maintenance_mode ? 'true' : 'false']
- );
-
- logger.info('Maintenance mode toggled', {
- userId: req.user.id,
- username: req.user.username,
- maintenanceMode: maintenance_mode
- });
-
- res.json({
- success: true,
- data: {
- maintenance_mode: maintenance_mode
- },
- message: maintenance_mode ? '维护模式已开启' : '维护模式已关闭'
- });
-
- } catch (error) {
- console.error('切换维护模式失败:', error);
- next(error);
- }
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/transfers.js b/routes/transfers.js
deleted file mode 100644
index 91ff2fd..0000000
--- a/routes/transfers.js
+++ /dev/null
@@ -1,1208 +0,0 @@
-const express = require('express');
-const transferService = require('../services/transferService');
-const {auth: authenticateToken} = require('../middleware/auth');
-const {validate, validateQuery, transferSchemas, commonSchemas} = require('../middleware/validation');
-const {logger} = require('../config/logger');
-const {HTTP_STATUS} = require('../config/constants');
-const {getDB} = require('../database');
-const multer = require('multer');
-const path = require('path');
-const dayjs = require('dayjs');
-
-const router = express.Router();
-
-
-router.get('/',
- authenticateToken,
- validateQuery(transferSchemas.query),
- async (req, res, next) => {
- try {
- const {page, limit, status, start_date, end_date, search, sort, order} = req.query;
-
- const filters = {
- status,
- start_date,
- end_date,
- search
- };
-
- // 非管理员只能查看自己相关的转账
- if (req.user.role !== 'admin') {
- filters.user_id = req.user.id;
- }
-
- const result = await transferService.getTransfers(filters, {page, limit, sort, order});
-
- logger.info('Transfer list requested', {
- userId: req.user.id,
- filters,
- resultCount: result.transfers.length
- });
-
- res.json({
- success: true,
- data: result
- });
- } catch (error) {
- next(error);
- }
- }
-);
-
-router.get('/history', authenticateToken, async (req, res, next) => {
- try {
- const {page, limit, start_date, end_date, search, sort, order} = req.query;
-
- const filters = {
- start_date,
- end_date,
- search
- };
-
- // 非管理员只能查看自己相关的转账
- if (req.user.role !== 'admin') {
- filters.user_id = req.user.id;
- }
-
- const result = await transferService.getTransfersHistory(filters, {page, limit, sort, order});
-
- res.json({
- success: true,
- data: result
- });
- } catch (error) {
- next(error);
- }
-})
-
-router.get('/list',
- authenticateToken,
- validateQuery(transferSchemas.query),
- async (req, res, next) => {
- try {
- const {page, limit, status, transfer_type, start_date, end_date, sort, order} = req.query;
-
- const filters = {
- status,
- transfer_type,
- start_date,
- end_date
- };
-
- // 非管理员只能查看自己相关的转账
- if (req.user.role !== 'admin') {
- filters.user_id = req.user.id;
- }
-
- const result = await transferService.getTransfers(filters, {page, limit, sort, order});
-
- logger.info('Transfer list requested', {
- userId: req.user.id,
- filters,
- resultCount: result.transfers.length
- });
-
- res.json({
- success: true,
- data: result
- });
- } catch (error) {
- next(error);
- }
- }
-);
-
-
-router.get('/public-account', authenticateToken, async (req, res) => {
- try {
- const db = getDB();
- const [publicUser] = await db.execute(`
- SELECT id, username, real_name, balance
- FROM users
- WHERE username = 'public_account'
- AND is_system_account = TRUE
- `);
-
- if (publicUser.length === 0) {
- return res.status(404).json({success: false, message: '公户不存在'});
- }
-
- res.json({success: true, data: publicUser[0]});
- } catch (error) {
- console.error('获取公户信息失败:', error);
- res.status(500).json({success: false, message: '服务器错误'});
- }
-});
-
-
-router.post('/create',
- authenticateToken,
- validate(transferSchemas.create),
- async (req, res, next) => {
- try {
- const result = await transferService.createTransfer(req.user.id, req.body);
-
- logger.info('Transfer creation requested', {
- userId: req.user.id,
- transferId: result.transfer_id,
- amount: req.body.amount
- });
-
- res.status(HTTP_STATUS.CREATED).json({
- success: true,
- message: '转账记录创建成功,等待确认',
- data: result
- });
- } catch (error) {
- next(error);
- }
- }
-);
-
-
-router.post('/admin/create',
- authenticateToken,
- async (req, res, next) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const {from_user_id, to_user_id, amount, transfer_type, description} = req.body;
-
- // 验证必填字段
- if (!from_user_id || !to_user_id || !amount || !transfer_type) {
- return res.status(400).json({success: false, message: '缺少必填字段'});
- }
-
- const result = await transferService.createTransfer(from_user_id, {
- to_user_id,
- amount,
- transfer_type,
- description: description || '管理员分配转账'
- });
-
- logger.info('Admin transfer creation requested', {
- adminId: req.user.id,
- fromUserId: from_user_id,
- toUserId: to_user_id,
- transferId: result.transfer_id,
- amount: amount
- });
-
- res.status(HTTP_STATUS.CREATED).json({
- success: true,
- message: '转账分配成功',
- data: result
- });
- } catch (error) {
- next(error);
- }
- }
-);
-
-// 确认转账
-router.post('/confirm',
- authenticateToken,
- validate(transferSchemas.confirm),
- async (req, res, next) => {
- try {
- const {transfer_id, note} = req.body;
-
- await transferService.confirmTransfer(transfer_id, note, req.user.id);
-
- logger.info('Transfer confirmed', {
- transferId: transfer_id,
- operatorId: req.user.id
- });
-
- res.json({
- success: true,
- message: '转账确认成功'
- });
- } catch (error) {
- next(error);
- }
- }
-);
-
-// 确认转账(路径参数形式)
-router.post('/confirm/:id',
- authenticateToken,
- async (req, res, next) => {
- try {
- const transfer_id = req.params.id;
- const {action, note} = req.body;
-
- // 验证action参数
- if (action !== 'confirm') {
- return res.status(400).json({
- success: false,
- message: 'action参数必须为confirm'
- });
- }
-
- await transferService.confirmTransfer(transfer_id, note, req.user.id);
-
- logger.info('Transfer confirmed via path param', {
- transferId: transfer_id,
- operatorId: req.user.id
- });
-
- res.json({
- success: true,
- message: '转账确认成功'
- });
- } catch (error) {
- next(error);
- }
- }
-);
-
-// 拒绝转账
-router.post('/reject',
- authenticateToken,
- validate(transferSchemas.reject),
- async (req, res, next) => {
- try {
- const {transfer_id, note = ''} = req.body;
-
- await transferService.rejectTransfer(transfer_id, note, req.user.id);
-
- logger.info('Transfer rejected', {
- transferId: transfer_id,
- operatorId: req.user.id
- });
-
- res.json({
- success: true,
- message: '转账已拒绝'
- });
- } catch (error) {
- next(error);
- }
- }
-);
-
-// 用户确认收到转账
-router.post('/confirm-received',
- authenticateToken,
- async (req, res, next) => {
- try {
- const {transfer_id} = req.body;
-
- if (!transfer_id) {
- return res.status(400).json({success: false, message: '缺少转账ID'});
- }
-
- await transferService.confirmReceived(transfer_id, req.user.id);
-
- logger.info('Transfer received confirmed by user', {
- transferId: transfer_id,
- userId: req.user.id
- });
-
- res.json({
- success: true,
- message: '已确认收到转账,余额已更新'
- });
- } catch (error) {
- next(error);
- }
- }
-);
-
-// 用户确认未收到转账
-router.post('/confirm-not-received',
- authenticateToken,
- async (req, res, next) => {
- try {
- const {transfer_id} = req.body;
-
- if (!transfer_id) {
- return res.status(400).json({success: false, message: '缺少转账ID'});
- }
-
- await transferService.confirmNotReceived(transfer_id, req.user.id);
-
- logger.info('Transfer not received confirmed by user', {
- transferId: transfer_id,
- userId: req.user.id
- });
-
- res.json({
- success: true,
- message: '已确认未收到转账'
- });
- } catch (error) {
- next(error);
- }
- }
-);
-
-
-
-// 获取用户转账记录
-router.get('/user/:userId', authenticateToken, async (req, res) => {
- try {
- const userId = req.params.userId;
- const {page = 1, limit = 10, status} = req.query;
-
- // 检查权限(只能查看自己的记录或管理员查看所有)
- // if (req.user.id != userId && req.user.role !== 'admin') {
- // return res.status(403).json({ success: false, message: '权限不足' });
- // }
-
- const db = getDB();
-
- // 确保参数为有效数字
- 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 whereClause = `WHERE source_type='manual' AND (t.from_user_id = ? OR t.to_user_id = ?)`;
- const userIdInt = parseInt(userId);
- let listParams = [userIdInt, userIdInt];
- let countParams = [userIdInt, userIdInt];
-
- if (status) {
- whereClause += ' AND t.status = ?';
- listParams.push(status);
- countParams.push(status);
- }
-
- // 添加分页参数
- listParams.push(limitNum.toString(), offset.toString());
-
- const [transfers] = await db.execute(`
- SELECT t.*,
- from_user.username as from_username,
- from_user.real_name as from_real_name,
- to_user.username as to_username,
- to_user.real_name as to_real_name
- FROM transfers t
- LEFT JOIN users from_user ON t.from_user_id = from_user.id
- LEFT JOIN users to_user ON t.to_user_id = to_user.id
- ${whereClause}
- ORDER BY t.created_at
- DESC
- LIMIT ${limitNum} OFFSET ${offset}
- `, countParams);
-
- const [countResult] = await db.execute(`
- SELECT COUNT(*) as total
- FROM transfers t ${whereClause}
- `, countParams);
-
- res.json({
- success: true,
- data: {
- transfers,
- pagination: {
- page: pageNum,
- limit: limitNum,
- total: countResult[0].total,
- pages: Math.ceil(countResult[0].total / limitNum)
- }
- }
- });
- } catch (error) {
- console.error('获取转账记录失败:', error);
- res.status(500).json({success: false, message: '服务器错误'});
- }
-});
-
-// 获取转账统计信息
-router.get('/stats', authenticateToken, async (req, res) => {
- try {
- const userId = req.user.id;
- const isAdmin = req.user.role === 'admin';
- const db = getDB();
-
- let stats = {};
-
- if (isAdmin) {
- // 管理员可以查看全局统计
- const [totalStats] = await db.execute(`
- SELECT COUNT(*) as total_transfers,
- SUM(amount) as total_flow_amount,
- SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
- SUM(CASE WHEN status = 'confirmed' THEN 1 ELSE 0 END) as confirmed_count,
- SUM(CASE WHEN status = 'received' THEN 1 ELSE 0 END) as received_count,
- SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected_count,
- SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled_count,
- SUM(CASE WHEN status = 'not_received' THEN 1 ELSE 0 END) as not_received_count,
- SUM(CASE WHEN is_overdue = 1 THEN 1 ELSE 0 END) as overdue_count,
- SUM(CASE WHEN is_bad_debt = 1 THEN 1 ELSE 0 END) as bad_debt_count,
- SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as total_amount,
- SUM(CASE WHEN is_bad_debt = 1 THEN amount ELSE 0 END) as bad_debt_amount,
- SUM(CASE
- WHEN transfer_type = 'initial' AND status = 'confirmed' THEN amount
- ELSE 0 END) as initial_amount,
- SUM(CASE
- WHEN transfer_type = 'return' AND status = 'confirmed' THEN amount
- ELSE 0 END) as return_amount,
- SUM(CASE
- WHEN transfer_type = 'user_to_user' AND status = 'confirmed' THEN amount
- ELSE 0 END) as user_to_user_amount,
- (SELECT SUM(balance)
- FROM users
- WHERE role = 'user'
- AND is_system_account = 1) as total_merchant_balance,
- (SELECT SUM(balance)
- FROM users
- WHERE role = 'user'
- AND is_system_account != 1) as total_user_balance,
- SUM(CASE WHEN source_type IN ('system') THEN amount END) as participated_transfers,
- SUM(CASE WHEN source_type IN ('agent') THEN amount END) as agent_total,
- SUM(CASE WHEN source_type IN ('operated_agent') THEN amount END) as operated_agent_total
- FROM transfers
- `);
-
- const todayStr = dayjs().format('YYYY-MM-DD');
- const currentYear = dayjs().year();
- const currentMonth = dayjs().month() + 1;
- console.log(todayStr, 'todayStr');
-
- const [todayStats] = await db.execute(`
- SELECT COUNT(*) as today_transfers,
- (
- COALESCE((SELECT SUM(amount)
- FROM transfers
- WHERE DATE(created_at) = ?
- AND to_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
- AND status = 'received'), 0) -
- COALESCE((SELECT SUM(amount)
- FROM transfers
- WHERE DATE(created_at) = ?
- AND from_user_id IN (SELECT id FROM users WHERE is_system_account = 1)
- AND status = 'received'), 0)
- ) as today_amount
- FROM transfers
- WHERE DATE(created_at) = ?
- `, [todayStr, todayStr, todayStr]);
-
- const [monthlyStats] = await db.execute(`
- SELECT COUNT(*) as monthly_transfers,
- SUM(CASE WHEN status = 'received' THEN amount ELSE 0 END) as monthly_amount,
- SUM(CASE WHEN source_type IN ('system') THEN amount END) as monthly_participated_transfers
- FROM transfers
- WHERE YEAR(created_at) = ?
- AND MONTH(created_at) = ?
- `, [currentYear, currentMonth]);
-
- // 获取上月统计数据用于对比
- const lastMonth = currentMonth === 1 ? 12 : currentMonth - 1;
- const lastMonthYear = currentMonth === 1 ? currentYear - 1 : currentYear;
-
- const [lastMonthStats] = await db.execute(`
- SELECT COUNT(*) as last_monthly_transfers,
- SUM(CASE WHEN status = 'confirmed' THEN amount ELSE 0 END) as last_monthly_amount,
- SUM(CASE WHEN source_type IN ('system') THEN amount END) as last_monthly_participated_transfers
- FROM transfers
- WHERE YEAR(created_at) = ?
- AND MONTH(created_at) = ?
- `, [lastMonthYear, lastMonth]);
-
- stats = {
- total: {
- transfers: totalStats[0].total_transfers || 0,
- pending: parseFloat(totalStats[0].total_flow_amount || 0),
- pending_count: totalStats[0].pending_count || 0,
- confirmed: totalStats[0].confirmed_count || 0,
- received_count: totalStats[0].received_count || 0,
- rejected: totalStats[0].rejected_count || 0,
- cancelled_count: totalStats[0].cancelled_count || 0,
- not_received_count: totalStats[0].not_received_count || 0,
- overdue: totalStats[0].overdue_count || 0,
- bad_debt: totalStats[0].bad_debt_count || 0,
- amount: parseFloat(totalStats[0].total_amount || 0),
- bad_debt_amount: parseFloat(totalStats[0].bad_debt_amount || 0),
- total_merchant_balance: parseFloat(totalStats[0].total_merchant_balance || 0),
- initial_amount: parseFloat(totalStats[0].initial_amount || 0),
- return_amount: parseFloat(totalStats[0].return_amount || 0),
- user_to_user_amount: parseFloat(totalStats[0].user_to_user_amount || 0),
- participated_transfers: totalStats[0].participated_transfers || 0,
- total_user_balance:totalStats[0].total_user_balance || 0,
- agent_total:totalStats[0].agent_total || 0,//代理收入
- operated_agent_total:totalStats[0].operated_agent_total || 0,//直营代理收入
- },
- today: {
- transfers: todayStats[0].today_transfers || 0,
- amount: parseFloat(todayStats[0].today_amount || 0)
- },
- monthly: {
- transfers: monthlyStats[0].monthly_transfers || 0,
- amount: parseFloat(monthlyStats[0].monthly_amount || 0),
- participated_transfers: monthlyStats[0].monthly_participated_transfers || 0
- },
- lastMonth: {
- transfers: lastMonthStats[0].last_monthly_transfers || 0,
- amount: parseFloat(lastMonthStats[0].last_monthly_amount || 0),
- participated_transfers: lastMonthStats[0].last_monthly_participated_transfers || 0
- }
- };
- } else {
- // 普通用户只能查看自己的统计
- const [userStats] = await db.execute(`
- SELECT COUNT(*) as total_transfers,
- SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count,
- SUM(CASE WHEN status = 'confirmed' THEN 1 ELSE 0 END) as confirmed_count,
- SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected_count,
- SUM(CASE WHEN status = 'confirmed' AND from_user_id = ? THEN amount ELSE 0 END) as sent_amount,
- SUM(CASE WHEN status = 'confirmed' AND to_user_id = ? THEN amount ELSE 0 END) as received_amount
- FROM transfers
- WHERE from_user_id = ?
- OR to_user_id = ?
- `, [userId, userId, userId, userId]);
-
- const todayStr = dayjs().format('YYYY-MM-DD');
-
- const [todayStats] = await db.execute(`
- SELECT COUNT(*) as today_transfers,
- SUM(CASE WHEN status = 'confirmed' AND from_user_id = ? THEN amount ELSE 0 END) as today_sent,
- SUM(CASE WHEN status = 'confirmed' AND to_user_id = ? THEN amount ELSE 0 END) as today_received
- FROM transfers
- WHERE (from_user_id = ? OR to_user_id = ?)
- AND DATE(created_at) = ?
- `, [userId, userId, userId, userId, todayStr]);
-
- stats = {
- total: {
- transfers: userStats[0].total_transfers || 0,
- pending: userStats[0].pending_count || 0,
- confirmed: userStats[0].confirmed_count || 0,
- rejected: userStats[0].rejected_count || 0,
- sent_amount: parseFloat(userStats[0].sent_amount || 0),
- received_amount: parseFloat(userStats[0].received_amount || 0)
- },
- today: {
- transfers: todayStats[0].today_transfers || 0,
- sent_amount: parseFloat(todayStats[0].today_sent || 0),
- received_amount: parseFloat(todayStats[0].today_received || 0)
- }
- };
- }
-
- res.json({success: true, data: stats});
- } catch (error) {
- console.error('获取转账统计失败:', error);
- res.status(500).json({success: false, message: '获取转账统计失败'});
- }
-});
-
-// 获取待确认的转账
-router.get('/pending', authenticateToken, async (req, res) => {
- try {
- const userId = parseInt(req.user.id);
- const db = getDB();
-
- const [transfers] = await db.execute(`
- SELECT t.*,
- from_user.username as from_username,
- from_user.real_name as from_real_name
- FROM transfers t
- LEFT JOIN users from_user ON t.from_user_id = from_user.id
- WHERE t.to_user_id = ?
- AND t.status = 'pending'
- ORDER BY t.created_at DESC
- `, [userId]);
-
- res.json({success: true, data: transfers});
- } catch (error) {
- console.error('获取待确认转账失败:', error);
- res.status(500).json({success: false, message: '服务器错误'});
- }
-});
-
-// 获取当前用户账户信息(不需要传递用户ID)
-router.get('/account', authenticateToken, async (req, res) => {
- try {
- const userId = req.user.id;
-
- const db = getDB();
- const [user] = await db.execute(`
- SELECT id, username, real_name, balance, created_at, updated_at, points
- FROM users
- WHERE id = ?
- `, [userId]);
-
- if (user.length === 0) {
- return res.status(404).json({success: false, message: '用户不存在'});
- }
-
- // 返回用户账户信息,格式与原来的 accounts 表保持一致
- const accountData = {
- id: user[0].id,
- user_id: user[0].id,
- account_type: 'user',
- balance: user[0].balance,
- username: user[0].username,
- real_name: user[0].real_name,
- created_at: user[0].created_at,
- updated_at: user[0].updated_at,
- points: user[0].points
- };
-
- res.json({success: true, data: accountData});
- } catch (error) {
- console.error('获取账户信息失败:', error);
- res.status(500).json({success: false, message: '服务器错误'});
- }
-});
-
-// 获取指定用户账户信息(管理员权限或用户本人)
-router.get('/account/:userId', authenticateToken, async (req, res) => {
- try {
- const userId = req.params.userId;
-
- // 检查权限
- if (req.user.id != userId && req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const db = getDB();
- const [user] = await db.execute(`
- SELECT id, username, real_name, balance, created_at, updated_at
- FROM users
- WHERE id = ?
- `, [userId]);
-
- if (user.length === 0) {
- return res.status(404).json({success: false, message: '用户不存在'});
- }
-
- // 返回用户账户信息,格式与原来的 accounts 表保持一致
- const accountData = {
- id: user[0].id,
- user_id: user[0].id,
- account_type: 'user',
- balance: user[0].balance,
- username: user[0].username,
- real_name: user[0].real_name,
- created_at: user[0].created_at,
- updated_at: user[0].updated_at
- };
-
- res.json({success: true, data: accountData});
- } catch (error) {
- console.error('获取账户信息失败:', error);
- res.status(500).json({success: false, message: '服务器错误'});
- }
-});
-
-// 获取转账趋势数据(管理员权限)
-router.get('/trend', authenticateToken, async (req, res) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const db = getDB();
- const {days = 7} = req.query;
- const daysNum = Math.min(30, Math.max(1, parseInt(days) || 7));
-
- // 首先获取数据库中最早和最晚的转账日期
- const [dateRange] = await db.execute(`
- SELECT MIN(DATE(created_at)) as min_date,
- MAX(DATE(created_at)) as max_date,
- COUNT(*) as total_count
- FROM transfers
- `);
-
- if (dateRange[0].total_count === 0) {
- // 如果没有转账记录,返回空数据
- const result = [];
- const now = new Date();
-
- for (let i = daysNum - 1; i >= 0; i--) {
- const date = dayjs().subtract(i, 'day');
- result.push({
- date: date.format('MM-DD'),
- count: 0,
- amount: 0
- });
- }
-
- return res.json({
- success: true,
- data: result
- });
- }
-
- // 获取最近的转账数据(基于实际数据的最大日期)
- const maxDate = dayjs(dateRange[0].max_date);
-
- // 获取指定天数内的转账趋势(从最大日期往前推)
- const [trendData] = await db.execute(`
- SELECT DATE(created_at) as date,
- COUNT(*) as count,
- SUM(amount) as amount
- FROM transfers
- WHERE DATE(created_at) >= DATE_SUB(?, INTERVAL ? DAY)
- AND status IN ('confirmed', 'received')
- GROUP BY DATE(created_at)
- ORDER BY date ASC
- `, [dateRange[0].max_date, daysNum - 1]);
-
- // 填充缺失的日期(转账数为0)
- const result = [];
-
- for (let i = daysNum - 1; i >= 0; i--) {
- const date = maxDate.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,
- amount: existingData ? parseFloat(existingData.amount) : 0
- });
- }
-
- res.json({
- success: true,
- data: result
- });
- } catch (error) {
- console.error('获取转账趋势错误:', error);
- res.status(500).json({success: false, message: '获取转账趋势失败'});
- }
-});
-
-// 管理员解除坏账(管理员权限)
-router.post('/remove-bad-debt/:transferId', authenticateToken, async (req, res) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const {transferId} = req.params;
- const {reason} = req.body;
- const adminId = req.user.id;
-
- // 验证转账ID
- if (!transferId || isNaN(transferId)) {
- return res.status(400).json({success: false, message: '无效的转账ID'});
- }
-
- const result = await transferService.removeBadDebt(transferId, adminId, reason);
-
- res.json({
- success: true,
- message: '坏账标记已解除',
- data: result
- });
- } catch (error) {
- console.error('解除坏账失败:', error);
- if (error.statusCode) {
- return res.status(error.statusCode).json({
- success: false,
- message: error.message
- });
- }
- res.status(500).json({success: false, message: '解除坏账失败'});
- }
-});
-
-// 强制变更转账状态(管理员权限)
-router.post('/force-change-status/:transferId', authenticateToken, async (req, res) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const {transferId} = req.params;
- const {newStatus, status, reason, adjust_balance = false} = req.body;
- console.log('newStatus:', newStatus);
- console.log('status:', status);
- console.log('reason:', reason);
- console.log('adjust_balance:', adjust_balance);
-
- // 兼容两种参数名:newStatus 和 status
- const actualNewStatus = newStatus || status;
- const adminId = req.user.id;
-
- // 验证转账ID
- if (!transferId || isNaN(transferId)) {
- return res.status(400).json({success: false, message: '无效的转账ID'});
- }
-
- // 验证必填参数
- if (!actualNewStatus) {
- return res.status(400).json({success: false, message: '新状态不能为空'});
- }
-
- // if (!reason) {
- // return res.status(400).json({ success: false, message: '变更原因不能为空' });
- // }
-
- const result = await transferService.forceChangeTransferStatus(
- transferId,
- actualNewStatus,
- reason,
- adminId,
- adjust_balance
- );
-
- res.json({
- success: true,
- message: `转账状态已从 ${result.oldStatus} 变更为 ${result.newStatus}`,
- data: result
- });
- } catch (error) {
- console.error('强制变更转账状态失败:', error);
- if (error.statusCode) {
- return res.status(error.statusCode).json({
- success: false,
- message: error.message
- });
- }
- res.status(500).json({success: false, message: '变更转账状态失败'});
- }
-});
-
-// 管理员查看数据库连接状态
-router.get('/admin/database/status', authenticateToken, async (req, res) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const dbMonitor = require('../db-monitor');
- const diagnosis = await dbMonitor.diagnose();
-
- res.json({
- success: true,
- data: diagnosis
- });
- } catch (error) {
- logger.error('Get database status failed', {
- adminId: req.user.id,
- error: error.message
- });
- res.status(500).json({
- success: false,
- message: '获取数据库状态失败: ' + error.message
- });
- }
-});
-
-// 管理员获取数据库监控报告
-router.get('/admin/database/report', authenticateToken, async (req, res) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const dbMonitor = require('../db-monitor');
- const report = await dbMonitor.generateReport();
-
- res.json({
- success: true,
- data: {
- report,
- timestamp: new Date().toISOString()
- }
- });
- } catch (error) {
- logger.error('Get database report failed', {
- adminId: req.user.id,
- error: error.message
- });
- res.status(500).json({
- success: false,
- message: '获取数据库报告失败: ' + error.message
- });
- }
-});
-
-/**
- * 获取待处理的匹配转账订单
- * @param {number} page - 页码
- * @param {number} limit - 每页数量
- * @param {string} status - 状态过滤
- * @param {string} search - 搜索关键词(用户名或真实姓名)
- * @param {string} sort - 排序字段
- * @param {string} order - 排序方向(asc/desc)
- */
-router.get('/pending-allocations',
- authenticateToken,
- async (req, res, next) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const {
- page = 1,
- limit = 20,
- status = '',
- search = '',
- sort = 'created_at',
- order = 'desc'
- } = req.query;
-
- const db = getDB();
- const offset = (parseInt(page) - 1) * parseInt(limit);
-
- // 构建查询条件
- let whereConditions = [];
- let queryParams = [];
-
- // 状态过滤
- if (status) {
- whereConditions.push('oa.status = ?');
- queryParams.push(status);
- }
-
- // 搜索过滤(用户名或真实姓名)
- if (search) {
- whereConditions.push('(uf.username LIKE ? OR uf.real_name LIKE ? OR ut.username LIKE ? OR ut.real_name LIKE ?)');
- const searchPattern = `%${search}%`;
- queryParams.push(searchPattern, searchPattern, searchPattern, searchPattern);
- }
-
- const whereClause = whereConditions.length > 0 ? 'WHERE ' + whereConditions.join(' AND ') : '';
-
- // 验证排序字段
- const allowedSortFields = ['created_at', 'amount', 'status', 'cycle_number'];
- const sortField = allowedSortFields.includes(sort) ? sort : 'created_at';
- const sortOrder = order.toLowerCase() === 'asc' ? 'ASC' : 'DESC';
-
- // 获取总数
- const countQuery = `
- SELECT COUNT(*) as total
- FROM transfers oa
- JOIN users uf ON oa.from_user_id = uf.id
- JOIN users ut ON oa.to_user_id = ut.id
- JOIN matching_orders mo ON oa.id = mo.id
- ${whereClause}
- `;
-
- const [countResult] = await db.execute(countQuery, queryParams);
- const total = countResult[0].total;
-
- // 使用 query 方法避免 LIMIT/OFFSET 参数问题
- const dataQuery = `
- SELECT oa.id,
- oa.from_user_id,
- oa.to_user_id,
- oa.amount,
- oa.cycle_number,
- oa.status,
- oa.outbound_date,
- oa.return_date,
- oa.can_return_after,
- oa.confirmed_at,
- oa.created_at,
- oa.updated_at,
- uf.username as from_username,
- uf.real_name as from_real_name,
- ut.username as to_username,
- ut.real_name as to_real_name,
- mo.amount as order_total_amount,
- mo.status as order_status,
- mo.matching_type,
- t.status as transfer_status,
- t.voucher_url
- FROM transfers oa
- JOIN users uf ON oa.from_user_id = uf.id
- JOIN users ut ON oa.to_user_id = ut.id
- JOIN matching_orders mo ON oa.id = mo.id
- ${whereClause}
- ORDER BY oa.${sortField} ${sortOrder}
- LIMIT ${parseInt(limit)} OFFSET ${parseInt(offset)}
- `;
-
- const [allocations] = queryParams.length > 0
- ? await db.execute(dataQuery, queryParams)
- : await db.query(dataQuery);
-
- // 计算分页信息
- const totalPages = Math.ceil(total / parseInt(limit));
-
- logger.info('Pending allocations list requested', {
- userId: req.user.id,
- page: parseInt(page),
- limit: parseInt(limit),
- total,
- resultCount: allocations.length
- });
-
- res.json({
- success: true,
- data: {
- allocations,
- pagination: {
- page: parseInt(page),
- limit: parseInt(limit),
- total,
- totalPages
- }
- }
- });
- } catch (error) {
- logger.error('Get pending allocations failed', {
- userId: req.user.id,
- error: error.message
- });
- next(error);
- }
- }
-);
-
-/**
- * 获取待处理匹配订单的统计信息
- */
-router.get('/pending-allocations/stats',
- authenticateToken,
- async (req, res, next) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const db = getDB();
-
- // 获取统计数据
- const [stats] = await db.execute(`
- SELECT COUNT(*) as total_allocations,
- COUNT(CASE WHEN oa.status = 'pending' THEN 1 END) as pending_count,
- COUNT(CASE WHEN oa.status = 'confirmed' THEN 1 END) as confirmed_count,
- COUNT(CASE WHEN oa.status = 'completed' THEN 1 END) as completed_count,
- SUM(oa.amount) as total_amount,
- SUM(CASE WHEN oa.status = 'pending' THEN oa.amount ELSE 0 END) as pending_amount
- FROM transfers oa
- JOIN matching_orders mo ON oa.id = mo.id
- WHERE mo.status != 'cancelled'
- `);
-
- res.json({
- success: true,
- data: stats[0]
- });
- } catch (error) {
- logger.error('Get pending allocations stats failed', {
- userId: req.user.id,
- error: error.message
- });
- next(error);
- }
- }
-);
-
-/**
- * 获取昨日用户转账统计列表
- */
-router.get('/daily-stats',
- authenticateToken,
- async (req, res, next) => {
- try {
- // 检查管理员权限
- if (req.user.role !== 'admin') {
- return res.status(403).json({success: false, message: '权限不足'});
- }
-
- const db = getDB();
-
- // 获取昨日和今日的日期范围(从0点开始计算)
- // 今日0点到23:59:59
- const todayStart = dayjs().startOf('day');
- const todayEnd = dayjs().endOf('day');
-
- // 昨日0点到23:59:59
- const yesterdayStart = dayjs().subtract(1, 'day').startOf('day');
- const yesterdayEnd = dayjs().subtract(1, 'day').endOf('day');
-
- // 转换为MySQL兼容的字符串格式
- const todayStartStr = todayStart.format('YYYY-MM-DD HH:mm:ss');
- const todayEndStr = todayEnd.format('YYYY-MM-DD HH:mm:ss');
- const yesterdayStartStr = yesterdayStart.format('YYYY-MM-DD HH:mm:ss');
- const yesterdayEndStr = yesterdayEnd.format('YYYY-MM-DD HH:mm:ss');
-
- // 使用dayjs格式化日期字符串用于返回
- const todayStr = todayStart.format('YYYY-MM-DD');
- const yesterdayStr = yesterdayStart.format('YYYY-MM-DD');
-
- // 获取所有用户的昨日转出和今日入账统计
- let [userStats] = await db.execute(`
- SELECT u.id as user_id,
- u.username,
- u.real_name,
- u.phone,
- 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(u.balance, 0) + COALESCE(confirmed_from.confirmed_amount, 0)) > ABS(u.balance)
- THEN ABS(u.balance)
- ELSE (COALESCE(u.balance, 0) + COALESCE(confirmed_from.confirmed_amount, 0))
- END as balance_needed
- FROM users u
- LEFT JOIN (SELECT from_user_id,
- SUM(amount) as amount
- FROM transfers
- WHERE created_at >= ?
- AND created_at <= ?
- AND status IN ('confirmed', 'received')
- GROUP BY from_user_id) yesterday_out ON u.id = yesterday_out.from_user_id
- LEFT JOIN (SELECT to_user_id,
- SUM(amount) as amount
- FROM transfers
- WHERE created_at >= ?
- AND created_at <= ?
- 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, 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: {
- date: {
- yesterday: yesterdayStr,
- today: todayStr
- },
- users: userStats
- }
- });
- } catch (error) {
- logger.error('Get daily transfer stats failed', {
- userId: req.user.id,
- error: error.message
- });
- next(error);
- }
- }
-);
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/upload.js b/routes/upload.js
deleted file mode 100644
index 2222e0b..0000000
--- a/routes/upload.js
+++ /dev/null
@@ -1,421 +0,0 @@
-const express = require('express');
-const multer = require('multer');
-const path = require('path');
-const { auth } = require('../middleware/auth');
-const { authenticateToken } = require('./auth');
-const minioService = require('../services/minioService');
-const { initializeBuckets } = require('../config/minio');
-
-const router = express.Router();
-
-// 初始化MinIO存储桶
-// initializeBuckets().catch(console.error);
-
-/**
- * @swagger
- * tags:
- * name: Upload
- * description: 文件上传API
- */
-
-// 配置multer内存存储(用于MinIO上传)
-const storage = multer.memoryStorage();
-
-// 文件过滤器 - 支持图片和视频
-const fileFilter = (req, file, cb) => {
- // 允许图片和视频文件
- if (file.mimetype.startsWith('image/') || file.mimetype.startsWith('video/')) {
- cb(null, true);
- } else {
- cb(new Error('只能上传图片或视频文件'), false);
- }
-};
-
-// 单文件上传配置
-const upload = multer({
- storage: storage,
- fileFilter: fileFilter,
- limits: {
- fileSize: 5 * 1024 * 1024, // 5MB
- files: 1 // 一次只能上传一个文件
- }
-});
-
-// 多文件上传配置
-const multiUpload = multer({
- storage: storage,
- fileFilter: fileFilter,
- limits: {
- fileSize: 10 * 1024 * 1024, // 10MB (视频文件更大)
- files: 10 // 最多10个文件
- }
-});
-
-/**
- * @swagger
- * /upload/image:
- * post:
- * summary: 上传图片
- * tags: [Upload]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * multipart/form-data:
- * schema:
- * type: object
- * properties:
- * file:
- * type: string
- * format: binary
- * description: 要上传的图片文件
- * type:
- * type: string
- * enum: [avatar, product, document]
- * default: document
- * description: 上传文件类型
- * responses:
- * 200:
- * description: 图片上传成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * url:
- * type: string
- * description: 上传后的文件URL
- * filename:
- * type: string
- * description: 上传后的文件名
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-router.post('/image', authenticateToken, (req, res) => {
- upload.single('file')(req, res, async (err) => {
- if (err instanceof multer.MulterError) {
- if (err.code === 'LIMIT_FILE_SIZE') {
- return res.status(400).json({
- success: false,
- message: '文件大小不能超过 5MB'
- });
- }
- if (err.code === 'LIMIT_FILE_COUNT') {
- return res.status(400).json({
- success: false,
- message: '一次只能上传一个文件'
- });
- }
- return res.status(400).json({
- success: false,
- message: '文件上传失败:' + err.message
- });
- } else if (err) {
- return res.status(400).json({
- success: false,
- message: err.message
- });
- }
-
- if (!req.file) {
- return res.status(400).json({
- success: false,
- message: '请选择要上传的文件'
- });
- }
-
- try {
- // 使用MinIO服务上传文件
- const type = req.body.type || 'document';
- const result = await minioService.uploadFile(
- req.file.buffer,
- req.file.originalname,
- req.file.mimetype,
- type
- );
-
- res.json({
- success: true,
- message: '文件上传成功',
- data: result.data
- });
- } catch (error) {
- console.error('文件上传到MinIO失败:', error);
- res.status(500).json({
- success: false,
- message: error.message || '文件上传失败'
- });
- }
- });
-});
-
-/**
- * @swagger
- * /upload:
- * post:
- * summary: 多文件上传接口 (支持MediaUpload组件)
- * tags: [Upload]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * multipart/form-data:
- * schema:
- * type: object
- * properties:
- * files:
- * type: array
- * items:
- * type: string
- * format: binary
- * description: 要上传的文件列表
- * type:
- * type: string
- * enum: [avatar, product, document]
- * default: document
- * description: 上传文件类型
- * responses:
- * 200:
- * description: 文件上传成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 文件上传成功
- * data:
- * type: array
- * items:
- * type: object
- * properties:
- * filename:
- * type: string
- * originalname:
- * type: string
- * mimetype:
- * type: string
- * size:
- * type: integer
- * path:
- * type: string
- * url:
- * type: string
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- * @access Private
- */
-router.post('/', authenticateToken, (req, res) => {
- multiUpload.array('file', 10)(req, res, async (err) => {
- if (err instanceof multer.MulterError) {
- if (err.code === 'LIMIT_FILE_SIZE') {
- return res.status(400).json({
- success: false,
- message: '文件大小不能超过 10MB'
- });
- }
- if (err.code === 'LIMIT_FILE_COUNT') {
- return res.status(400).json({
- success: false,
- message: '一次最多只能上传10个文件'
- });
- }
- return res.status(400).json({
- success: false,
- message: '文件上传失败:' + err.message
- });
- } else if (err) {
- return res.status(400).json({
- success: false,
- message: err.message
- });
- }
-
- if (!req.files || req.files.length === 0) {
- return res.status(400).json({
- success: false,
- message: '请选择要上传的文件'
- });
- }
-
- try {
- // 使用MinIO服务上传多个文件
- const type = req.body.type || 'document';
- const files = req.files.map(file => ({
- buffer: file.buffer,
- originalName: file.originalname,
- mimeType: file.mimetype
- }));
-
- const result = await minioService.uploadMultipleFiles(files, type);
-
- // 如果只上传了一个文件,返回单文件格式以保持兼容性
- if (result.data.files.length === 1) {
- result.data.files.forEach(element => {
- element.path = '/' + element.path
- });
- res.json({
- success: true,
- message: '文件上传成功',
- data: {
- ...result.data.files[0],
- urls: result.data.urls // 同时提供urls数组格式
- }
- });
- } else {
- // 多文件返回数组格式
- res.json({
- success: true,
- message: `成功上传${result.data.files.length}个文件`,
- data: result.data
- });
- }
- } catch (error) {
- console.error('文件上传到MinIO失败:', error);
- res.status(500).json({
- success: false,
- message: error.message || '文件上传失败'
- });
- }
- });
-});
-
-/**
- * @swagger
- * /upload/single:
- * post:
- * summary: 单文件上传接口(兼容性接口)
- * tags: [Upload]
- * security:
- * - bearerAuth: []
- * requestBody:
- * required: true
- * content:
- * multipart/form-data:
- * schema:
- * type: object
- * properties:
- * file:
- * type: string
- * format: binary
- * description: 要上传的文件
- * type:
- * type: string
- * enum: [avatar, product, document]
- * default: document
- * description: 上传文件类型
- * responses:
- * 200:
- * description: 文件上传成功
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * success:
- * type: boolean
- * example: true
- * message:
- * type: string
- * example: 文件上传成功
- * url:
- * type: string
- * description: 上传后的文件URL
- * filename:
- * type: string
- * description: 上传后的文件名
- * originalname:
- * type: string
- * description: 原始文件名
- * size:
- * type: integer
- * description: 文件大小
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未授权
- * 500:
- * description: 服务器错误
- */
-router.post('/single', auth, (req, res) => {
- upload.single('file')(req, res, async (err) => {
- if (err instanceof multer.MulterError) {
- return res.status(400).json({
- success: false,
- message: '文件上传失败:' + err.message
- });
- } else if (err) {
- return res.status(400).json({
- success: false,
- message: err.message
- });
- }
-
- if (!req.file) {
- return res.status(400).json({ success: false, message: '没有上传文件' });
- }
-
- try {
- // 使用MinIO服务上传文件
- const type = req.body.type || 'document';
- const result = await minioService.uploadFile(
- req.file.buffer,
- req.file.originalname,
- req.file.mimetype,
- type
- );
-
- res.json({
- success: true,
- message: '文件上传成功',
- url: result.data.url,
- filename: result.data.filename,
- originalname: result.data.originalname,
- size: result.data.size
- });
- } catch (error) {
- console.error('文件上传到MinIO失败:', error);
- res.status(500).json({
- success: false,
- message: error.message || '文件上传失败'
- });
- }
- });
-});
-
-// 错误处理中间件
-router.use((error, req, res, next) => {
- if (error instanceof multer.MulterError) {
- if (error.code === 'LIMIT_FILE_SIZE') {
- return res.status(400).json({ success: false, message: '文件大小不能超过10MB' });
- }
- if (error.code === 'LIMIT_FILE_COUNT') {
- return res.status(400).json({ success: false, message: '一次最多只能上传10个文件' });
- }
- }
-
- if (error.message === '只能上传图片或视频文件') {
- return res.status(400).json({ success: false, message: error.message });
- }
-
- res.status(500).json({ success: false, message: '上传失败' });
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/users.js b/routes/users.js
deleted file mode 100644
index 5b0afbc..0000000
--- a/routes/users.js
+++ /dev/null
@@ -1,1816 +0,0 @@
-const express = require('express');
-const bcrypt = require('bcryptjs');
-const {getDB} = require('../database');
-const {auth, adminAuth} = require('../middleware/auth');
-const dayjs = require('dayjs');
-
-const router = express.Router();
-
-/**
- * @swagger
- * tags:
- * name: Users
- * description: 用户管理API
- */
-
-router.post('/', auth, adminAuth, async (req, res) => {
- try {
- const db = getDB();
- await db.query('START TRANSACTION');
-
- const {
- username,
- password,
- role = 'user',
- isSystemAccount = false, // 是否为虚拟商户
- realName,
- idCard,
- wechatQr,
- alipayQr,
- bankCard,
- unionpayQr,
- province,
- city,
- districtId,
- phone,
- avatar,
- user_type = 'directly_operated',
- inviter = null
- } = req.body;
-
- if (!username || !password) {
- return res.status(400).json({success: false, message: '用户名和密码不能为空'});
- }
-
- if (!realName || !idCard) {
- return res.status(400).json({success: false, message: '姓名和身份证号不能为空'});
- }
- if (!city || !districtId || !province) {
- 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);
-
- // 创建用户
- console.log([username, hashedPassword, role, isSystemAccount, 0, realName, idCard, wechatQr, alipayQr, bankCard, unionpayQr, phone, province, city, districtId, user_type, inviter], 'info');
-
- 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, province, city, district_id, user_type, inviter,avatar) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)',
- [username, hashedPassword, role, isSystemAccount, 0, realName, idCard, wechatQr, alipayQr, bankCard, unionpayQr, phone, province, city, districtId, user_type, inviter, avatar]
- );
-
- const userId = result.insertId;
- if (user_type === 'agent_directly') {
- const agentCode = 'AG' + Date.now().toString().slice(-8);
- await db.execute(
- 'INSERT INTO regional_agents (user_id, region_id,status,agent_code) VALUES (?, ?,?,?)',
- [userId, districtId, 'active', agentCode]
- );
- await db.execute(
- `UPDATE users
- SET payment_status='paid'
- WHERE id = ${userId}`
- )
- await db.execute(`
- INSERT INTO distribution (user_id, amount, is_offline, type)
- VALUES (${userId}, 2980, 1, 'system')
- `)
- }
-
- // 用户余额已在创建用户时设置为默认值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: '用户创建成功',
- user: newUser[0]
- });
- } catch (error) {
- try {
- await getDB().query('ROLLBACK');
- } catch (rollbackError) {
- console.error('回滚错误:', rollbackError);
- }
- console.error('创建用户错误:', error);
- res.status(500).json({success: false, message: '创建用户失败'});
- }
-});
-
-
-router.get('/pending-audit', auth, adminAuth, async (req, res) => {
- try {
- const db = getDB();
- const {page = 1, limit = 10} = req.query;
- 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,
- wechat_qr,
- alipay_qr,
- unionpay_qr,
- bank_card,
- audit_status,
- created_at
- FROM users
- WHERE audit_status = ?
- ORDER BY created_at ASC
- LIMIT ${limitNum} OFFSET ${offset}`,
- ['pending']
- );
-
- res.json({
- success: true,
- data: {
- users,
- pagination: {
- page: pageNum,
- limit: limitNum,
- total,
- pages: Math.ceil(total / limitNum)
- }
- }
- });
- } catch (error) {
- console.error('获取待审核用户列表错误:', error);
- res.status(500).json({success: false, message: '获取待审核用户列表失败'});
- }
-});
-
-// 获取用户列表用于转账(普通用户权限)
-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
- });
- } catch (error) {
- console.error('获取转账用户列表错误:', error);
- res.status(500).json({success: false, message: '获取用户列表失败'});
- }
-});
-
-// 获取用户列表(管理员权限)
-router.get('/', auth, adminAuth, async (req, res) => {
- try {
- const db = getDB();
- const {
- page = 1,
- limit = 10,
- search = '',
- role = '',
- city = '',
- district = '',
- province = '',
- 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 (province) {
- whereConditions.push('u.province = ?');
- countParams.push(province);
- listParams.push(province);
- }
-
- 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(
- `SELECT COUNT(*) as total
- FROM users u
- LEFT JOIN zhejiang_regions r ON u.district_id = r.id
- ${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()
- : '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.province,
- u.city,
- u.district_id,
- u.id_card_front,
- u.id_card_back,
- u.business_license,
- u.is_distribute,
- u.user_type,
- u.inviter,
- p.name as province_name,
- c.name as city_name,
- d.name as district_name,
- COALESCE(yesterday_out.amount, 0) as yesterday_transfer_amount,
- COALESCE(today_in.amount, 0) as today_received_amount
- FROM users u
- LEFT JOIN china_regions p ON u.province = p.code
- LEFT JOIN china_regions c ON u.city = c.code
- LEFT JOIN china_regions d ON u.district_id = d.code
- LEFT JOIN (SELECT from_user_id, SUM(amount) as amount
- FROM transfers
- WHERE created_at >= DATE(DATE_SUB(NOW(), INTERVAL 1 DAY))
- AND created_at < DATE(NOW())
- AND status IN ('confirmed', 'received')
- GROUP BY from_user_id) yesterday_out ON u.id = yesterday_out.from_user_id
- LEFT JOIN (SELECT to_user_id, SUM(amount) as amount
- FROM transfers
- WHERE created_at >= DATE(NOW())
- AND created_at < DATE(DATE_ADD(NOW(), INTERVAL 1 DAY))
- AND status IN ('confirmed', 'received')
- GROUP BY to_user_id) today_in ON u.id = today_in.to_user_id
- ${whereClause}
- ORDER BY u.${sortField} ${sortOrder}
- LIMIT ${limitNum} OFFSET ${offset}`,
- listParams.slice(0, -2)
- );
- users.forEach(user => {
- user.region = [user.province, user.city, user.district_id]
- })
-
- res.json({
- success: true,
- users,
- total: countResult[0].total,
- page: pageNum,
- limit: limitNum,
- totalPages: Math.ceil(countResult[0].total / limitNum)
- });
- } catch (error) {
- console.error('获取用户列表错误:', error);
- res.status(500).json({success: false, message: '获取用户列表失败'});
- }
-});
-
-// 获取当前用户的个人资料
-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]
- );
- const [distribution] = await db.execute(`
- SELECT count(*) as total
- FROM distribution
- WHERE user_id = ?`, [userId])
- users[0].distribution = distribution[0].total > 0 ? true : false;
- if (users.length === 0) {
- return res.status(404).json({success: false, message: '用户不存在'});
- }
- const profile = {
- ...users[0],
- nickname: users[0].username, // 添加nickname字段,映射到username
- realName: users[0].real_name,
- idCard: users[0].id_card,
- wechatQr: users[0].wechat_qr,
- alipayQr: users[0].alipay_qr,
- bankCard: users[0].bank_card,
- unionpayQr: users[0].unionpay_qr,
- businessLicense: users[0].business_license,
- idCardFront: users[0].id_card_front,
- idCardBack: users[0].id_card_back
- };
- res.json({success: true, user: profile});
- } catch (error) {
- console.error('获取用户资料错误:', error);
- res.status(500).json({success: false, message: '获取用户资料失败'});
- }
-});
-
-/**
- * 获取当前用户的收款码状态
- * 用于检查用户是否已上传微信、支付宝、云闪付收款码
- */
-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,
- data: {
- wechat_qr: paymentCodes.wechat_qr || '',
- alipay_qr: paymentCodes.alipay_qr || '',
- unionpay_qr: paymentCodes.unionpay_qr || ''
- }
- });
- } catch (error) {
- console.error('获取收款码状态错误:', error);
- res.status(500).json({success: false, message: '获取收款码状态失败'});
- }
-});
-
-// 获取用户收款信息
-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,
- data: {
- id: user.id,
- username: user.username,
- wechat_qr: user.wechat_qr,
- alipay_qr: user.alipay_qr,
- unionpay_qr: user.unionpay_qr,
- bank_card: user.bank_card
- }
- });
- } catch (error) {
- console.error('获取用户收款信息错误:', error);
- res.status(500).json({success: false, message: '获取用户收款信息失败'});
- }
-});
-
-// 获取当前用户的统计信息
-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 [directly_operated] = await db.execute('SELECT COUNT(*) as count FROM users WHERE user_type = "directly_operated"');
- // 代理数量
- const [agent] = await db.execute('SELECT COUNT(*) as count FROM users WHERE user_type = "agent"');
- // 直营代理数量
- const [agent_directly] = await db.execute('SELECT COUNT(*) as count FROM users WHERE user_type = "agent_directly"');
- // 普通用户数量
- const [regularUsers] = await db.execute('SELECT COUNT(*) as count FROM users WHERE user_type = "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
- ? ((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 from_user_id) as count FROM transfers'
- );
- const [weekUsers] = await db.execute(`
- SELECT COUNT(DISTINCT from_user_id) as count
- FROM transfers
- WHERE DATE(created_at) = DATE_SUB(CURDATE(), INTERVAL 7 DAY)
- `)
- res.json({
- success: true,
- stats: {
- totalUsers: totalUsers[0].count,
- directly_operated: directly_operated[0].count,
- agent: agent[0].count,
- agent_directly: agent_directly[0].count,
- regularUsers: regularUsers[0].count,
- monthNewUsers: monthUsers[0].count,
- todayUsers: todayUsers[0].count,
- yesterdayUsers: yesterdayUsers[0].count,
- monthlyGrowth: parseFloat(monthGrowthRate),
- totalPoints: totalPoints[0].total,
- activeUsers: activeUsers[0].count,
- activeRate: (weekUsers[0].count / totalUsers[0].count * 100).toFixed(2)
- }
- });
- } else {
- // 普通用户返回个人统计
- // 用户订单数
- const [orderCount] = await db.execute(
- '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: {
- orderCount: orderCount[0].count,
- totalSpent: totalSpent[0].total,
- pointsEarned: pointsEarned[0].total,
- pointsSpent: pointsSpent[0].total,
- currentPoints: req.user.points || 0
- }
- });
- }
- } catch (error) {
- console.error('获取用户统计错误:', error);
- res.status(500).json({success: false, message: '获取用户统计失败'});
- }
-});
-
-// 获取用户统计信息(管理员权限)- 必须在/:id路由之前定义
-router.get('/admin/stats', auth, adminAuth, async (req, res) => {
- try {
- 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
- ? ((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: {
- totalUsers: totalUsers[0].count,
- adminUsers: adminUsers[0].count,
- regularUsers: regularUsers[0].count,
- monthNewUsers: monthUsers[0].count,
- todayUsers: todayUsers[0].count,
- yesterdayUsers: yesterdayUsers[0].count,
- monthlyGrowth: parseFloat(monthGrowthRate),
- totalPoints: totalPoints[0].total,
- activeUsers: activeUsers[0].count
- }
- });
- } catch (error) {
- console.error('获取用户统计错误:', error);
- res.status(500).json({success: false, message: '获取用户统计失败'});
- }
-});
-
-// 获取当前用户积分
-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
- });
- } catch (error) {
- console.error('获取用户积分错误:', error);
- res.status(500).json({success: false, message: '获取用户积分失败'});
- }
-});
-
-// 获取用户积分历史记录
-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 ${limitNum} OFFSET ${offset}`,
- queryParams
- );
-
- res.json({
- success: true,
- data: {
- records,
- pagination: {
- page: pageNum,
- limit: limitNum,
- total: countResult[0].total,
- totalPages: Math.ceil(countResult[0].total / limitNum)
- }
- }
- });
- } catch (error) {
- console.error('获取积分历史失败:', error);
- res.status(500).json({success: false, message: '获取积分历史失败'});
- }
-});
-
-// 获取用户增长趋势数据(管理员权限)
-router.get('/growth-trend', auth, adminAuth, async (req, res) => {
- try {
- 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 DATE(created_at) as date,
- COUNT(*) as count
- FROM users
- WHERE created_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
- 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
- });
- } catch (error) {
- console.error('获取用户增长趋势错误:', error);
- res.status(500).json({success: false, message: '获取用户增长趋势失败'});
- }
-});
-
-// 获取日收入统计数据(管理员权限)
-router.get('/daily-revenue', auth, adminAuth, async (req, res) => {
- try {
- 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 DATE(created_at) as date,
- COUNT(*) as user_count
- FROM users
- WHERE created_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
- GROUP BY DATE(created_at)
- ORDER BY date ASC
- `, [daysNum]);
- const [dailyDataTransfers] = await db.execute(`
- SELECT DATE(created_at) as date,
- SUM(CASE WHEN source_type IN ('system') THEN amount END) as amount
- FROM transfers
- WHERE created_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
- 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 existingDataTransfers = dailyDataTransfers.find(item => {
- const itemDateStr = dayjs(item.date).format('YYYY-MM-DD');
- return itemDateStr === dateStr;
- });
- const userCount = existingData ? existingData.user_count : 0;
- const revenue = existingDataTransfers && existingDataTransfers.amount !== null ? existingDataTransfers.amount : 0; // 每个用户398元收入
-
- result.push({
- date: dateDisplay,
- userCount: userCount,
- amount: revenue
- });
- }
-
- res.json({
- success: true,
- data: result
- });
- } catch (error) {
- console.error('获取日收入统计错误:', error);
- res.status(500).json({success: false, message: '获取日收入统计失败'});
- }
-});
-// 获取当前用户个人资料
-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 = {
- ...user,
- nickname: user.username, // 添加nickname字段,映射到username
- realName: user.real_name,
- idCard: user.id_card,
- wechatQr: user.wechat_qr,
- alipayQr: user.alipay_qr,
- bankCard: user.bank_card,
- unionpayQr: user.unionpay_qr,
- businessLicense: user.business_license,
- idCardFront: user.id_card_front,
- idCardBack: user.id_card_back,
- auditStatus: user.audit_status
- };
-
- res.json({success: true, user: profile});
- } catch (error) {
- console.error('获取用户个人资料错误:', error);
- res.status(500).json({success: false, message: '获取用户个人资料失败'});
- }
-});
-
-// 更新当前用户个人资料
-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,
- unionpayQr,
- businessLicense,
- idCardFront,
- idCardBack,
- 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);
- }
- if (idCard) {
- conditions.push('id_card = ?');
- checkValues.push(idCard);
- }
- if (phone) {
- 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]$/;
- if (!idCardRegex.test(idCard)) {
- 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 = {
- ...user,
- nickname: user.username, // 添加nickname字段,映射到username
- realName: user.real_name,
- idCard: user.id_card,
- wechatQr: user.wechat_qr,
- alipayQr: user.alipay_qr,
- bankCard: user.bank_card,
- unionpayQr: user.unionpay_qr,
- businessLicense: user.business_license,
- idCardFront: user.id_card_front,
- idCardBack: user.id_card_back,
- auditStatus: user.audit_status
- };
-
- res.json({
- success: true,
- message: '个人资料更新成功',
- data: profile
- });
- } catch (error) {
- console.error('更新个人资料错误:', error);
- res.status(500).json({success: false, message: '更新个人资料失败'});
- }
-});
-// 获取用户详情
-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);
- res.status(500).json({success: false, message: '获取用户详情失败'});
- }
-});
-
-// 更新用户信息
-router.put('/:id', auth, async (req, res) => {
- try {
- const db = getDB();
- const userId = req.params.id;
- const {
- username,
- password,
- role,
- isSystemAccount,
- avatar,
- realName,
- idCard,
- phone,
- wechatQr,
- alipayQr,
- bankCard,
- unionpayQr,
- idCardFront,
- idCardBack,
- businessLicense,
- province,
- city,
- districtId,
- user_type,
- inviter,
- } = 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);
- }
- if (idCard) {
- conditions.push('id_card = ?');
- checkValues.push(idCard);
- }
- if (phone) {
- 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]$/;
- if (!idCardRegex.test(idCard)) {
- 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) {
- 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 (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 (province !== undefined) {
- updateFields.push('province = ?');
- updateValues.push(province);
- needsReaudit = true;
- }
- if (businessLicense !== undefined) {
- updateFields.push('business_license = ?');
- updateValues.push(businessLicense);
- needsReaudit = true;
- }
- if (user_type !== undefined) {
- updateFields.push('user_type = ?');
- updateValues.push(user_type);
- needsReaudit = true;
- }
- if (inviter !== undefined) {
- updateFields.push('inviter = ?');
- updateValues.push(inviter);
- 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, province, created_at, updated_at FROM users WHERE id = ?',
- [userId]
- );
-
- res.json({
- success: true,
- message: '用户信息更新成功',
- user: updatedUsers[0]
- });
- } catch (error) {
- console.error('更新用户信息错误:', error);
- res.status(500).json({success: false, message: '更新用户信息失败'});
- }
-});
-
-
-// 删除用户(管理员权限)
-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);
- res.status(500).json({success: false, message: '删除用户失败'});
- }
-});
-
-/**
- * 审核用户(管理员权限)
- */
-router.put('/:id/audit', auth, adminAuth, async (req, res) => {
- try {
- 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(
- `UPDATE users
- SET audit_status = ?,
- audit_note = ?,
- audited_by = ?,
- audited_at = NOW()
- WHERE id = ?`,
- [auditStatus, note || null, req.user.id, userId]
- );
-
- const message = action === 'approve' ? '用户审核通过' : '用户审核拒绝';
- res.json({success: true, message});
- } catch (error) {
- console.error('审核用户错误:', error);
- res.status(500).json({success: false, message: '审核用户失败'});
- }
-});
-
-/**
- * 获取用户审核详情(管理员权限)
- */
-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,
- u.id_card_front,
- u.id_card_back,
- u.audit_status,
- u.audit_note,
- u.audited_at,
- u.created_at,
- auditor.username as auditor_name
- FROM users u
- LEFT JOIN users auditor ON u.audited_by = auditor.id
- WHERE u.id = ?`,
- [userId]
- );
-
- if (users.length === 0) {
- return res.status(404).json({success: false, message: '用户不存在'});
- }
-
- res.json({
- success: true,
- data: users[0]
- });
- } catch (error) {
- console.error('获取用户审核详情错误:', error);
- res.status(500).json({success: false, message: '获取用户审核详情失败'});
- }
-});
-
-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,user_type FROM users WHERE id = ?',
- [userId]
- );
-
- if (users.length === 0) {
- return res.status(404).json({success: false, message: '用户不存在'});
- }
- if (users[0].user_type === 'directly_operated') {
- return res.status(400).json({success: false, message: '直营用户不允许开启委托出售'});
- }
- let [isServiceFee] = await db.execute('SELECT COUNT(*) AS total FROM distribution WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 YEAR) AND user_id = ?', [userId]);
- if (isServiceFee[0].total > 0) {
- // 更新分发状态
- await db.execute(
- 'UPDATE users SET is_distribute = ? WHERE id = ?',
- [is_distribute, userId]
- );
- res.json({
- success: true,
- message: '分发状态更新成功',
- is_distribute
- });
- } else {
- return res.json({success: false, message: '请缴纳2980融豆服务费'});
- }
- } catch (error) {
-
- }
-})
-/**
- * 扣除2980融豆服务费
- */
-router.post('/:id/deduct-service-fee', auth, async (req, res) => {
- const db = getDB();
- try {
- const userId = req.params.id;
- const serviceFee = 2980; // 服务费金额
-
- // 开始事务
- await db.query('START TRANSACTION');
-
- // 使用行级锁定查询用户信息,只锁定当前用户记录
- const [users] = await db.execute(
- 'SELECT id, balance, username,district_id FROM users WHERE id = ? FOR UPDATE',
- [userId]
- );
-
- if (users.length === 0) {
- await db.query('ROLLBACK');
- return res.status(404).json({success: false, message: '用户不存在'});
- }
- //判断今年是否已扣款
- let [isServiceFee] = await db.execute('SELECT COUNT(*) AS total FROM distribution WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 YEAR) AND user_id = ?', [userId]);
- if (isServiceFee[0].total > 0) {
- return res.status(400).json({success: false, message: '已缴纳2980融豆服务费'});
- }
- const user = users[0];
- const currentBalance = Math.abs(user.balance); // 将负数转为正数处理
-
- // 检查融豆余额是否足够
- if (currentBalance < serviceFee) {
- await db.query('ROLLBACK');
- return res.status(400).json({
- success: false,
- message: `融豆余额不足,当前余额:${currentBalance},需要:${serviceFee}`
- });
- }
-
- // 扣除融豆(balance字段为负数,所以减去服务费实际是增加负数)
- await db.execute(
- 'UPDATE users SET balance = balance + ? WHERE id = ?',
- [serviceFee, userId]
- );
- //查找上级分销
- let [distribute] = await db.execute(
- 'SELECT inviter FROM users WHERE id = ?',
- [userId]
- );
- distribute = distribute[0]
- //如果有上级分销
- if (distribute.inviter) {
- // 查找上级分销
- let [distributeUser] = await db.execute(
- 'SELECT id, balance,user_type,inviter FROM users WHERE id = ?',
- [distribute.inviter]
- );
- distributeUser = distributeUser[0]
- if (distributeUser.user_type == 'agent') {
- //给代理添加2980融豆的70% 增加区域保护
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.7, distributeUser.id]
- );
- //记录转账记录
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.7, '用户服务费返现', 'agent']
- );
- //记录平台利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, 3512, 'user_to_system', 'received', serviceFee * 0.3, '用户服务费返现', 'system']
- );
- //记录服务费
- await db.execute(
- 'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
- [userId, distributeUser.id, serviceFee, 'agent']
- )
- }
- //如果不是代理,查看是否是直营代理
- if (distributeUser.user_type == 'agent_directly') {
- //给直营代理50%融豆给平台50%融豆
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.5, distributeUser.id]
- );
- //记录转账记录
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent_operated']
- );
- //记录平台利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
- );
- //记录服务费
- await db.execute(
- 'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
- [userId, distributeUser.id, serviceFee, 'direct_agent']
- )
- }
- //是否是直营
- if (distributeUser.user_type == 'directly_operated') {
- //查询这个月直营做了多少单
- let [orderCount] = await db.execute(
- `SELECT COUNT(*) AS total
- FROM distribution
- WHERE agent_id = ?
- AND created_at >= DATE_FORMAT(NOW(), '%Y-%m-01')`,
- [distributeUser.id]
- );
- orderCount = orderCount[0]
- if (orderCount.total <= 5) {
- //给直营代理20%融豆给平台50% 融豆给用户30%
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.2, distributeUser.inviter]
- );
- //给直营添加30%融豆
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.3, distributeUser.id]
- );
- //记录转账记录
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.2, '用户服务费返现', 'operated_agent']
- );
- //记录直营利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.3, '用户服务费返现', 'directly_operated']
- );
- //记录平台利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
- );
- }
- if (orderCount.total > 5 && orderCount.total <= 15) {
- //给直营代理20%融豆给平台50%融豆给用户30%
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.15, distributeUser.inviter]
- );
- //给直营添加30%融豆
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.35, distributeUser.id]
- );
- //记录转账记录
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.15, '用户服务费返现', 'agent_operated']
- );
- //记录直营利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.35, '用户服务费返现', 'directly_operated']
- );
- //记录平台利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
- );
- }
- if (orderCount.total > 15) {
- //给直营代理20%融豆给平台50%融豆给用户30%
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.1, distributeUser.inviter]
- );
- //给直营添加30%融豆
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.4, distributeUser.id]
- );
- //记录转账记录
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.inviter, 'user_to_agent', 'received', serviceFee * 0.1, '用户服务费返现', 'agent_operated']
- );
- //记录直营利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.id, 'user_to_operated', 'received', serviceFee * 0.4, '用户服务费返现', 'directly_operated']
- );
- //记录平台利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, 3512, 'user_to_system', 'received', serviceFee * 0.5, '用户服务费返现', 'system']
- );
- }
- //记录服务费
- await db.execute(
- 'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
- [userId, distributeUser.id, serviceFee, 'direct_agent']
- )
- }
- //要是用户之间分销
- if (distributeUser.user_type == 'user') {
- //查询用户是否有上级
- let [userUpInfo] = await db.execute(
- `SELECT *
- FROM users
- WHERE id = ?`,
- [distributeUser.inviter]
- )
- userUpInfo = userUpInfo[0]
- //判断用户上级是否是代理
- if (userUpInfo && userUpInfo.user_type === 'agent') {
- //给用户分配
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.2, distributeUser.id]
- );
- //给代理分配
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.5, userUpInfo.id]
- );
- //记录用户转账信息
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
- );
- //记录代理转账信息
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, userUpInfo.id, 'user_to_agent', 'received', serviceFee * 0.5, '用户服务费返现', 'agent']
- );
- //记录平台利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, 3512, 'user_to_system', 'received', serviceFee * 0.3, '用户服务费返现', 'system']
- );
- } else {
- //用户没有上级
- await db.execute(
- 'UPDATE users SET balance = balance - ? WHERE id = ?',
- [serviceFee * 0.2, distributeUser.id]
- );
- //记录转账记录
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, distributeUser.id, 'user_to_user', 'received', serviceFee * 0.2, '用户服务费返现', 'operated']
- );
- //记录平台利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, 3512, 'user_to_system', 'received', serviceFee * 0.8, '用户服务费返现', 'system']
- );
-
- }
- //记录服务费
- await db.execute(
- 'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
- [userId, distributeUser.id, serviceFee, 'user']
- )
- }
-
- } else {
- //判断用户此区域是否有代理
- let [agentUser] = await db.execute(`
- SELECT user_id
- FROM regional_agents
- WHERE region_id = ?
- `, [users[0].district_id])
- if (agentUser.length > 0) {
- //给代理分成5%
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, agentUser[0].user_id, 'user_to_regional', 'received', serviceFee * 0.05, '区域保护服务费返现', 'agent']
- )
- //记录平台利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, 3512, 'user_to_system', 'received', serviceFee * 0.95, '用户服务费返现', 'system']
- );
- } else {
- //记录平台利润
- await db.execute(
- 'INSERT INTO transfers (from_user_id, to_user_id, transfer_type,status,amount,description,source_type) VALUES (?, ?, ?,?,?,?,?)',
- [userId, 3512, 'user_to_system', 'received', serviceFee, '用户服务费返现', 'system']
- );
-
- }
- await db.execute(
- 'INSERT INTO distribution (user_id,agent_id, amount, type) VALUES (?, ?, ?,?)',
- [userId, 3512, serviceFee, 'user']
- )
-
- }
- await db.execute(
- 'UPDATE users SET is_distribute = ? WHERE id = ?',
- [true, userId]
- );
- // 提交事务
- await db.query('COMMIT');
-
- res.json({
- success: true,
- message: '服务费扣除成功',
- data: {
- user_id: userId,
- username: user.username,
- deducted_amount: serviceFee,
- remaining_balance: currentBalance - serviceFee
- }
- });
-
- } catch (error) {
- try {
- // 发生错误时回滚事务
- await db.query('ROLLBACK');
- } catch (rollbackError) {
- console.error('回滚失败:', rollbackError);
- }
- console.error('扣除服务费失败:', error);
- res.status(500).json({success: false, message: '扣除服务费失败'});
- }
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/routes/wechatPay.js b/routes/wechatPay.js
deleted file mode 100644
index 149e337..0000000
--- a/routes/wechatPay.js
+++ /dev/null
@@ -1,202 +0,0 @@
-const express = require('express');
-const router = express.Router();
-const WechatPayService = require('../services/wechatPayService');
-const { getDB } = require('../database');
-const { auth, paymentAuth } = require('../middleware/auth');
-
-// 创建微信支付服务实例
-const wechatPayService = new WechatPayService();
-
-/**
- * 创建注册支付订单 (API v3 - H5支付)
- * POST /api/wechat-pay/create-registration-order
- */
-router.post('/create-registration-order', paymentAuth, async (req, res) => {
- try {
- const userId = req.user.id;
- const username = req.user.username;
- const phone = req.user.phone;
-
- // 获取客户端IP
- const clientIp = req.headers['x-forwarded-for'] ||
- req.headers['x-real-ip'] ||
- req.connection.remoteAddress ||
- req.socket.remoteAddress ||
- (req.connection.socket ? req.connection.socket.remoteAddress : null) ||
- '127.0.0.1';
-
- // 检查用户是否已经支付过
- const db = getDB();
- const [existingOrders] = await db.execute(
- 'SELECT id FROM payment_orders WHERE user_id = ? AND status = "paid"',
- [userId]
- );
-
- if (existingOrders.length > 0) {
- return res.status(400).json({
- success: false,
- message: '用户已完成支付,无需重复支付'
- });
- }
-
- console.log('创建H5支付订单:', {
- userId,
- username,
- phone,
- clientIp
- });
-
- // 创建H5支付订单
- const result = await wechatPayService.createRegistrationPayOrder({
- userId,
- username,
- phone,
- clientIp
- });
-
- if (result.success) {
- res.json({
- success: true,
- data: {
- outTradeNo: result.data.outTradeNo,
- h5Url: result.data.h5Url,
- paymentType: result.data.paymentType
- }
- });
- } else {
- res.status(500).json({
- success: false,
- message: '创建支付订单失败'
- });
- }
- } catch (error) {
- console.error('创建H5支付订单异常:', error);
- res.status(500).json({
- success: false,
- message: error.message || '服务器内部错误'
- });
- }
-});
-
-// H5支付不需要获取openid,移除相关接口
-
-/**
- * 微信支付回调接口 (API v3)
- * POST /api/wechat-pay/notify
- */
-router.post('/notify', async (req, res) => {
- try {
- // API v3 回调是JSON格式
- const notifyData = req.body;
-
- // 获取请求头中的签名信息
- const signature = req.headers['wechatpay-signature'];
- const timestamp = req.headers['wechatpay-timestamp'];
- const nonce = req.headers['wechatpay-nonce'];
- const serial = req.headers['wechatpay-serial'];
-
- console.log('收到API v3支付回调:', {
- signature,
- timestamp,
- nonce,
- serial,
- body: notifyData
- });
-
- // 验证签名和处理回调
- const result = await wechatPayService.handleV3PaymentNotify({
- signature,
- timestamp,
- nonce,
- serial,
- body: JSON.stringify(notifyData)
- });
-
- if (result.success) {
- // API v3 成功响应
- res.status(200).json({ code: 'SUCCESS', message: '成功' });
- } else {
- // API v3 失败响应
- res.status(400).json({ code: 'FAIL', message: result.message || '处理失败' });
- }
- } catch (error) {
- console.error('支付回调处理异常:', error);
- res.status(500).json({ code: 'ERROR', message: '服务器内部错误' });
- }
-});
-
-/**
- * 查询支付状态
- * GET /api/wechat-pay/query-status/:outTradeNo
- */
-router.get('/query-status/:outTradeNo', paymentAuth, async (req, res) => {
- try {
- const { outTradeNo } = req.params;
- const userId = req.user.id;
-
- // 验证订单是否属于当前用户
- const db = getDB();
- const [orders] = await db.execute(
- 'SELECT id FROM payment_orders WHERE out_trade_no = ? AND user_id = ?',
- [outTradeNo, userId]
- );
-
- if (orders.length === 0) {
- return res.status(404).json({
- success: false,
- message: '订单不存在或无权限访问'
- });
- }
-
- const result = await wechatPayService.queryPaymentStatus(outTradeNo);
- res.json(result);
- } catch (error) {
- console.error('查询支付状态失败:', error);
- res.status(500).json({
- success: false,
- message: error.message || '查询支付状态失败'
- });
- }
-});
-
-/**
- * 检查用户支付状态
- * GET /api/wechat-pay/check-user-payment
- */
-router.get('/check-user-payment', auth, async (req, res) => {
- try {
- const userId = req.user.id;
- const db = getDB();
-
- // 查询用户支付状态
- const [users] = await db.execute(
- 'SELECT payment_status FROM users WHERE id = ?',
- [userId]
- );
-
- if (users.length === 0) {
- return res.status(404).json({
- success: false,
- message: '用户不存在'
- });
- }
-
- const paymentStatus = users[0].payment_status;
-
- res.json({
- success: true,
- data: {
- paymentStatus,
- isPaid: paymentStatus === 'paid'
- }
- });
- } catch (error) {
- console.error('检查用户支付状态失败:', error);
- res.status(500).json({
- success: false,
- message: '检查支付状态失败'
- });
- }
-});
-
-module.exports = router;
\ No newline at end of file
diff --git a/scripts/README_MERGE_TABLES.md b/scripts/README_MERGE_TABLES.md
deleted file mode 100644
index 4b5a980..0000000
--- a/scripts/README_MERGE_TABLES.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# 表合并说明文档
-
-## 概述
-
-本文档说明如何使用 `merge_tables.js` 脚本将 `order_allocations` 表数据合并到 `transfers` 表中,以实现数据库结构优化。
-
-## 背景
-
-原系统中,`order_allocations` 表和 `transfers` 表存在功能重叠,为了简化数据库结构和提高查询效率,我们决定将 `order_allocations` 表的数据合并到 `transfers` 表中,并通过 `source_type` 字段区分不同来源的转账记录。
-
-## 合并策略
-
-1. 为 `transfers` 表添加必要的字段,包括 `source_type`、`matching_order_id`、`cycle_number` 等
-2. 将 `order_allocations` 表中的数据迁移到 `transfers` 表
-3. 更新相关的外键引用
-
-## 使用方法
-
-### 前置条件
-
-1. 确保已对数据库进行备份
-2. 确保系统处于维护状态,没有用户正在使用
-
-### 执行步骤
-
-1. 进入项目根目录
-2. 执行以下命令运行合并脚本:
-
-```bash
-node scripts/merge_tables.js
-```
-
-3. 脚本执行完成后,检查控制台输出,确认迁移是否成功
-
-### 验证步骤
-
-1. 检查 `transfers` 表中是否包含所有 `order_allocations` 表的数据
-2. 验证系统功能是否正常,特别是与匹配订单相关的功能
-3. 确认无误后,可以考虑删除 `order_allocations` 表(可选)
-
-## 回滚方案
-
-如果合并过程中出现问题,或者合并后系统功能异常,可以通过以下步骤回滚:
-
-1. 使用之前的数据库备份恢复数据
-2. 如果没有备份,可以手动将 `transfers` 表中 `source_type='allocation'` 的记录删除,并重新运行原有的系统
-
-## 注意事项
-
-1. 合并过程会自动处理已有关联的记录,避免重复迁移
-2. 合并脚本使用事务进行操作,确保数据一致性
-3. 建议在测试环境验证成功后再在生产环境执行
-
-## 技术支持
-
-如有问题,请联系技术支持团队。
\ No newline at end of file
diff --git a/scripts/import_china_regions.js b/scripts/import_china_regions.js
deleted file mode 100644
index 99af78d..0000000
--- a/scripts/import_china_regions.js
+++ /dev/null
@@ -1,144 +0,0 @@
-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
deleted file mode 100644
index aaca902..0000000
--- a/scripts/pca-code.json
+++ /dev/null
@@ -1,14625 +0,0 @@
-[
- {
- "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/update_code_references.js b/scripts/update_code_references.js
deleted file mode 100644
index eb9a509..0000000
--- a/scripts/update_code_references.js
+++ /dev/null
@@ -1,273 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-
-/**
- * 代码更新脚本:将所有 order_allocations 表引用更新为 transfers 表
- *
- * 更新策略:
- * 1. 将 order_allocations 表名替换为 transfers
- * 2. 更新相关的字段映射和查询逻辑
- * 3. 添加必要的 WHERE 条件来过滤 allocation 类型的记录
- */
-
-class CodeUpdater {
- constructor() {
- this.filesToUpdate = [
- 'services/matchingService.js',
- 'routes/matchingAdmin.js',
- 'routes/transfers.js',
- 'routes/matching.js'
- ];
-
- // 字段映射关系
- this.fieldMappings = {
- // order_allocations 字段 -> transfers 字段
- 'matching_order_id': 'matching_order_id',
- 'from_user_id': 'from_user_id',
- 'to_user_id': 'to_user_id',
- 'amount': 'amount',
- 'cycle_number': 'cycle_number',
- 'status': 'status',
- 'transfer_id': 'id', // order_allocations.transfer_id 对应 transfers.id
- 'created_at': 'created_at',
- 'updated_at': 'updated_at',
- 'confirmed_at': 'confirmed_at',
- 'outbound_date': 'outbound_date',
- 'return_date': 'return_date',
- 'can_return_after': 'can_return_after'
- };
- }
-
- /**
- * 更新单个文件
- * @param {string} filePath - 文件路径
- */
- async updateFile(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. 替换表名
- content = content.replace(/\border_allocations\b/g, 'transfers');
-
- // 2. 添加 source_type 过滤条件
- content = this.addSourceTypeFilters(content);
-
- // 3. 更新 INSERT 语句
- content = this.updateInsertStatements(content);
-
- // 4. 更新特定的查询逻辑
- content = this.updateSpecificQueries(content, filePath);
-
- // 5. 更新注释
- content = this.updateComments(content);
-
- if (content !== originalContent) {
- // 创建备份
- fs.writeFileSync(fullPath + '.backup', originalContent);
-
- // 写入更新后的内容
- fs.writeFileSync(fullPath, content);
-
- console.log(`✓ 已更新: ${filePath}`);
- console.log(`✓ 备份已创建: ${filePath}.backup`);
- } else {
- console.log(`- 无需更新: ${filePath}`);
- }
- }
-
- /**
- * 添加 source_type 过滤条件
- * @param {string} content - 文件内容
- * @returns {string} 更新后的内容
- */
- addSourceTypeFilters(content) {
- // 在 FROM transfers 后添加 WHERE source_type = 'allocation' 条件
- // 但要避免重复添加
-
- // 匹配 FROM transfers 但不包含 source_type 的情况
- content = content.replace(
- /FROM transfers(?!.*source_type)([\s\S]*?)(?=WHERE|ORDER|GROUP|LIMIT|;|$)/gi,
- (match, afterFrom) => {
- // 如果已经有 WHERE 子句,添加 AND 条件
- if (afterFrom.includes('WHERE')) {
- return match.replace(/WHERE/, 'WHERE source_type = \'allocation\' AND');
- } else {
- // 如果没有 WHERE 子句,添加新的 WHERE 条件
- const beforeNextClause = match.match(/(ORDER|GROUP|LIMIT|;|$)/);
- if (beforeNextClause) {
- return match.replace(beforeNextClause[0], ` WHERE source_type = 'allocation' ${beforeNextClause[0]}`);
- } else {
- return match + " WHERE source_type = 'allocation'";
- }
- }
- }
- );
-
- return content;
- }
-
- /**
- * 更新 INSERT 语句
- * @param {string} content - 文件内容
- * @returns {string} 更新后的内容
- */
- updateInsertStatements(content) {
- // 更新 INSERT INTO transfers 语句,添加必要的字段
- content = content.replace(
- /INSERT INTO transfers\s*\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)/gi,
- (match, fields, values) => {
- // 如果字段列表中没有 source_type,添加它
- if (!fields.includes('source_type')) {
- const fieldList = fields.trim() + ', source_type';
- const valueList = values.trim() + ', \'allocation\'';
- return `INSERT INTO transfers (${fieldList}) VALUES (${valueList})`;
- }
- return match;
- }
- );
-
- return content;
- }
-
- /**
- * 更新特定的查询逻辑
- * @param {string} content - 文件内容
- * @param {string} filePath - 文件路径
- * @returns {string} 更新后的内容
- */
- updateSpecificQueries(content, filePath) {
- // 根据不同文件进行特定更新
-
- if (filePath.includes('matchingService.js')) {
- // 更新 matchingService.js 中的特定逻辑
- content = this.updateMatchingServiceQueries(content);
- }
-
- if (filePath.includes('matchingAdmin.js')) {
- // 更新 matchingAdmin.js 中的特定逻辑
- content = this.updateMatchingAdminQueries(content);
- }
-
- return content;
- }
-
- /**
- * 更新 matchingService.js 中的查询
- * @param {string} content - 文件内容
- * @returns {string} 更新后的内容
- */
- updateMatchingServiceQueries(content) {
- // 更新确认分配的逻辑
- content = content.replace(
- /UPDATE transfers SET status = "confirmed", transfer_id = \?, confirmed_at = NOW\(\) WHERE id = \?/g,
- 'UPDATE transfers SET status = "confirmed", confirmed_at = NOW() WHERE id = ? AND source_type = \'allocation\''
- );
-
- // 更新获取分配记录的查询
- content = content.replace(
- /SELECT \* FROM transfers WHERE id = \? AND from_user_id = \?/g,
- 'SELECT * FROM transfers WHERE id = ? AND from_user_id = ? AND source_type = \'allocation\''
- );
-
- return content;
- }
-
- /**
- * 更新 matchingAdmin.js 中的查询
- * @param {string} content - 文件内容
- * @returns {string} 更新后的内容
- */
- updateMatchingAdminQueries(content) {
- // 更新管理员查询逻辑,确保只查询 allocation 类型的记录
- content = content.replace(
- /FROM transfers oa/g,
- 'FROM transfers oa WHERE oa.source_type = \'allocation\''
- );
-
- return content;
- }
-
- /**
- * 更新注释
- * @param {string} content - 文件内容
- * @returns {string} 更新后的内容
- */
- updateComments(content) {
- content = content.replace(/order_allocations/g, 'transfers (allocation type)');
- content = content.replace(/订单分配/g, '转账分配');
- content = content.replace(/分配表/g, '转账表(分配类型)');
-
- return content;
- }
-
- /**
- * 执行所有文件的更新
- */
- async updateAllFiles() {
- console.log('开始更新代码引用...');
- console.log('=' .repeat(60));
-
- for (const filePath of this.filesToUpdate) {
- try {
- await this.updateFile(filePath);
- } catch (error) {
- console.error(`更新文件 ${filePath} 失败:`, error.message);
- }
- }
-
- console.log('\n' + '=' .repeat(60));
- console.log('✓ 代码更新完成!');
- console.log('\n注意事项:');
- console.log('1. 所有原始文件已备份为 .backup 文件');
- console.log('2. 请测试更新后的代码功能是否正常');
- console.log('3. 如有问题,可以使用备份文件恢复');
- console.log('4. 确认无误后可删除 .backup 文件');
- }
-
- /**
- * 恢复所有备份文件
- */
- async restoreBackups() {
- console.log('开始恢复备份文件...');
-
- for (const filePath of this.filesToUpdate) {
- const fullPath = path.join(process.cwd(), filePath);
- const backupPath = fullPath + '.backup';
-
- if (fs.existsSync(backupPath)) {
- fs.copyFileSync(backupPath, fullPath);
- console.log(`✓ 已恢复: ${filePath}`);
- }
- }
-
- console.log('✓ 备份恢复完成!');
- }
-}
-
-async function main() {
- const updater = new CodeUpdater();
-
- const args = process.argv.slice(2);
-
- if (args.includes('--restore')) {
- await updater.restoreBackups();
- } else {
- await updater.updateAllFiles();
- }
-}
-
-// 如果直接运行此脚本
-if (require.main === module) {
- main().catch(console.error);
-}
-
-module.exports = CodeUpdater;
\ No newline at end of file
diff --git a/scripts/verify_data.js b/scripts/verify_data.js
deleted file mode 100644
index 470682a..0000000
--- a/scripts/verify_data.js
+++ /dev/null
@@ -1,37 +0,0 @@
-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/scripts/verify_merge.js b/scripts/verify_merge.js
deleted file mode 100644
index e3e4995..0000000
--- a/scripts/verify_merge.js
+++ /dev/null
@@ -1,115 +0,0 @@
-const mysql = require('mysql2/promise');
-const { dbConfig } = require('../config/config');
-
-/**
- * 验证表合并结果的脚本
- * 检查 order_allocations 表和 transfers 表的数据一致性
- */
-async function verifyMerge() {
- console.log('开始验证表合并结果...');
- console.log('=' .repeat(60));
-
- let connection;
-
- try {
- // 创建数据库连接
- connection = await mysql.createConnection({
- host: dbConfig.host,
- user: dbConfig.user,
- password: dbConfig.password,
- database: dbConfig.database
- });
-
- // 1. 检查 order_allocations 表中有多少条记录
- const [allocationCount] = await connection.execute(
- 'SELECT COUNT(*) as count FROM order_allocations'
- );
- console.log(`order_allocations 表总记录数: ${allocationCount[0].count}`);
-
- // 2. 检查 transfers 表中有多少条 allocation 类型的记录
- const [transferCount] = await connection.execute(
- 'SELECT COUNT(*) as count FROM transfers WHERE source_type = \'allocation\''
- );
- console.log(`transfers 表中 allocation 类型记录数: ${transferCount[0].count}`);
-
- // 3. 检查 order_allocations 表中有多少条记录没有关联的 transfer_id
- const [unlinkedCount] = await connection.execute(
- 'SELECT COUNT(*) as count FROM order_allocations WHERE transfer_id IS NULL'
- );
- console.log(`order_allocations 表中无关联 transfer_id 的记录数: ${unlinkedCount[0].count}`);
-
- // 4. 检查数据一致性 - 抽样检查
- console.log('\n数据一致性检查(抽样):');
- const [sampleAllocations] = await connection.execute(
- 'SELECT * FROM order_allocations WHERE transfer_id IS NOT NULL LIMIT 5'
- );
-
- for (const allocation of sampleAllocations) {
- const [transfer] = await connection.execute(
- 'SELECT * FROM transfers WHERE id = ?',
- [allocation.transfer_id]
- );
-
- if (transfer.length === 0) {
- console.log(` ✗ 错误: allocation_id=${allocation.id} 关联的 transfer_id=${allocation.transfer_id} 不存在`);
- continue;
- }
-
- const transferRecord = transfer[0];
- const isConsistent =
- transferRecord.from_user_id == allocation.from_user_id &&
- transferRecord.to_user_id == allocation.to_user_id &&
- transferRecord.amount == allocation.amount &&
- transferRecord.matching_order_id == allocation.matching_order_id &&
- transferRecord.cycle_number == allocation.cycle_number;
-
- if (isConsistent) {
- console.log(` ✓ allocation_id=${allocation.id} 与 transfer_id=${allocation.transfer_id} 数据一致`);
- } else {
- console.log(` ✗ 错误: allocation_id=${allocation.id} 与 transfer_id=${allocation.transfer_id} 数据不一致`);
- console.log(' allocation:', {
- from_user_id: allocation.from_user_id,
- to_user_id: allocation.to_user_id,
- amount: allocation.amount,
- matching_order_id: allocation.matching_order_id,
- cycle_number: allocation.cycle_number
- });
- console.log(' transfer:', {
- from_user_id: transferRecord.from_user_id,
- to_user_id: transferRecord.to_user_id,
- amount: transferRecord.amount,
- matching_order_id: transferRecord.matching_order_id,
- cycle_number: transferRecord.cycle_number
- });
- }
- }
-
- console.log('\n' + '=' .repeat(60));
-
- // 总结
- if (allocationCount[0].count === transferCount[0].count && unlinkedCount[0].count === 0) {
- console.log('✓ 验证成功! 所有 order_allocations 记录都已正确迁移到 transfers 表');
- } else {
- console.log('⚠ 验证结果: 可能存在未完全迁移的数据');
- console.log(` - order_allocations 总数: ${allocationCount[0].count}`);
- console.log(` - transfers 中 allocation 类型数: ${transferCount[0].count}`);
- console.log(` - 未关联记录数: ${unlinkedCount[0].count}`);
- }
-
- } catch (error) {
- console.error('验证失败:', error);
- throw error;
- } finally {
- // 关闭数据库连接
- if (connection) {
- await connection.end();
- }
- }
-}
-
-// 如果直接运行此脚本
-if (require.main === module) {
- verifyMerge().catch(console.error);
-}
-
-module.exports = verifyMerge;
\ No newline at end of file
diff --git a/server.js b/server.js
index 61eeb6c..fe047e7 100644
--- a/server.js
+++ b/server.js
@@ -86,210 +86,22 @@ const limiter = rateLimit({
}
}
});
-app.use('/api', limiter);
-
-// 静态文件服务 - 必须在API路由之前
-// 为uploads路径配置CORS和静态文件服务
-app.use('/uploads', express.static(path.join(__dirname, 'uploads'), {
- setHeaders: (res, filePath) => {
- // 设置CORS头部
- res.setHeader('Access-Control-Allow-Origin', '*');
- res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
- res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
-
- // 设置缓存和内容类型
- if (filePath.endsWith('.jpg') || filePath.endsWith('.jpeg')) {
- res.setHeader('Content-Type', 'image/jpeg');
- } else if (filePath.endsWith('.png')) {
- res.setHeader('Content-Type', 'image/png');
- } else if (filePath.endsWith('.gif')) {
- res.setHeader('Content-Type', 'image/gif');
- } else if (filePath.endsWith('.webp')) {
- res.setHeader('Content-Type', 'image/webp');
- }
- res.setHeader('Cache-Control', 'public, max-age=86400'); // 1天缓存
- }
-}));
-
-// 处理vite.svg请求
-app.get('/vite.svg', (req, res) => {
- const referer = req.get('Referer');
- if (referer && referer.includes('/admin')) {
- // 为admin页面提供logo.svg
- res.setHeader('Content-Type', 'image/svg+xml');
- res.sendFile(path.join(__dirname, 'admin/dist/logo.svg'));
- } else {
- // 前端页面没有vite.svg,返回404
- res.status(404).send('File not found');
- }
-});
-
-// 静态文件服务配置
-// 专门处理admin路径下的assets
-app.use('/admin/assets', express.static(path.join(__dirname, 'admin/dist/assets'), {
- setHeaders: (res, filePath) => {
- res.removeHeader('Origin-Agent-Cluster');
- if (filePath.endsWith('.css')) {
- res.setHeader('Content-Type', 'text/css; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000');
- } else if (filePath.endsWith('.js')) {
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000');
- }
- }
-}));
-
-// 为admin页面的assets提供服务(当从admin页面访问/assets/时)
-app.use('/assets', (req, res, next) => {
- // 检查referer来判断是否来自admin页面
- const referer = req.get('Referer');
- if (referer && referer.includes('/admin')) {
- // 如果来自admin页面,从admin/dist/assets提供文件
- express.static(path.join(__dirname, 'admin/dist/assets'), {
- setHeaders: (res, filePath) => {
- res.removeHeader('Origin-Agent-Cluster');
- if (filePath.endsWith('.css')) {
- res.setHeader('Content-Type', 'text/css; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000');
- } else if (filePath.endsWith('.js')) {
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000');
- }
- }
- })(req, res, next);
- } else {
- // 否则从frontend/dist/assets提供文件
- express.static(path.join(__dirname, 'frontend/dist/assets'), {
- setHeaders: (res, filePath) => {
- res.removeHeader('Origin-Agent-Cluster');
- if (filePath.endsWith('.css')) {
- res.setHeader('Content-Type', 'text/css; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000');
- } else if (filePath.endsWith('.js')) {
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000');
- }
- }
- })(req, res, next);
- }
-});
-
-app.use('/admin', express.static(path.join(__dirname, 'admin/dist'), {
- setHeaders: (res, filePath) => {
- // 移除Origin-Agent-Cluster头部以避免冲突
- res.removeHeader('Origin-Agent-Cluster');
-
- if (filePath.endsWith('.css')) {
- res.setHeader('Content-Type', 'text/css; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000'); // 1年缓存
- } else if (filePath.endsWith('.js')) {
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000'); // 1年缓存
- } else if (filePath.endsWith('.html')) {
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); // HTML文件不缓存
- res.setHeader('Pragma', 'no-cache');
- res.setHeader('Expires', '0');
- } else if (filePath.endsWith('.svg')) {
- res.setHeader('Content-Type', 'image/svg+xml');
- }
- }
-}));
-
-app.use(express.static(path.join(__dirname, 'frontend/dist'), {
- setHeaders: (res, filePath) => {
- // 移除Origin-Agent-Cluster头部以避免冲突
- res.removeHeader('Origin-Agent-Cluster');
-
- if (filePath.endsWith('.css')) {
- res.setHeader('Content-Type', 'text/css; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000'); // 1年缓存
- } else if (filePath.endsWith('.js')) {
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
- res.setHeader('Cache-Control', 'public, max-age=31536000'); // 1年缓存
- } else if (filePath.endsWith('.html')) {
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); // HTML文件不缓存
- res.setHeader('Pragma', 'no-cache');
- res.setHeader('Expires', '0');
- } else if (filePath.endsWith('.svg')) {
- res.setHeader('Content-Type', 'image/svg+xml');
- }
- }
-}));
+app.use('/', limiter);
// 引入数据库初始化模块
const { initDatabase } = require('./config/database-init');
-// API路由
-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')); // 验证码路由
-app.use('/api/sms', require('./routes/sms')); // 短信验证路由
-
-app.use('/api/upload', require('./routes/upload'));
-app.use('/api/transfers', require('./routes/transfers'));
-app.use('/api/matching', require('./routes/matching'));
-app.use('/api/admin/matching', require('./routes/matchingAdmin'));
-app.use('/api/system', require('./routes/system'));
-app.use('/api/risk', require('./routes/riskManagement'));
-app.use('/api/agents', require('./routes/agents'));
-app.use('/api/admin/agents', require('./routes/agents/agents'));
-app.use('/api/admin/withdrawals', require('./routes/agents/withdrawals'));
-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'));
-app.use('/api/announcements', require('./routes/announcements')); // 通知公告路由
-app.use('/api/wechat-pay', require('./routes/wechatPay')); // 只保留微信支付
-app.use('/api/payment', require('./routes/payment'));
-
// 商城后台相关接口
-app.use('/api/shopbackend', require('./routes/shopbackend'));
-
-// 前端路由 - 必须在最后,作为fallback
-app.get('/', (req, res) => {
- res.removeHeader('Origin-Agent-Cluster');
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
- res.setHeader('Pragma', 'no-cache');
- res.setHeader('Expires', '0');
- res.sendFile(path.join(__dirname, 'frontend/dist/index.html'));
-});
-
-app.get('/admin*', (req, res) => {
- res.removeHeader('Origin-Agent-Cluster');
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
- res.setHeader('Pragma', 'no-cache');
- res.setHeader('Expires', '0');
- res.sendFile(path.join(__dirname, 'admin/dist/index.html'));
-});
-
-app.get('/frontend*', (req, res) => {
- res.removeHeader('Origin-Agent-Cluster');
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
- res.setHeader('Pragma', 'no-cache');
- res.setHeader('Expires', '0');
- res.sendFile(path.join(__dirname, 'frontend/dist/index.html'));
-});
-
-// SPA fallback - 处理前端路由
-app.get('*', (req, res) => {
- // 如果请求的是静态资源但找不到,返回404(不返回JSON)
- if (req.path.includes('.')) {
- return res.status(404).send('File not found');
- }
- // 否则返回前端应用的index.html
- res.removeHeader('Origin-Agent-Cluster');
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
- res.setHeader('Pragma', 'no-cache');
- res.setHeader('Expires', '0');
- res.sendFile(path.join(__dirname, 'frontend/dist/index.html'));
-});
+app.use('/shopbackend', require('./routes/shopbackend'));
+//订单管理接口
+app.use('/orders', require('./routes/orders'))
+//商品列表
+app.use('/products', require('./routes/products'))
+//供应商接口
+app.use('/supplier',require('./routes/supplier'))
+//规格处理
+app.use('/specifications', require('./routes/specifications'));
// 404处理
app.use(notFound);
@@ -309,16 +121,6 @@ app.listen(PORT, async () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
await initDatabase();
- // global.sqlReq = mysql.createConnection()
- // 启动转账超时检查服务
- const timeoutService = require('./services/timeoutService');
- timeoutService.startTimeoutChecker();
- console.log('转账超时检查服务已启动');
-
- // 启动数据库连接监控
- // const dbMonitor = require('./db-monitor');
- // dbMonitor.startMonitoring(60000); // 每分钟监控一次
- // console.log('数据库连接监控已启动');
global.captchaStore = new Map();
logger.info('Server started successfully', {
port: PORT,
diff --git a/services/alipayservice.js b/services/alipayservice.js
deleted file mode 100644
index 1e9385c..0000000
--- a/services/alipayservice.js
+++ /dev/null
@@ -1,306 +0,0 @@
-const { AlipaySdk } = require('alipay-sdk');
-const { getDB } = require('../database');
-const crypto = require('crypto');
-const path = require('path');
-const fs = require('fs');
-
-class AlipayService {
- constructor() {
- this.privateKey = null;
- this.alipayPublicKey = null;
- this.alipaySdk = null;
- this.isInitialized = false;
-
- this.initializeAlipay();
- }
-
- /**
- * 初始化支付宝服务
- */
- initializeAlipay() {
- try {
- // 读取密钥文件
- const privateKeyPath = this.resolveCertPath('../certs/alipay-private-key.pem');
- const publicKeyPath = this.resolveCertPath('../certs/alipay-public-key.pem');
-
- console.log('支付宝私钥路径:', privateKeyPath);
- console.log('支付宝公钥路径:', publicKeyPath);
- this.privateKey = fs.readFileSync(privateKeyPath, 'utf8');
- this.alipayPublicKey = fs.readFileSync(publicKeyPath, 'utf8');
- this.initializeSDK();
-
- } catch (error) {
- console.error('支付宝服务初始化失败:', error.message);
- console.error('支付宝功能将不可用');
- // 不抛出错误,允许服务继续运行
- }
- }
-
- /**
- * 初始化支付宝SDK
- */
- initializeSDK() {
- if (!this.privateKey || !this.alipayPublicKey) {
- console.warn('支付宝密钥未加载,跳过SDK初始化');
- return;
- }
-
- // 支付宝配置
- this.config = {
- appId: process.env.ALIPAY_APP_ID || '2021001161683774', // 替换为实际的应用ID
- privateKey: this.privateKey, // 从文件读取的应用私钥
- alipayPublicKey: this.alipayPublicKey, // 从文件读取的支付宝公钥
- gateway: 'https://openapi.alipay.com/gateway.do', // 支付宝网关地址
- signType: 'RSA2',
- charset: 'utf-8',
- version: '1.0',
- timeout: 5000
- };
-
- // 初始化支付宝SDK
- this.alipaySdk = new AlipaySdk({
- appId: this.config.appId,
- privateKey: this.config.privateKey,
- alipayPublicKey: this.config.alipayPublicKey,
- gateway: this.config.gateway,
- signType: this.config.signType,
- timeout: this.config.timeout
- });
-
- this.isInitialized = true;
- console.log('支付宝SDK初始化成功');
- }
-
- /**
- * 解析证书文件路径
- * @param {string} relativePath - 相对路径
- * @returns {string} 绝对路径
- */
- resolveCertPath(relativePath) {
- return path.resolve(__dirname, relativePath);
- }
-
- /**
- * 验证文件是否有效
- * @param {string} filePath - 文件路径
- * @returns {boolean} 是否为有效文件
- */
- isValidFile(filePath) {
- try {
- const stats = fs.statSync(filePath);
- return stats.isFile();
- } catch (error) {
- return false;
- }
- }
-
- /**
- * 检查支付宝服务是否已初始化
- * @returns {boolean} 是否已初始化
- */
- isServiceAvailable() {
- return this.isInitialized && this.alipaySdk !== null;
- }
-
- /**
- * 创建注册支付订单
- * @param {Object} params - 支付参数
- * @param {string} params.userId - 用户ID
- * @param {string} params.username - 用户名
- * @param {string} params.phone - 手机号
- * @param {string} params.clientIp - 客户端IP
- * @returns {Promise