docs: 更新项目文档,完善需求和技术细节
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
const swaggerJsdoc = require('swagger-jsdoc');
|
||||
const swaggerJSDoc = require('swagger-jsdoc');
|
||||
const swaggerUi = require('swagger-ui-express');
|
||||
|
||||
// Swagger 配置选项
|
||||
const options = {
|
||||
definition: {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
title: '爱鉴花小程序后端API',
|
||||
title: '爱鉴花 API 文档',
|
||||
version: '1.0.0',
|
||||
description: '爱鉴花小程序后端RESTful API文档',
|
||||
description: '爱鉴花小程序和后端管理系统的 RESTful API 文档',
|
||||
contact: {
|
||||
name: 'API支持',
|
||||
name: '技术支持',
|
||||
email: 'support@aijianhua.com'
|
||||
},
|
||||
license: {
|
||||
@@ -19,15 +20,17 @@ const options = {
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: process.env.NODE_ENV === 'production'
|
||||
? 'https://api.aijianhua.com'
|
||||
: `http://localhost:${process.env.PORT || 3200}`,
|
||||
description: process.env.NODE_ENV === 'production' ? '生产环境' : '开发环境'
|
||||
url: 'http://localhost:3200/api/v1',
|
||||
description: '开发环境'
|
||||
},
|
||||
{
|
||||
url: 'https://api.aijianhua.com/api/v1',
|
||||
description: '生产环境'
|
||||
}
|
||||
],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
BearerAuth: {
|
||||
bearerAuth: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT'
|
||||
@@ -37,117 +40,286 @@ const options = {
|
||||
User: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'integer', example: 1 },
|
||||
username: { type: 'string', example: 'testuser' },
|
||||
phone: { type: 'string', example: '13800138000' },
|
||||
email: { type: 'string', example: 'user@example.com' },
|
||||
user_type: { type: 'string', enum: ['farmer', 'buyer', 'admin'], example: 'farmer' },
|
||||
avatar_url: { type: 'string', example: '/uploads/avatars/avatar.jpg' },
|
||||
created_at: { type: 'string', format: 'date-time', example: '2023-01-01T00:00:00Z' },
|
||||
last_login: { type: 'string', format: 'date-time', example: '2023-01-01T00:00:00Z' }
|
||||
id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
username: {
|
||||
type: 'string',
|
||||
example: 'user123'
|
||||
},
|
||||
phone: {
|
||||
type: 'string',
|
||||
example: '13800138000'
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
example: 'user@example.com'
|
||||
},
|
||||
user_type: {
|
||||
type: 'string',
|
||||
example: 'farmer'
|
||||
},
|
||||
avatar_url: {
|
||||
type: 'string',
|
||||
example: 'https://example.com/avatar.jpg'
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-01-01T00:00:00Z'
|
||||
},
|
||||
last_login: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-01-01T00:00:00Z'
|
||||
}
|
||||
}
|
||||
},
|
||||
Product: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'integer', example: 1 },
|
||||
name: { type: 'string', example: '玫瑰花' },
|
||||
category_id: { type: 'integer', example: 1 },
|
||||
price: { type: 'number', format: 'float', example: 29.9 },
|
||||
stock: { type: 'integer', example: 100 },
|
||||
image: { type: 'string', example: '/uploads/products/rose.jpg' },
|
||||
description: { type: 'string', example: '新鲜玫瑰花,香气浓郁' },
|
||||
status: { type: 'integer', enum: [0, 1], example: 1 },
|
||||
created_at: { type: 'string', format: 'date-time', example: '2023-01-01T00:00:00Z' },
|
||||
updated_at: { type: 'string', format: 'date-time', example: '2023-01-01T00:00:00Z' }
|
||||
id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
example: '玫瑰花'
|
||||
},
|
||||
category_id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
category_name: {
|
||||
type: 'string',
|
||||
example: '鲜花'
|
||||
},
|
||||
price: {
|
||||
type: 'number',
|
||||
example: 29.9
|
||||
},
|
||||
stock: {
|
||||
type: 'integer',
|
||||
example: 100
|
||||
},
|
||||
image: {
|
||||
type: 'string',
|
||||
example: 'https://example.com/product.jpg'
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
example: '新鲜玫瑰花'
|
||||
},
|
||||
status: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-01-01T00:00:00Z'
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-01-01T00:00:00Z'
|
||||
}
|
||||
}
|
||||
},
|
||||
Order: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'integer', example: 1 },
|
||||
order_number: { type: 'string', example: 'O123456789' },
|
||||
user_id: { type: 'integer', example: 1 },
|
||||
total_amount: { type: 'number', format: 'float', example: 99.9 },
|
||||
payment_status: { type: 'string', enum: ['pending', 'paid', 'cancelled'], example: 'pending' },
|
||||
shipping_status: { type: 'string', enum: ['pending', 'shipped', 'delivered'], example: 'pending' },
|
||||
shipping_address: { type: 'string', example: '北京市朝阳区xxx街道' },
|
||||
created_at: { type: 'string', format: 'date-time', example: '2023-01-01T00:00:00Z' },
|
||||
updated_at: { type: 'string', format: 'date-time', example: '2023-01-01T00:00:00Z' }
|
||||
}
|
||||
},
|
||||
Identification: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'integer', example: 1 },
|
||||
user_id: { type: 'integer', example: 1 },
|
||||
image_url: { type: 'string', example: '/uploads/identifications/identify-123.jpg' },
|
||||
result: { type: 'string', description: 'JSON格式的识别结果' },
|
||||
confidence: { type: 'number', format: 'float', example: 0.95 },
|
||||
created_at: { type: 'string', format: 'date-time', example: '2023-01-01T00:00:00Z' }
|
||||
}
|
||||
},
|
||||
Error: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
code: { type: 'integer', example: 400 },
|
||||
message: { type: 'string', example: '参数验证失败' },
|
||||
data: { type: 'object', nullable: true, example: null }
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
UnauthorizedError: {
|
||||
description: '未授权访问',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: { $ref: '#/components/schemas/Error' },
|
||||
example: {
|
||||
code: 401,
|
||||
message: '未提供有效的认证token',
|
||||
data: null
|
||||
}
|
||||
id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
order_number: {
|
||||
type: 'string',
|
||||
example: 'ORD202301010001'
|
||||
},
|
||||
user_id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
total_amount: {
|
||||
type: 'number',
|
||||
example: 99.9
|
||||
},
|
||||
payment_status: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
shipping_status: {
|
||||
type: 'integer',
|
||||
example: 0
|
||||
},
|
||||
shipping_address: {
|
||||
type: 'string',
|
||||
example: '北京市朝阳区xxx街道xxx号'
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-01-01T00:00:00Z'
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-01-01T00:00:00Z'
|
||||
}
|
||||
}
|
||||
},
|
||||
NotFoundError: {
|
||||
description: '资源不存在',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: { $ref: '#/components/schemas/Error' },
|
||||
example: {
|
||||
code: 404,
|
||||
message: '资源不存在',
|
||||
data: null
|
||||
}
|
||||
OrderItem: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
order_id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
product_id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
quantity: {
|
||||
type: 'integer',
|
||||
example: 2
|
||||
},
|
||||
price: {
|
||||
type: 'number',
|
||||
example: 29.9
|
||||
}
|
||||
}
|
||||
},
|
||||
ValidationError: {
|
||||
description: '参数验证失败',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: { $ref: '#/components/schemas/Error' },
|
||||
example: {
|
||||
code: 400,
|
||||
message: '参数验证失败',
|
||||
data: null
|
||||
}
|
||||
CartItem: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
user_id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
product_id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
product_name: {
|
||||
type: 'string',
|
||||
example: '玫瑰花'
|
||||
},
|
||||
product_image: {
|
||||
type: 'string',
|
||||
example: 'https://example.com/product.jpg'
|
||||
},
|
||||
price: {
|
||||
type: 'number',
|
||||
example: 29.9
|
||||
},
|
||||
quantity: {
|
||||
type: 'integer',
|
||||
example: 2
|
||||
},
|
||||
stock: {
|
||||
type: 'integer',
|
||||
example: 100
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-01-01T00:00:00Z'
|
||||
},
|
||||
updated_at: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-01-01T00:00:00Z'
|
||||
}
|
||||
}
|
||||
},
|
||||
Address: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
user_id: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
recipient: {
|
||||
type: 'string',
|
||||
example: '张三'
|
||||
},
|
||||
phone: {
|
||||
type: 'string',
|
||||
example: '13800138000'
|
||||
},
|
||||
province: {
|
||||
type: 'string',
|
||||
example: '北京市'
|
||||
},
|
||||
city: {
|
||||
type: 'string',
|
||||
example: '北京市'
|
||||
},
|
||||
district: {
|
||||
type: 'string',
|
||||
example: '朝阳区'
|
||||
},
|
||||
detail: {
|
||||
type: 'string',
|
||||
example: 'xxx街道xxx号'
|
||||
},
|
||||
is_default: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
created_at: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
example: '2023-01-01T00:00:00Z'
|
||||
}
|
||||
}
|
||||
},
|
||||
Pagination: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
page: {
|
||||
type: 'integer',
|
||||
example: 1
|
||||
},
|
||||
limit: {
|
||||
type: 'integer',
|
||||
example: 10
|
||||
},
|
||||
total: {
|
||||
type: 'integer',
|
||||
example: 100
|
||||
},
|
||||
pages: {
|
||||
type: 'integer',
|
||||
example: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
security: [
|
||||
{ BearerAuth: [] }
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
]
|
||||
},
|
||||
apis: [
|
||||
'./routes/*.js',
|
||||
'./middlewares/*.js'
|
||||
]
|
||||
apis: ['./routes/*.js'] // 扫描路由文件中的 Swagger 注释
|
||||
};
|
||||
|
||||
const specs = swaggerJsdoc(options);
|
||||
const specs = swaggerJSDoc(options);
|
||||
|
||||
module.exports = { specs, swaggerUi };
|
||||
230
backend/package-lock.json
generated
230
backend/package-lock.json
generated
@@ -22,6 +22,7 @@
|
||||
"redis": "^4.6.8",
|
||||
"socket.io": "^4.7.2",
|
||||
"sqlite3": "^5.1.7",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^4.6.3",
|
||||
"validator": "^13.11.0",
|
||||
"yamljs": "^0.3.0"
|
||||
@@ -33,6 +34,50 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@apidevtools/json-schema-ref-parser": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz",
|
||||
"integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jsdevtools/ono": "^7.1.3",
|
||||
"@types/json-schema": "^7.0.6",
|
||||
"call-me-maybe": "^1.0.1",
|
||||
"js-yaml": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@apidevtools/openapi-schemas": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz",
|
||||
"integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@apidevtools/swagger-methods": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz",
|
||||
"integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@apidevtools/swagger-parser": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz",
|
||||
"integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apidevtools/json-schema-ref-parser": "^9.0.6",
|
||||
"@apidevtools/openapi-schemas": "^2.0.4",
|
||||
"@apidevtools/swagger-methods": "^3.0.2",
|
||||
"@jsdevtools/ono": "^7.1.3",
|
||||
"call-me-maybe": "^1.0.1",
|
||||
"z-schema": "^5.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openapi-types": ">=7"
|
||||
}
|
||||
},
|
||||
"node_modules/@gar/promisify": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@gar/promisify/-/promisify-1.1.3.tgz",
|
||||
@@ -40,6 +85,12 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@jsdevtools/ono": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/@jsdevtools/ono/-/ono-7.1.3.tgz",
|
||||
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@npmcli/fs": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/@npmcli/fs/-/fs-1.1.1.tgz",
|
||||
@@ -170,6 +221,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.11",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.11.tgz",
|
||||
@@ -340,6 +397,12 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
@@ -636,6 +699,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/call-me-maybe": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
|
||||
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
|
||||
@@ -712,6 +781,15 @@
|
||||
"color-support": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-6.2.0.tgz",
|
||||
"integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/compressible": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz",
|
||||
@@ -902,6 +980,18 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
|
||||
"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"esutils": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz",
|
||||
@@ -1105,6 +1195,15 @@
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/esutils": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
|
||||
@@ -1736,6 +1835,18 @@
|
||||
"license": "ISC",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||
@@ -1785,6 +1896,13 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.get": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
|
||||
"deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
@@ -1797,6 +1915,13 @@
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
@@ -1821,6 +1946,12 @@
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.mergewith": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
|
||||
"integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
@@ -2450,6 +2581,13 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/openapi-types": {
|
||||
"version": "12.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/openapi-types/-/openapi-types-12.1.3.tgz",
|
||||
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/p-map": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz",
|
||||
@@ -3272,6 +3410,59 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-jsdoc": {
|
||||
"version": "6.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz",
|
||||
"integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "6.2.0",
|
||||
"doctrine": "3.0.0",
|
||||
"glob": "7.1.6",
|
||||
"lodash.mergewith": "^4.6.2",
|
||||
"swagger-parser": "^10.0.3",
|
||||
"yaml": "2.0.0-1"
|
||||
},
|
||||
"bin": {
|
||||
"swagger-jsdoc": "bin/swagger-jsdoc.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-jsdoc/node_modules/glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-parser": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/swagger-parser/-/swagger-parser-10.0.3.tgz",
|
||||
"integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-parser": "10.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-ui-dist": {
|
||||
"version": "5.28.0",
|
||||
"resolved": "https://registry.npmmirror.com/swagger-ui-dist/-/swagger-ui-dist-5.28.0.tgz",
|
||||
@@ -3588,6 +3779,15 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.0.0-1",
|
||||
"resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.0.0-1.tgz",
|
||||
"integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/yamljs": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/yamljs/-/yamljs-0.3.0.tgz",
|
||||
@@ -3610,6 +3810,36 @@
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/z-schema": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/z-schema/-/z-schema-5.0.5.tgz",
|
||||
"integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"validator": "^13.7.0"
|
||||
},
|
||||
"bin": {
|
||||
"z-schema": "bin/z-schema"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"commander": "^9.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/z-schema/node_modules/commander": {
|
||||
"version": "9.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/commander/-/commander-9.5.0.tgz",
|
||||
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || >=14"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"redis": "^4.6.8",
|
||||
"socket.io": "^4.7.2",
|
||||
"sqlite3": "^5.1.7",
|
||||
"swagger-jsdoc": "^6.2.8",
|
||||
"swagger-ui-express": "^4.6.3",
|
||||
"validator": "^13.11.0",
|
||||
"yamljs": "^0.3.0"
|
||||
|
||||
@@ -2,6 +2,61 @@ const express = require('express');
|
||||
const router = express.Router();
|
||||
const dbConnector = require('../utils/dbConnector');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/addresses:
|
||||
* get:
|
||||
* summary: 获取用户收货地址列表
|
||||
* description: 获取当前用户的所有收货地址,按默认地址和创建时间排序
|
||||
* tags:
|
||||
* - 地址管理
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取收货地址列表
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 获取成功
|
||||
* data:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Address'
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 500
|
||||
* message:
|
||||
* type: string
|
||||
* example: 服务器内部错误
|
||||
*/
|
||||
// 获取用户收货地址列表
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
@@ -29,12 +84,132 @@ router.get('/', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/addresses:
|
||||
* post:
|
||||
* summary: 添加收货地址
|
||||
* description: 为当前用户添加新的收货地址
|
||||
* tags:
|
||||
* - 地址管理
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - recipient
|
||||
* - phone
|
||||
* - province
|
||||
* - city
|
||||
* - district
|
||||
* - detail
|
||||
* properties:
|
||||
* recipient:
|
||||
* type: string
|
||||
* example: "张三"
|
||||
* description: 收货人姓名
|
||||
* phone:
|
||||
* type: string
|
||||
* example: "13800138000"
|
||||
* description: 收货人手机号
|
||||
* province:
|
||||
* type: string
|
||||
* example: "北京市"
|
||||
* description: 省份
|
||||
* city:
|
||||
* type: string
|
||||
* example: "北京市"
|
||||
* description: 城市
|
||||
* district:
|
||||
* type: string
|
||||
* example: "朝阳区"
|
||||
* description: 区/县
|
||||
* detail:
|
||||
* type: string
|
||||
* example: "xxx街道xxx号"
|
||||
* description: 详细地址
|
||||
* is_default:
|
||||
* type: integer
|
||||
* example: 1
|
||||
* description: 是否为默认地址 (0-否, 1-是)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功添加收货地址
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 添加成功
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* address_id:
|
||||
* type: integer
|
||||
* example: 1
|
||||
* 400:
|
||||
* description: 请求参数错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 400
|
||||
* message:
|
||||
* type: string
|
||||
* example: 收货人姓名、手机号和地址信息不能为空
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 500
|
||||
* message:
|
||||
* type: string
|
||||
* example: 服务器内部错误
|
||||
*/
|
||||
// 添加收货地址
|
||||
router.post('/', async (req, res) => {
|
||||
try {
|
||||
const { recipient, phone, province, city, district, detail, is_default } = req.body;
|
||||
const userId = req.user.id;
|
||||
|
||||
// 验证必填字段
|
||||
if (!recipient || !phone || !province || !city || !district || !detail) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
message: '收货人姓名、手机号和地址信息不能为空'
|
||||
});
|
||||
}
|
||||
|
||||
// 如果设置为默认地址,先取消其他默认地址
|
||||
if (is_default) {
|
||||
await dbConnector.query(
|
||||
@@ -50,8 +225,8 @@ router.post('/', async (req, res) => {
|
||||
[userId, recipient, phone, province, city, district, detail, is_default || 0]
|
||||
);
|
||||
|
||||
res.status(201).json({
|
||||
code: 201,
|
||||
res.json({
|
||||
code: 200,
|
||||
message: '添加成功',
|
||||
data: {
|
||||
address_id: result.insertId
|
||||
@@ -67,6 +242,125 @@ router.post('/', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/addresses/{id}:
|
||||
* put:
|
||||
* summary: 更新收货地址
|
||||
* description: 更新当前用户的指定收货地址
|
||||
* tags:
|
||||
* - 地址管理
|
||||
* 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:
|
||||
* type: string
|
||||
* example: "张三"
|
||||
* description: 收货人姓名
|
||||
* phone:
|
||||
* type: string
|
||||
* example: "13800138000"
|
||||
* description: 收货人手机号
|
||||
* province:
|
||||
* type: string
|
||||
* example: "北京市"
|
||||
* description: 省份
|
||||
* city:
|
||||
* type: string
|
||||
* example: "北京市"
|
||||
* description: 城市
|
||||
* district:
|
||||
* type: string
|
||||
* example: "朝阳区"
|
||||
* description: 区/县
|
||||
* detail:
|
||||
* type: string
|
||||
* example: "xxx街道xxx号"
|
||||
* description: 详细地址
|
||||
* is_default:
|
||||
* type: integer
|
||||
* example: 1
|
||||
* description: 是否为默认地址 (0-否, 1-是)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功更新收货地址
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 更新成功
|
||||
* 400:
|
||||
* description: 请求参数错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 400
|
||||
* message:
|
||||
* type: string
|
||||
* example: 收货人姓名、手机号和地址信息不能为空
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
* 404:
|
||||
* description: 地址不存在
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 404
|
||||
* message:
|
||||
* type: string
|
||||
* example: 地址不存在
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 500
|
||||
* message:
|
||||
* type: string
|
||||
* example: 服务器内部错误
|
||||
*/
|
||||
// 更新收货地址
|
||||
router.put('/:id', async (req, res) => {
|
||||
try {
|
||||
@@ -74,13 +368,21 @@ router.put('/:id', async (req, res) => {
|
||||
const { recipient, phone, province, city, district, detail, is_default } = req.body;
|
||||
const userId = req.user.id;
|
||||
|
||||
// 检查地址是否存在
|
||||
const address = await dbConnector.query(
|
||||
// 验证必填字段
|
||||
if (!recipient || !phone || !province || !city || !district || !detail) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
message: '收货人姓名、手机号和地址信息不能为空'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查地址是否存在且属于当前用户
|
||||
const existingAddress = await dbConnector.query(
|
||||
'SELECT * FROM addresses WHERE id = ? AND user_id = ?',
|
||||
[id, userId]
|
||||
);
|
||||
|
||||
if (address.length === 0) {
|
||||
if (existingAddress.length === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
message: '地址不存在'
|
||||
@@ -96,10 +398,11 @@ router.put('/:id', async (req, res) => {
|
||||
}
|
||||
|
||||
await dbConnector.query(
|
||||
`UPDATE addresses SET
|
||||
recipient = ?, phone = ?, province = ?, city = ?, district = ?, detail = ?, is_default = ?, updated_at = NOW()
|
||||
WHERE id = ? AND user_id = ?`,
|
||||
[recipient, phone, province, city, district, detail, is_default || 0, id, userId]
|
||||
`UPDATE addresses
|
||||
SET recipient = ?, phone = ?, province = ?, city = ?, district = ?, detail = ?,
|
||||
is_default = ?, updated_at = NOW()
|
||||
WHERE id = ?`,
|
||||
[recipient, phone, province, city, district, detail, is_default || 0, id]
|
||||
);
|
||||
|
||||
res.json({
|
||||
@@ -116,17 +419,99 @@ router.put('/:id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/addresses/{id}:
|
||||
* delete:
|
||||
* summary: 删除收货地址
|
||||
* description: 删除当前用户的指定收货地址
|
||||
* tags:
|
||||
* - 地址管理
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 地址ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功删除收货地址
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 删除成功
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
* 404:
|
||||
* description: 地址不存在
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 404
|
||||
* message:
|
||||
* type: string
|
||||
* example: 地址不存在
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 500
|
||||
* message:
|
||||
* type: string
|
||||
* example: 服务器内部错误
|
||||
*/
|
||||
// 删除收货地址
|
||||
router.delete('/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const userId = req.user.id;
|
||||
|
||||
await dbConnector.query(
|
||||
'DELETE FROM addresses WHERE id = ? AND user_id = ?',
|
||||
// 检查地址是否存在且属于当前用户
|
||||
const existingAddress = await dbConnector.query(
|
||||
'SELECT * FROM addresses WHERE id = ? AND user_id = ?',
|
||||
[id, userId]
|
||||
);
|
||||
|
||||
if (existingAddress.length === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
message: '地址不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 删除地址
|
||||
await dbConnector.query('DELETE FROM addresses WHERE id = ?', [id]);
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
message: '删除成功'
|
||||
@@ -141,36 +526,4 @@ router.delete('/:id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 设置默认地址
|
||||
router.put('/:id/default', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const userId = req.user.id;
|
||||
|
||||
// 先取消所有默认地址
|
||||
await dbConnector.query(
|
||||
'UPDATE addresses SET is_default = 0 WHERE user_id = ?',
|
||||
[userId]
|
||||
);
|
||||
|
||||
// 设置当前地址为默认
|
||||
await dbConnector.query(
|
||||
'UPDATE addresses SET is_default = 1, updated_at = NOW() WHERE id = ? AND user_id = ?',
|
||||
[id, userId]
|
||||
);
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
message: '设置默认地址成功'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('设置默认地址失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
message: '服务器内部错误',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -8,6 +8,106 @@ const router = express.Router();
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/auth/register:
|
||||
* post:
|
||||
* summary: 用户注册
|
||||
* description: 创建一个新的用户账户
|
||||
* tags:
|
||||
* - 认证管理
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - username
|
||||
* - password
|
||||
* - phone
|
||||
* properties:
|
||||
* username:
|
||||
* type: string
|
||||
* example: "user123"
|
||||
* description: 用户名
|
||||
* password:
|
||||
* type: string
|
||||
* example: "password123"
|
||||
* description: 密码(至少6位)
|
||||
* phone:
|
||||
* type: string
|
||||
* example: "13800138000"
|
||||
* description: 手机号
|
||||
* email:
|
||||
* type: string
|
||||
* example: "user@example.com"
|
||||
* description: 邮箱地址
|
||||
* user_type:
|
||||
* type: string
|
||||
* example: "farmer"
|
||||
* description: 用户类型
|
||||
* responses:
|
||||
* 201:
|
||||
* description: 注册成功
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 201
|
||||
* message:
|
||||
* type: string
|
||||
* example: 注册成功
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* user_id:
|
||||
* type: integer
|
||||
* example: 1
|
||||
* username:
|
||||
* type: string
|
||||
* example: "user123"
|
||||
* phone:
|
||||
* type: string
|
||||
* example: "13800138000"
|
||||
* email:
|
||||
* type: string
|
||||
* example: "user@example.com"
|
||||
* user_type:
|
||||
* type: string
|
||||
* example: "farmer"
|
||||
* token:
|
||||
* type: string
|
||||
* example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
* 400:
|
||||
* description: 请求参数错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 400
|
||||
* message:
|
||||
* type: string
|
||||
* example: 用户名、密码和手机号为必填项
|
||||
* 409:
|
||||
* description: 用户已存在
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 409
|
||||
* message:
|
||||
* type: string
|
||||
* example: 用户名、手机号或邮箱已存在
|
||||
*
|
||||
* 用户注册
|
||||
*/
|
||||
router.post('/register', async (req, res, next) => {
|
||||
@@ -96,6 +196,87 @@ router.post('/register', async (req, res, next) => {
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/auth/login:
|
||||
* post:
|
||||
* summary: 用户登录
|
||||
* description: 使用用户名和密码进行身份验证并获取访问令牌
|
||||
* tags:
|
||||
* - 认证管理
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - username
|
||||
* - password
|
||||
* properties:
|
||||
* username:
|
||||
* type: string
|
||||
* example: "user123"
|
||||
* description: 用户名
|
||||
* password:
|
||||
* type: string
|
||||
* example: "password123"
|
||||
* description: 密码
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 登录成功
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 登录成功
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* user_id:
|
||||
* type: integer
|
||||
* example: 1
|
||||
* username:
|
||||
* type: string
|
||||
* example: "user123"
|
||||
* user_type:
|
||||
* type: string
|
||||
* example: "farmer"
|
||||
* token:
|
||||
* type: string
|
||||
* example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
* 400:
|
||||
* description: 请求参数错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 400
|
||||
* message:
|
||||
* type: string
|
||||
* example: 用户名和密码为必填项
|
||||
* 401:
|
||||
* description: 用户名或密码错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 用户名或密码错误
|
||||
*
|
||||
* 用户登录
|
||||
*/
|
||||
router.post('/login', async (req, res, next) => {
|
||||
|
||||
@@ -2,6 +2,70 @@ const express = require('express');
|
||||
const router = express.Router();
|
||||
const dbConnector = require('../utils/dbConnector');
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/cart:
|
||||
* get:
|
||||
* summary: 获取用户购物车
|
||||
* description: 获取当前用户的购物车商品列表及总计信息
|
||||
* tags:
|
||||
* - 购物车管理
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取购物车信息
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 获取成功
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* items:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/CartItem'
|
||||
* total_amount:
|
||||
* type: number
|
||||
* example: 99.9
|
||||
* total_quantity:
|
||||
* type: integer
|
||||
* example: 3
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 500
|
||||
* message:
|
||||
* type: string
|
||||
* example: 服务器内部错误
|
||||
*/
|
||||
// 获取用户购物车
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
@@ -36,6 +100,101 @@ router.get('/', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/cart/items:
|
||||
* post:
|
||||
* summary: 添加商品到购物车
|
||||
* description: 将指定商品添加到当前用户的购物车中
|
||||
* tags:
|
||||
* - 购物车管理
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - product_id
|
||||
* - quantity
|
||||
* properties:
|
||||
* product_id:
|
||||
* type: integer
|
||||
* example: 1
|
||||
* description: 商品ID
|
||||
* quantity:
|
||||
* type: integer
|
||||
* example: 2
|
||||
* description: 商品数量
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功添加到购物车
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 添加成功
|
||||
* 400:
|
||||
* description: 请求参数错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 400
|
||||
* message:
|
||||
* type: string
|
||||
* example: 商品数量必须大于0
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
* 404:
|
||||
* description: 商品不存在
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 404
|
||||
* message:
|
||||
* type: string
|
||||
* example: 商品不存在
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 500
|
||||
* message:
|
||||
* type: string
|
||||
* example: 服务器内部错误
|
||||
*/
|
||||
// 添加商品到购物车
|
||||
router.post('/items', async (req, res) => {
|
||||
try {
|
||||
@@ -50,33 +209,38 @@ router.post('/items', async (req, res) => {
|
||||
message: '商品不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查购物车是否已有该商品
|
||||
|
||||
if (quantity <= 0) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
message: '商品数量必须大于0'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查购物车中是否已存在该商品
|
||||
const existingItem = await dbConnector.query(
|
||||
'SELECT * FROM cart_items WHERE user_id = ? AND product_id = ?',
|
||||
[userId, product_id]
|
||||
);
|
||||
|
||||
|
||||
if (existingItem.length > 0) {
|
||||
// 更新数量
|
||||
const newQuantity = existingItem[0].quantity + quantity;
|
||||
await dbConnector.query(
|
||||
'UPDATE cart_items SET quantity = quantity + ?, updated_at = NOW() WHERE id = ?',
|
||||
[quantity, existingItem[0].id]
|
||||
'UPDATE cart_items SET quantity = ? WHERE id = ?',
|
||||
[newQuantity, existingItem[0].id]
|
||||
);
|
||||
} else {
|
||||
// 新增商品
|
||||
// 添加新商品到购物车
|
||||
await dbConnector.query(
|
||||
'INSERT INTO cart_items (user_id, product_id, quantity, created_at, updated_at) VALUES (?, ?, ?, NOW(), NOW())',
|
||||
'INSERT INTO cart_items (user_id, product_id, quantity) VALUES (?, ?, ?)',
|
||||
[userId, product_id, quantity]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
message: '添加成功',
|
||||
data: {
|
||||
cart_item_id: existingItem.length > 0 ? existingItem[0].id : null
|
||||
}
|
||||
message: '添加成功'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('添加购物车失败:', error);
|
||||
@@ -88,31 +252,136 @@ router.post('/items', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/cart/items/{id}:
|
||||
* put:
|
||||
* summary: 更新购物车商品数量
|
||||
* description: 更新购物车中指定商品的数量
|
||||
* tags:
|
||||
* - 购物车管理
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 购物车商品项ID
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - quantity
|
||||
* properties:
|
||||
* quantity:
|
||||
* type: integer
|
||||
* example: 3
|
||||
* description: 新的商品数量
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功更新购物车商品数量
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 更新成功
|
||||
* 400:
|
||||
* description: 请求参数错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 400
|
||||
* message:
|
||||
* type: string
|
||||
* example: 商品数量必须大于0
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
* 404:
|
||||
* description: 购物车商品项不存在
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 404
|
||||
* message:
|
||||
* type: string
|
||||
* example: 购物车商品项不存在
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 500
|
||||
* message:
|
||||
* type: string
|
||||
* example: 服务器内部错误
|
||||
*/
|
||||
// 更新购物车商品数量
|
||||
router.put('/items/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { quantity } = req.body;
|
||||
const userId = req.user.id;
|
||||
|
||||
// 检查购物车项是否存在
|
||||
|
||||
if (quantity <= 0) {
|
||||
return res.status(400).json({
|
||||
code: 400,
|
||||
message: '商品数量必须大于0'
|
||||
});
|
||||
}
|
||||
|
||||
// 检查购物车商品项是否存在且属于当前用户
|
||||
const cartItem = await dbConnector.query(
|
||||
'SELECT * FROM cart_items WHERE id = ? AND user_id = ?',
|
||||
[id, userId]
|
||||
);
|
||||
|
||||
|
||||
if (cartItem.length === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
message: '购物车项不存在'
|
||||
message: '购物车商品项不存在'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 更新数量
|
||||
await dbConnector.query(
|
||||
'UPDATE cart_items SET quantity = ?, updated_at = NOW() WHERE id = ?',
|
||||
'UPDATE cart_items SET quantity = ? WHERE id = ?',
|
||||
[quantity, id]
|
||||
);
|
||||
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
message: '更新成功'
|
||||
@@ -127,23 +396,105 @@ router.put('/items/:id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/v1/cart/items/{id}:
|
||||
* delete:
|
||||
* summary: 删除购物车商品
|
||||
* description: 从购物车中删除指定商品
|
||||
* tags:
|
||||
* - 购物车管理
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 购物车商品项ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功删除购物车商品
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 删除成功
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
* 404:
|
||||
* description: 购物车商品项不存在
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 404
|
||||
* message:
|
||||
* type: string
|
||||
* example: 购物车商品项不存在
|
||||
* 500:
|
||||
* description: 服务器内部错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 500
|
||||
* message:
|
||||
* type: string
|
||||
* example: 服务器内部错误
|
||||
*/
|
||||
// 删除购物车商品
|
||||
router.delete('/items/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const userId = req.user.id;
|
||||
|
||||
await dbConnector.query(
|
||||
'DELETE FROM cart_items WHERE id = ? AND user_id = ?',
|
||||
|
||||
// 检查购物车商品项是否存在且属于当前用户
|
||||
const cartItem = await dbConnector.query(
|
||||
'SELECT * FROM cart_items WHERE id = ? AND user_id = ?',
|
||||
[id, userId]
|
||||
);
|
||||
|
||||
|
||||
if (cartItem.length === 0) {
|
||||
return res.status(404).json({
|
||||
code: 404,
|
||||
message: '购物车商品项不存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 删除购物车商品项
|
||||
await dbConnector.query('DELETE FROM cart_items WHERE id = ?', [id]);
|
||||
|
||||
res.json({
|
||||
code: 200,
|
||||
message: '删除成功'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('删除购物车失败:', error);
|
||||
console.error('删除购物车商品失败:', error);
|
||||
res.status(500).json({
|
||||
code: 500,
|
||||
message: '服务器内部错误',
|
||||
|
||||
@@ -5,7 +5,81 @@ const { asyncHandler } = require('../middlewares/errorHandler');
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* 获取订单列表
|
||||
* @swagger
|
||||
* /api/v1/orders:
|
||||
* get:
|
||||
* summary: 获取订单列表
|
||||
* description: 分页获取当前用户的订单列表,支持按状态和日期筛选
|
||||
* tags:
|
||||
* - 订单管理
|
||||
* 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: status
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 订单状态 (0-待支付, 1-已支付, 2-已取消)
|
||||
* - in: query
|
||||
* name: start_date
|
||||
* schema:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 开始日期 (YYYY-MM-DD)
|
||||
* - in: query
|
||||
* name: end_date
|
||||
* schema:
|
||||
* type: string
|
||||
* format: date
|
||||
* description: 结束日期 (YYYY-MM-DD)
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取订单列表
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 获取成功
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* orders:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Order'
|
||||
* pagination:
|
||||
* $ref: '#/components/schemas/Pagination'
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
*/
|
||||
router.get('/', asyncHandler(async (req, res) => {
|
||||
const { page = 1, limit = 10, status, start_date, end_date } = req.query;
|
||||
@@ -68,7 +142,71 @@ router.get('/', asyncHandler(async (req, res) => {
|
||||
}));
|
||||
|
||||
/**
|
||||
* 获取订单详情
|
||||
* @swagger
|
||||
* /api/v1/orders/{id}:
|
||||
* get:
|
||||
* summary: 获取订单详情
|
||||
* description: 根据订单ID获取当前用户的订单详情
|
||||
* tags:
|
||||
* - 订单管理
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 订单ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取订单详情
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 获取成功
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* order:
|
||||
* $ref: '#/components/schemas/Order'
|
||||
* orderItems:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/OrderItem'
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
* 404:
|
||||
* description: 订单不存在
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 404
|
||||
* message:
|
||||
* type: string
|
||||
* example: 订单不存在
|
||||
*/
|
||||
router.get('/:id', asyncHandler(async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
@@ -6,7 +6,83 @@ const { asyncHandler } = require('../middlewares/errorHandler');
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* 获取商品列表
|
||||
* @swagger
|
||||
* /api/v1/products:
|
||||
* get:
|
||||
* summary: 获取商品列表
|
||||
* description: 分页获取商品列表,支持按分类、价格区间、关键词筛选
|
||||
* tags:
|
||||
* - 商品管理
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: page
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 1
|
||||
* description: 页码
|
||||
* - in: query
|
||||
* name: limit
|
||||
* schema:
|
||||
* type: integer
|
||||
* default: 12
|
||||
* description: 每页数量
|
||||
* - in: query
|
||||
* name: category_id
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 分类ID
|
||||
* - in: query
|
||||
* name: keyword
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 搜索关键词
|
||||
* - in: query
|
||||
* name: min_price
|
||||
* schema:
|
||||
* type: number
|
||||
* description: 最低价格
|
||||
* - in: query
|
||||
* name: max_price
|
||||
* schema:
|
||||
* type: number
|
||||
* description: 最高价格
|
||||
* - in: query
|
||||
* name: sort_by
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [name, price, created_at, stock]
|
||||
* default: created_at
|
||||
* description: 排序字段
|
||||
* - in: query
|
||||
* name: sort_order
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [asc, desc]
|
||||
* default: desc
|
||||
* description: 排序顺序
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取商品列表
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 获取成功
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* products:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Product'
|
||||
* pagination:
|
||||
* $ref: '#/components/schemas/Pagination'
|
||||
*/
|
||||
router.get('/', optionalAuth, asyncHandler(async (req, res) => {
|
||||
const {
|
||||
@@ -91,7 +167,51 @@ router.get('/', optionalAuth, asyncHandler(async (req, res) => {
|
||||
}));
|
||||
|
||||
/**
|
||||
* 获取商品详情
|
||||
* @swagger
|
||||
* /api/v1/products/{id}:
|
||||
* get:
|
||||
* summary: 获取商品详情
|
||||
* description: 根据商品ID获取商品详情
|
||||
* tags:
|
||||
* - 商品管理
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 商品ID
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取商品详情
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 获取成功
|
||||
* data:
|
||||
* $ref: '#/components/schemas/Product'
|
||||
* 404:
|
||||
* description: 商品不存在
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 404
|
||||
* message:
|
||||
* type: string
|
||||
* example: 商品不存在
|
||||
* data:
|
||||
* type: null
|
||||
*/
|
||||
router.get('/:id', optionalAuth, asyncHandler(async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
@@ -8,7 +8,74 @@ const { asyncHandler } = require('../middlewares/errorHandler');
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* 获取用户列表(管理员权限)
|
||||
* @swagger
|
||||
* /api/v1/users:
|
||||
* get:
|
||||
* summary: 获取用户列表(管理员权限)
|
||||
* description: 分页获取用户列表,支持按关键词和用户类型筛选
|
||||
* tags:
|
||||
* - 用户管理
|
||||
* 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: keyword
|
||||
* schema:
|
||||
* type: string
|
||||
* description: 搜索关键词(用户名、手机号或邮箱)
|
||||
* - in: query
|
||||
* name: user_type
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: 用户类型
|
||||
* responses:
|
||||
* 200:
|
||||
* description: 成功获取用户列表
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 200
|
||||
* message:
|
||||
* type: string
|
||||
* example: 获取成功
|
||||
* data:
|
||||
* type: object
|
||||
* properties:
|
||||
* users:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/User'
|
||||
* pagination:
|
||||
* $ref: '#/components/schemas/Pagination'
|
||||
* 401:
|
||||
* description: 未授权访问
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 401
|
||||
* message:
|
||||
* type: string
|
||||
* example: 未授权访问
|
||||
*/
|
||||
router.get('/', adminRequired, asyncHandler(async (req, res) => {
|
||||
const { page = 1, limit = 10, keyword, user_type } = req.query;
|
||||
@@ -57,7 +124,91 @@ router.get('/', adminRequired, asyncHandler(async (req, res) => {
|
||||
}));
|
||||
|
||||
/**
|
||||
* 创建用户(管理员权限)
|
||||
* @swagger
|
||||
* /api/v1/users:
|
||||
* post:
|
||||
* summary: 创建用户(管理员权限)
|
||||
* description: 管理员创建新用户
|
||||
* tags:
|
||||
* - 用户管理
|
||||
* security:
|
||||
* - bearerAuth: []
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required:
|
||||
* - username
|
||||
* - phone
|
||||
* - email
|
||||
* - user_type
|
||||
* - password
|
||||
* properties:
|
||||
* username:
|
||||
* type: string
|
||||
* example: "user123"
|
||||
* phone:
|
||||
* type: string
|
||||
* example: "13800138000"
|
||||
* email:
|
||||
* type: string
|
||||
* example: "user@example.com"
|
||||
* user_type:
|
||||
* type: integer
|
||||
* example: 1
|
||||
* password:
|
||||
* type: string
|
||||
* example: "password123"
|
||||
* real_name:
|
||||
* type: string
|
||||
* example: "张三"
|
||||
* avatar_url:
|
||||
* type: string
|
||||
* example: "https://example.com/avatar.jpg"
|
||||
* responses:
|
||||
* 201:
|
||||
* description: 用户创建成功
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 201
|
||||
* message:
|
||||
* type: string
|
||||
* example: 用户创建成功
|
||||
* data:
|
||||
* $ref: '#/components/schemas/User'
|
||||
* 400:
|
||||
* description: 参数错误
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 400
|
||||
* message:
|
||||
* type: string
|
||||
* example: 参数错误
|
||||
* 409:
|
||||
* description: 用户名、邮箱或手机号已存在
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* code:
|
||||
* type: integer
|
||||
* example: 409
|
||||
* message:
|
||||
* type: string
|
||||
* example: 用户名已存在
|
||||
*/
|
||||
router.post('/', adminRequired, asyncHandler(async (req, res) => {
|
||||
const { username, phone, email, user_type, password, real_name, avatar_url } = req.body;
|
||||
|
||||
Reference in New Issue
Block a user