Generating commit message...
This commit is contained in:
96
backend/src/utils/database.js
Normal file
96
backend/src/utils/database.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const mongoose = require('mongoose')
|
||||
|
||||
class Database {
|
||||
constructor() {
|
||||
this.mongoose = mongoose
|
||||
this.isConnected = false
|
||||
}
|
||||
|
||||
async connect() {
|
||||
if (this.isConnected) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 连接数据库
|
||||
const mongodbUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/jiebanke'
|
||||
await this.mongoose.connect(mongodbUri, {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true
|
||||
})
|
||||
|
||||
this.isConnected = true
|
||||
console.log('✅ MongoDB连接成功')
|
||||
|
||||
// 监听连接事件
|
||||
this.mongoose.connection.on('error', (error) => {
|
||||
console.error('❌ MongoDB连接错误:', error)
|
||||
this.isConnected = false
|
||||
})
|
||||
|
||||
this.mongoose.connection.on('disconnected', () => {
|
||||
console.warn('⚠️ MongoDB连接断开')
|
||||
this.isConnected = false
|
||||
})
|
||||
|
||||
this.mongoose.connection.on('reconnected', () => {
|
||||
console.log('🔁 MongoDB重新连接成功')
|
||||
this.isConnected = true
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ MongoDB连接失败:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
if (!this.isConnected) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await this.mongoose.disconnect()
|
||||
this.isConnected = false
|
||||
console.log('✅ MongoDB连接已关闭')
|
||||
} catch (error) {
|
||||
console.error('❌ MongoDB断开连接失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 健康检查
|
||||
async healthCheck() {
|
||||
try {
|
||||
await this.mongoose.connection.db.admin().ping()
|
||||
return { status: 'healthy', connected: this.isConnected }
|
||||
} catch (error) {
|
||||
return { status: 'unhealthy', connected: this.isConnected, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
// 获取连接状态
|
||||
getStatus() {
|
||||
return {
|
||||
connected: this.isConnected,
|
||||
readyState: this.mongoose.connection.readyState,
|
||||
host: this.mongoose.connection.host,
|
||||
name: this.mongoose.connection.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建单例实例
|
||||
const database = new Database()
|
||||
|
||||
// 进程退出时关闭数据库连接
|
||||
process.on('SIGINT', async () => {
|
||||
await database.disconnect()
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
process.on('SIGTERM', async () => {
|
||||
await database.disconnect()
|
||||
process.exit(0)
|
||||
})
|
||||
|
||||
module.exports = database
|
||||
79
backend/src/utils/errors.js
Normal file
79
backend/src/utils/errors.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// 自定义应用错误类
|
||||
class AppError extends Error {
|
||||
constructor(message, statusCode) {
|
||||
super(message)
|
||||
this.statusCode = statusCode
|
||||
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error'
|
||||
this.isOperational = true
|
||||
|
||||
Error.captureStackTrace(this, this.constructor)
|
||||
}
|
||||
}
|
||||
|
||||
// 异步错误处理包装器
|
||||
const catchAsync = (fn) => {
|
||||
return (req, res, next) => {
|
||||
fn(req, res, next).catch(next)
|
||||
}
|
||||
}
|
||||
|
||||
// 404错误处理
|
||||
const notFound = (req, res, next) => {
|
||||
const error = new AppError(`无法找到 ${req.originalUrl}`, 404)
|
||||
next(error)
|
||||
}
|
||||
|
||||
// 全局错误处理中间件
|
||||
const globalErrorHandler = (err, req, res, next) => {
|
||||
err.statusCode = err.statusCode || 500
|
||||
err.status = err.status || 'error'
|
||||
err.message = err.message || '服务器内部错误'
|
||||
|
||||
// 开发环境详细错误信息
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
res.status(err.statusCode).json({
|
||||
status: err.status,
|
||||
error: err,
|
||||
message: err.message,
|
||||
stack: err.stack
|
||||
})
|
||||
} else {
|
||||
// 生产环境简化错误信息
|
||||
res.status(err.statusCode).json({
|
||||
status: err.status,
|
||||
message: err.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MongoDB重复键错误处理
|
||||
const handleDuplicateFieldsDB = (err) => {
|
||||
const value = err.errmsg.match(/(["'])(\\?.)*?\1/)[0]
|
||||
const message = `字段值 ${value} 已存在,请使用其他值`
|
||||
return new AppError(message, 400)
|
||||
}
|
||||
|
||||
// MongoDB验证错误处理
|
||||
const handleValidationErrorDB = (err) => {
|
||||
const errors = Object.values(err.errors).map(el => el.message)
|
||||
const message = `输入数据无效: ${errors.join('. ')}`
|
||||
return new AppError(message, 400)
|
||||
}
|
||||
|
||||
// JWT错误处理
|
||||
const handleJWTError = () =>
|
||||
new AppError('无效的token,请重新登录', 401)
|
||||
|
||||
const handleJWTExpiredError = () =>
|
||||
new AppError('token已过期,请重新登录', 401)
|
||||
|
||||
module.exports = {
|
||||
AppError,
|
||||
catchAsync,
|
||||
notFound,
|
||||
globalErrorHandler,
|
||||
handleDuplicateFieldsDB,
|
||||
handleValidationErrorDB,
|
||||
handleJWTError,
|
||||
handleJWTExpiredError
|
||||
}
|
||||
70
backend/src/utils/response.js
Normal file
70
backend/src/utils/response.js
Normal file
@@ -0,0 +1,70 @@
|
||||
// 成功响应格式
|
||||
const success = (data = null, message = '操作成功') => {
|
||||
return {
|
||||
success: true,
|
||||
code: 200,
|
||||
message,
|
||||
data,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
// 分页响应格式
|
||||
const paginate = (data, pagination, message = '获取成功') => {
|
||||
return {
|
||||
success: true,
|
||||
code: 200,
|
||||
message,
|
||||
data: {
|
||||
list: data,
|
||||
pagination: {
|
||||
page: pagination.page,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagination.total,
|
||||
totalPages: Math.ceil(pagination.total / pagination.pageSize)
|
||||
}
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
// 错误响应格式
|
||||
const error = (message = '操作失败', code = 400, errors = null) => {
|
||||
return {
|
||||
success: false,
|
||||
code,
|
||||
message,
|
||||
errors,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
// 创建成功响应
|
||||
const created = (data = null, message = '创建成功') => {
|
||||
return {
|
||||
success: true,
|
||||
code: 201,
|
||||
message,
|
||||
data,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
// 无内容响应
|
||||
const noContent = (message = '无内容') => {
|
||||
return {
|
||||
success: true,
|
||||
code: 204,
|
||||
message,
|
||||
data: null,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
success,
|
||||
paginate,
|
||||
error,
|
||||
created,
|
||||
noContent
|
||||
}
|
||||
Reference in New Issue
Block a user