Compare commits

...

3 Commits

Author SHA1 Message Date
adc9f2259e refactor(config): 优化 MongoDB配置文件
- 替换 dotenv 引入方式,使用 destructuring 导入 config 函数- 调用 config()函数以加载环境变量
-移除冗余的 import('dotenv').config() 语句
2025-01-05 18:46:36 +08:00
fbbcf675d1 refactor(user): 重构用户相关代码
- 优化了用户路由、控制器、仓库和服务的代码结构
- 改进了代码的可读性和可维护性
-移除了冗余的导入和导出语句
- 统一了异步函数的定义方式- 简化了错误处理逻辑
2025-01-05 18:44:46 +08:00
039317d229 refactor(user): 重构用户相关代码
- 更新导入路径,使用 .js 后缀
-修正用户模型中的密码加密和验证逻辑
- 优化用户路由和控制器
- 重构用户服务和仓库,使用原生 mongoose 方法- 更新错误处理和日志记录
2025-01-05 18:28:06 +08:00
13 changed files with 140 additions and 183 deletions

36
app.js
View File

@ -1,35 +1,20 @@
import createError from 'http-errors' import createError from 'http-errors'
import express from 'express' import express from 'express'
import path from 'path' import path from 'path'
import logger from 'morgan' import logger from 'morgan'
import cors from 'cors' import cors from 'cors'
import session from 'express-session' import session from 'express-session'
import { RedisStore } from 'connect-redis' import { RedisStore } from 'connect-redis'
import Redis from 'ioredis' import Redis from 'ioredis'
import { fileURLToPath } from 'url'
import cookieParser from 'cookie-parser' import cookieParser from 'cookie-parser'
import indexRouter from './routes/index.js'
import indexRouter from './routes/index' import userRouter from './routes/userRouter.js'
import accountRouter from './routes/accountRouter.js'
import userRouter from './routes/userRouter'
import accountRouter from './routes/accountRouter'
import { config } from 'dotenv' import { config } from 'dotenv'
import { connectMongoDB } from './config/mongodbConfig.js'
// mongodb数据库连接 import { HTTP_STATUS } from './common/constant/httpStatus.js'
import { connectMongoDB } from './config/mongodbConfig' import { authenticateSession } from './utils/loginUtil.js'
import { HTTP_STATUS } from './common/constant/httpStatus'
import loginUtil from './utils/loginUtil'
config() config()
// 初始化 Redis 客户端 // 初始化 Redis 客户端
@ -42,6 +27,9 @@ const redisClient = new Redis({
const app = express() const app = express()
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
app.use(logger('dev')) app.use(logger('dev'))
app.use(express.json()) app.use(express.json())
app.use(express.urlencoded({ extended: false })) app.use(express.urlencoded({ extended: false }))
@ -81,7 +69,7 @@ async function startServer() {
startServer().then((r) => logger(r)) startServer().then((r) => logger(r))
app.use('/', indexRouter) app.use('/', indexRouter)
app.use('/user', loginUtil.authenticateSession, userRouter) app.use('/user', authenticateSession, userRouter)
app.use('/account', accountRouter) app.use('/account', accountRouter)
// catch 404 and forward to error handler // catch 404 and forward to error handler
@ -105,4 +93,4 @@ app.use(function (err, req, res) {
}) })
}) })
module.exports = app export default app

View File

@ -3,9 +3,9 @@
/** /**
* Module dependencies. * Module dependencies.
*/ */
const app = require('../app') import app from '../app.js'
const debug = require('debug')('express:server') import debug from 'debug'
const http = require('http') import http from 'http'
/** /**
* Get port from environment and store in Express. * Get port from environment and store in Express.

View File

@ -66,5 +66,3 @@ export const HTTP_STATUS = {
/** @type {number} 网关超时 */ /** @type {number} 网关超时 */
GATEWAY_TIMEOUT: 504 GATEWAY_TIMEOUT: 504
} }
export default { HTTP_STATUS }

View File

@ -1,5 +1,4 @@
import mongoose from 'mongoose' import mongoose from 'mongoose'
import logger from 'morgan' import logger from 'morgan'
import { config } from 'dotenv' import { config } from 'dotenv'
@ -16,7 +15,7 @@ let isConnected = false
export async function connectMongoDB() { export async function connectMongoDB() {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
logger(account, password, host, port) logger({ account, password, host, port })
} }
// 如果已经连接,不再重复连接 // 如果已经连接,不再重复连接

View File

@ -1,12 +1,12 @@
import { body, validationResult } from 'express-validator' import { body, validationResult } from 'express-validator'
import logger from 'morgan' import logger from 'morgan'
import userService from '../services/userService' import userService from '../services/userService.js'
import FetchResult from '../common/web/fetchResult' import FetchResult from '../common/web/fetchResult.js'
import messages from '../config/messages' import messages from '../config/messages.js'
import { HTTP_STATUS } from '../common/constant/httpStatus' import { HTTP_STATUS } from '../common/constant/httpStatus.js'
import { SearchQuery } from '../models/search' import { SearchQuery } from '../models/search.js'
export default UserController = { const UserController = {
async getAllUsers(res) { async getAllUsers(res) {
try { try {
const users = await userService.getUserList() const users = await userService.getUserList()
@ -15,7 +15,6 @@ export default UserController = {
return FetchResult.formatResult(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, 'Internal server error') return FetchResult.formatResult(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, 'Internal server error')
} }
}, },
async findUserList(req, res) { async findUserList(req, res) {
try { try {
const { page, size, sort } = req.query const { page, size, sort } = req.query
@ -28,7 +27,6 @@ export default UserController = {
return FetchResult.formatResult(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, 'Internal server error') return FetchResult.formatResult(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, 'Internal server error')
} }
}, },
createUser: [ createUser: [
body('account').isLength({ min: 3 }).withMessage('Account must be at least 3 characters long'), body('account').isLength({ min: 3 }).withMessage('Account must be at least 3 characters long'),
body('account').isEmpty().withMessage('Account is required'), body('account').isEmpty().withMessage('Account is required'),
@ -63,7 +61,6 @@ export default UserController = {
} }
} }
], ],
login: [ login: [
body('account').notEmpty().withMessage('Account is required'), body('account').notEmpty().withMessage('Account is required'),
body('password').notEmpty().withMessage('Password is required'), body('password').notEmpty().withMessage('Password is required'),
@ -99,7 +96,6 @@ export default UserController = {
} }
} }
], ],
logout: [ logout: [
async (req, res) => { async (req, res) => {
try { try {
@ -111,7 +107,6 @@ export default UserController = {
} }
} }
], ],
async getUserExists(req, res) { async getUserExists(req, res) {
try { try {
const { account } = req.query const { account } = req.query
@ -126,5 +121,7 @@ export default UserController = {
logger('Error checking user existence: ', err) logger('Error checking user existence: ', err)
return FetchResult.formatResult(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, 'Internal server error') return FetchResult.formatResult(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, 'Internal server error')
} }
} },
} }
export default UserController

View File

@ -7,7 +7,7 @@ export class SearchQuery {
} }
} }
class SearchResult { export class SearchResult {
constructor({ list = [], num = 0, size = 0, total = 0 } = {}) { constructor({ list = [], num = 0, size = 0, total = 0 } = {}) {
if (typeof num !== 'number') { if (typeof num !== 'number') {
throw new Error('Invalid num parameter') throw new Error('Invalid num parameter')
@ -25,5 +25,3 @@ class SearchResult {
this.total = total this.total = total
} }
} }
export default { SearchQuery, SearchResult }

View File

@ -1,5 +1,5 @@
import { Schema as _Schema, model } from 'mongoose' import { Schema as _Schema, model } from 'mongoose'
import { hashPassword } from '../utils/hashUtils' import { hashPassword } from '../utils/hashUtils.js'
const Schema = _Schema const Schema = _Schema
const UserSchema = new Schema({ const UserSchema = new Schema({
@ -7,12 +7,12 @@ const UserSchema = new Schema({
gender: { type: String, enum: ['male', 'female', 'other'], maxlength: 20 }, gender: { type: String, enum: ['male', 'female', 'other'], maxlength: 20 },
birth: { type: Date }, birth: { type: Date },
avatar: { type: String, maxlength: 100 }, avatar: { type: String, maxlength: 100 },
account: { type: String, required: true, unique: true, maxlength: 100, index: true }, account: { type: String, required: true, unique: true, maxlength: 100 },
password: { type: String, required: true, maxlength: 100 }, password: { type: String, required: true, maxlength: 100 },
email: { type: String, maxlength: 255, index: true }, email: { type: String, match: /^\S+@\S+\.\S+$/, maxlength: 255, index: true },
phone: { type: String, maxlength: 11 }, phone: { type: String, match: /^1[3-9]\d{9}$/, maxlength: 11 },
registerDate: { type: Date, default: Date.now }, registerDate: { type: Date, default: Date.now },
lastLoginDate: { type: Date, default: Date.now }, lastLoginDate: { type: Date },
status: { type: String, enum: ['active', 'inactive', 'pending'], default: 'pending' } status: { type: String, enum: ['active', 'inactive', 'pending'], default: 'pending' }
}) })
@ -20,13 +20,18 @@ UserSchema.pre('save', async function (next) {
const user = this const user = this
if (!user.isModified('password')) return next() if (!user.isModified('password')) return next()
try { try {
console.log(user.password)
user.password = await hashPassword(user.password) user.password = await hashPassword(user.password)
next() next()
} catch (error) { } catch (error) {
next(error) // 记录错误日志,避免泄露敏感信息
console.error('Error hashing password:', error.message)
next(new Error('Failed to hash password'))
} }
}) })
// 更新 lastLoginDate 在用户登录时
UserSchema.methods.updateLastLoginDate = function () {
this.lastLoginDate = new Date()
}
export default model('User', UserSchema) export default model('User', UserSchema)

View File

@ -1,114 +1,93 @@
import { import User from '../models/userModel.js'
startSession, import { SearchResult } from '../models/search.js'
find,
aggregate,
findById,
findOne,
exists,
create,
findByIdAndUpdate,
findByIdAndDelete,
deleteMany
} from '../models/userModel'
import { SearchResult } from '../models/search'
export async function startTransaction() { const userRepository = {
const session = await startSession() async startTransaction() {
session.startTransaction() const session = await User.startSession()
return session session.startTransaction()
} return session
},
async commitTransaction(session) {
return session.commitTransaction()
},
async rollbackTransaction(session) {
return session.abortTransaction()
},
async selectAllUser() {
return User.find()
},
async selectUserList(search = {}) {
try {
const { size = 20, page = 1, sort, filters } = search
export async function commitTransaction(session) { const skip = (page - 1) * size
return session.commitTransaction() const searchResult = new SearchResult()
} searchResult.num = page
searchResult.size = size
export async function rollbackTransaction(session) { // 检查 filters确保只有在 filters 存在时才应用
return session.abortTransaction() const matchFilters = filters ? { ...filters } : {}
}
export async function selectAllUser() { const sortObj = sort && typeof sort === 'object' ? sort : { _id: 1 }
return find()
}
export async function selectUserList(search = {}) { const result = await User.aggregate([
try { // 应用过滤条件确保filters存在时才传入
const { size = 20, page = 1, sort, filters } = search { $match: matchFilters },
{
const skip = (page - 1) * size $facet: {
const searchResult = new SearchResult() metadata: [{ $count: 'total' }, { $addFields: { total: '$total' } }],
searchResult.num = page data: [{ $sort: sortObj }, { $skip: skip }, { $limit: size }]
searchResult.size = size }
// 检查 filters确保只有在 filters 存在时才应用
const matchFilters = filters ? { ...filters } : {}
const sortObj = sort && typeof sort === 'object' ? sort : { _id: 1 } // 默认按 _id 升序排序
const result = await aggregate([
// 应用过滤条件确保filters存在时才传入
{ $match: matchFilters },
{
$facet: {
metadata: [{ $count: 'total' }, { $addFields: { total: '$total' } }],
data: [{ $sort: sortObj }, { $skip: skip }, { $limit: size }]
} }
} ])
])
// 解构 result 并进行必要的空值检查 // 解构 result 并进行必要的空值检查
const { metadata = [], data = [] } = result[0] || {} const { metadata = [], data = [] } = result[0] || {}
// 提取 metadata 中的 total若不存在则为 0 // 提取 metadata 中的 total若不存在则为 0
searchResult.total = metadata.length > 0 ? metadata[0].total : 0 searchResult.total = metadata.length > 0 ? metadata[0].total : 0
searchResult.list = data searchResult.list = data
return searchResult return searchResult
} catch (error) { } catch (error) {
console.log(error) console.log(error)
throw error throw error
}
},
async selectUserById(id) {
return User.findById(id)
},
async selectUserByAccount(account) {
return User.findOne({ account: account })
},
async selectUserByEmail(email) {
return User.findOne({ email: email })
},
async selectUserByUsername(username) {
return User.findOne({ username: username })
},
async selectUserByPhone(phone) {
return User.findOne({ phone: phone })
},
async selectUserByAccountExist(account) {
const exist = await User.exists({ account: account })
return exist !== null
},
async createUser(user) {
return User.create(user)
},
async updateUserById(id, user) {
return User.findByIdAndUpdate(id, user)
},
async updateUserByLoginDate(id, loginDate) {
return User.findByIdAndUpdate(id, { lastLoginDate: loginDate })
},
async deleteUserById(id) {
return User.findByIdAndDelete(id)
},
async deleteAllUser() {
return User.deleteMany()
} }
} }
export async function selectUserById(id) { export default userRepository
return findById(id)
}
export async function selectUserByAccount(account) {
return findOne({ account: account })
}
export async function selectUserByEmail(email) {
return findOne({ email: email })
}
export async function selectUserByUsername(username) {
return findOne({ username: username })
}
export async function selectUserByPhone(phone) {
return findOne({ phone: phone })
}
export async function selectUserByAccountExist(account) {
const exist = await exists({ account: account })
return exist !== null
}
export async function createUser(user) {
return create(user)
}
export async function updateUserById(id, user) {
return findByIdAndUpdate(id, user)
}
export async function updateUserByLoginDate(id, loginDate) {
return findByIdAndUpdate(id, { last_login_date: loginDate })
}
export async function deleteUserById(id) {
return findByIdAndDelete(id)
}
export async function deleteAllUser() {
return deleteMany()
}

View File

@ -1,9 +1,8 @@
// routes/userRoutes.js
import express from 'express' import express from 'express'
const userController = import('../controllers/userController') import userController from '../controllers/userController.js'
const router = express.Router() const router = express.Router()
router.get('/', getUserExists) router.get('/', userController.getUserExists)
export default router export default router

View File

@ -1,6 +1,6 @@
import express from 'express' import express from 'express'
import userController from '../controllers/userController' import userController from '../controllers/userController.js'
const router = express.Router() const router = express.Router()
router.get('/', function (req, res) { router.get('/', function (req, res) {

View File

@ -1,7 +1,5 @@
// routes/userRoutes.js
import userController from '../controllers/userController'
import express from 'express' import express from 'express'
import userController from '../controllers/userController.js'
const router = express.Router() const router = express.Router()

View File

@ -1,9 +1,9 @@
import userMapper from '../repositories/userRepository' import userRepository from '../repositories/userRepository.js'
import logger from 'morgan' import logger from 'morgan'
import messages from '../config/messages' import messages from '../config/messages.js'
import { comparePassword } from '../utils/hashUtils' import { comparePassword } from '../utils/hashUtils.js'
export default userServeice = { const userService = {
/** /**
* 用户登录 * 用户登录
* @param {string} account - 用户账号 * @param {string} account - 用户账号
@ -12,7 +12,7 @@ export default userServeice = {
* @throws {Error} - 如果用户不存在或密码不正确 * @throws {Error} - 如果用户不存在或密码不正确
*/ */
async login(account, password) { async login(account, password) {
const user = await userMapper.selectUserByAccount(account) const user = await userRepository.selectUserByAccount(account)
// 用户不存在 // 用户不存在
if (!user) { if (!user) {
@ -25,10 +25,6 @@ export default userServeice = {
throw new Error(messages.user.passwordIncorrect) throw new Error(messages.user.passwordIncorrect)
} }
// 更新用户的最后登录时间
user.lastLoginDate = new Date()
await userMapper.updateUserByLoginDate(user.id, user.lastLoginDate)
return user return user
}, },
@ -38,16 +34,16 @@ export default userServeice = {
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
async getUserExists(account) { async getUserExists(account) {
return userMapper.selectUserByAccountExist(account) return userRepository.selectUserByAccountExist(account)
}, },
// Removed the unnecessary try-catch block and simplified the function // Removed the unnecessary try-catch block and simplified the function
async getAllUser() { async getAllUser() {
return await userMapper.selectAllUser() return await userRepository.selectAllUser()
}, },
async getUserList(searchQuery) { async getUserList(searchQuery) {
return userMapper.selectUserList(searchQuery) return userRepository.selectUserList(searchQuery)
}, },
/** /**
@ -58,27 +54,29 @@ export default userServeice = {
*/ */
async createUser(user) { async createUser(user) {
const { account } = user const { account } = user
const session = await userMapper.startTransaction() const session = await userRepository.startTransaction()
try { try {
// 检查用户是否已存在 // 检查用户是否已存在
const existingUser = await userMapper.selectUserByAccount(account) const existingUser = await userRepository.selectUserByAccount(account)
if (existingUser) { if (existingUser) {
return new Error(messages.user.alreadyExists) return new Error(messages.user.alreadyExists)
} }
// 创建新用户 // 创建新用户
const result = await userMapper.createUser(user) const result = await userRepository.createUser(user)
// 提交事务 // 提交事务
await userMapper.commitTransaction(session) await userRepository.commitTransaction(session)
return result return result
} catch (err) { } catch (err) {
// 回滚事务 // 回滚事务
await userMapper.rollbackTransaction(session).catch((rollbackErr) => { await userRepository.rollbackTransaction(session).catch((rollbackErr) => {
logger('Error rolling back transaction: ', rollbackErr) logger('Error rolling back transaction: ', rollbackErr)
}) })
throw err // 将错误抛给调用方Controller 层) throw err // 将错误抛给调用方Controller 层)
} }
} }
} }
export default userService

View File

@ -5,7 +5,7 @@ import bcrypt from 'bcrypt'
* @param {string} password 密码 * @param {string} password 密码
* @returns password 加密后的密码 * @returns password 加密后的密码
*/ */
async function hashPassword(password) { export async function hashPassword(password) {
return bcrypt.hash(password, 10) return bcrypt.hash(password, 10)
} }
@ -18,5 +18,3 @@ async function hashPassword(password) {
export async function comparePassword(password, hashedPassword) { export async function comparePassword(password, hashedPassword) {
return bcrypt.compare(password, hashedPassword) return bcrypt.compare(password, hashedPassword)
} }
export default { hashPassword, comparePassword }