feat: add backend server
This commit is contained in:
3
server/.gitignore
vendored
Normal file
3
server/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
.env
|
||||||
|
http-test/
|
||||||
99
server/controller/stability.js
Normal file
99
server/controller/stability.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import { requireAuth, requireAdmin } from './user.js'
|
||||||
|
import Stability from '../model/stability.js'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// 列出所有稳定性实验
|
||||||
|
router.get('/', requireAuth, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const stabilities = await Stability.find().populate(['checks.checker', 'creater'])
|
||||||
|
res.json(stabilities)
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '服务器错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 列出未完成的稳定性实验
|
||||||
|
router.get('/unend', requireAuth, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const statilities = await Stability.find({ ended: false }).populate(['checks.checker', 'creater'])
|
||||||
|
res.json(statilities)
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '服务器错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建稳定性实验
|
||||||
|
router.post('/', requireAuth, async (req, res) => {
|
||||||
|
if (!req.body.batch) {
|
||||||
|
return res.status(400).json({ message: '缺少必要字段' })
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stability = await Stability.create({
|
||||||
|
creater: req.user.id,
|
||||||
|
...req.body,
|
||||||
|
})
|
||||||
|
if (stability) {
|
||||||
|
res.json(stability)
|
||||||
|
} else {
|
||||||
|
res.status(400).json({ message: '资源创建失败' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '服务器错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 删除稳定性实验
|
||||||
|
router.delete('/:id', requireAuth, requireAdmin, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const stability = await Stability.findByIdAndDelete(req.params.id)
|
||||||
|
if (stability) {
|
||||||
|
res.json({ message: '删除成功' })
|
||||||
|
} else {
|
||||||
|
res.status(404).json({ message: '删除失败' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '服务器错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 对 指定的稳定性实验进行check
|
||||||
|
// id:稳定性实验id
|
||||||
|
// checkid:检查点id
|
||||||
|
router.post('/check/:id/:checkid', requireAuth, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const stability = await Stability.findById(req.params.id)
|
||||||
|
if (!stability) {
|
||||||
|
return res.status(404).json({ message: `未找到id为${req.params.id}的记录` })
|
||||||
|
}
|
||||||
|
stability.checks.forEach((check) => {
|
||||||
|
if (check._id == req.params.checkid) {
|
||||||
|
check.checked = true
|
||||||
|
check.checker = req.user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
stability.ended = stability.checks.every((check) => check.checked)
|
||||||
|
await stability.save()
|
||||||
|
res.json(stability)
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '服务器错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新稳定性实验
|
||||||
|
router.patch('/:id', requireAuth, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const stability = await Stability.findByIdAndUpdate(req.params.id, req.body, { returnDocument: 'after' })
|
||||||
|
if (stability) {
|
||||||
|
res.json(stability)
|
||||||
|
} else {
|
||||||
|
res.status(404).json({ message: `未找到id为${req.params.id}的记录` })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '服务器错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export { router as stabilityRouter }
|
||||||
65
server/controller/standard.js
Normal file
65
server/controller/standard.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import Standard from '../model/standard.js'
|
||||||
|
import { requireAuth, requireAdmin } from './user.js'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// 获取对照品
|
||||||
|
router.get('/', requireAuth, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const standard = await Standard.find()
|
||||||
|
res.json(standard)
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '服务器错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建对照品
|
||||||
|
router.post('/', requireAuth, async (req, res) => {
|
||||||
|
if (!req.body.batch) {
|
||||||
|
return res.status(400).json({ message: '缺少必要字段' })
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const standard = await Standard.create({ ...req.body })
|
||||||
|
if (standard) {
|
||||||
|
res.json(standard)
|
||||||
|
} else {
|
||||||
|
res.status(400).json({ message: '资源创建失败' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '服务器错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 删除对照品
|
||||||
|
router.delete('/:id', requireAuth, requireAdmin, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const standard = await Standard.findByIdAndDelete(req.params.id)
|
||||||
|
if (standard) {
|
||||||
|
res.json({ message: '删除成功' })
|
||||||
|
} else {
|
||||||
|
res.status(404).json({ message: '删除成功' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '服务器错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新对照品
|
||||||
|
router.patch('/:id', requireAuth, async (req, res) => {
|
||||||
|
try {
|
||||||
|
console.log(req.params.id, req.body)
|
||||||
|
const standard = await Standard.findByIdAndUpdate(req.params.id, req.body, {
|
||||||
|
returnDocument: 'after',
|
||||||
|
})
|
||||||
|
res.json(standard)
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({
|
||||||
|
message: '服务器错误',
|
||||||
|
error: error.message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export { router as standardRouter }
|
||||||
103
server/controller/user.js
Normal file
103
server/controller/user.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import express from 'express'
|
||||||
|
import jwt from 'jsonwebtoken'
|
||||||
|
import User from '../model/user.js'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// 获取所有用户
|
||||||
|
router.get('/', requireAuth, requireAdmin, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const users = await User.find().select('-password')
|
||||||
|
res.json(users)
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '获取用户信息时发生错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建新用户
|
||||||
|
router.post('/', requireAuth, requireAdmin, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { username, nickname, password } = req.body
|
||||||
|
const user = await User.create({
|
||||||
|
username,
|
||||||
|
nickname,
|
||||||
|
password,
|
||||||
|
role: 'user',
|
||||||
|
})
|
||||||
|
res.status(201).json(user)
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({ message: '创建用户时发生错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 删除用户
|
||||||
|
router.delete('/:id', requireAuth, requireAdmin, async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (req.user._id.toString() === req.params.id) {
|
||||||
|
return res.status(400).json({ message: '不能删除你自己' })
|
||||||
|
}
|
||||||
|
const user = await User.findByIdAndDelete(req.params.id)
|
||||||
|
if (user) {
|
||||||
|
res.json({ message: '已删除用户' })
|
||||||
|
} else {
|
||||||
|
res.status(404).json({ message: '该用户不存在' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '删除用户时发生错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 用户登录
|
||||||
|
router.post('/login', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { username, password } = req.body
|
||||||
|
const user = await User.findOne({ username })
|
||||||
|
if (user && (await user.comparePassword(password))) {
|
||||||
|
const token = jwt.sign({ id: user._id, username: user.username }, process.env.JWT_SECRET, {
|
||||||
|
expiresIn: '24h',
|
||||||
|
})
|
||||||
|
res.json({ token, user })
|
||||||
|
} else {
|
||||||
|
res.status(401).json({ message: '用户名或密码错误' })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ message: '登录过程中发生错误', error: error.message })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证用户身份,拒绝未认证的请求
|
||||||
|
*/
|
||||||
|
async function requireAuth(req, res, next) {
|
||||||
|
const authHeader = req.headers['authorization']
|
||||||
|
if (!authHeader) {
|
||||||
|
return res.status(401).json({ message: '未提供身份验证信息' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = authHeader.split(' ').pop()
|
||||||
|
if (!token) {
|
||||||
|
return res.status(401).json({ message: '提供了错误的身份验证信息' })
|
||||||
|
}
|
||||||
|
|
||||||
|
jwt.verify(token, process.env.JWT_SECRET, async (err, decoded) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(401).json({ message: '身份验证失败' })
|
||||||
|
}
|
||||||
|
// 从数据库获取完整的用户信息
|
||||||
|
req.user = await User.findById(decoded.id)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查用户是否具有管理员权限,调用前必须先调用requireAuth中间件验证用户身份
|
||||||
|
*/
|
||||||
|
async function requireAdmin(req, res, next) {
|
||||||
|
if (req.user && req.user.role === 'admin') {
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
res.status(403).json({ message: '需要管理员权限' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { router as userRouter, requireAuth, requireAdmin }
|
||||||
24
server/database.js
Normal file
24
server/database.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import mongoose from 'mongoose'
|
||||||
|
import User from './model/user.js'
|
||||||
|
|
||||||
|
const connectDB = async () => {
|
||||||
|
try {
|
||||||
|
const conn = await mongoose.connect(process.env.MONGO_URI)
|
||||||
|
console.log(`MongoDB Connected: ${conn.connection.host}`)
|
||||||
|
// 检查是否存在管理员用户,如果不存在则创建一个默认管理员
|
||||||
|
const adminUser = await User.findOne({ username: process.env.ADMIN_USERNAME })
|
||||||
|
if (!adminUser) {
|
||||||
|
await User.create({
|
||||||
|
username: process.env.ADMIN_USERNAME,
|
||||||
|
nickname: '管理员',
|
||||||
|
password: process.env.ADMIN_PASSWORD,
|
||||||
|
role: 'admin',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error connecting to MongoDB:', error)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connectDB
|
||||||
26
server/index.js
Normal file
26
server/index.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import dotenv from 'dotenv'
|
||||||
|
dotenv.config()
|
||||||
|
|
||||||
|
// 连接数据库
|
||||||
|
import connectDB from './database.js'
|
||||||
|
await connectDB()
|
||||||
|
|
||||||
|
import express from 'express'
|
||||||
|
import { userRouter } from './controller/user.js'
|
||||||
|
import { stabilityRouter } from './controller/stability.js'
|
||||||
|
import { standardRouter } from './controller/standard.js'
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
app.use(express.json())
|
||||||
|
|
||||||
|
// 注册路由
|
||||||
|
app.use('/api/user', userRouter)
|
||||||
|
app.use('/api/stability', stabilityRouter)
|
||||||
|
app.use('/api/standard', standardRouter)
|
||||||
|
|
||||||
|
// 从环境变量中读取端口号,如果环境变量中没有指定端口号,则使用默认的端口号
|
||||||
|
const PORT = process.env.PORT || 5000
|
||||||
|
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server is running on http://localhost:${PORT}`)
|
||||||
|
})
|
||||||
79
server/model/stability.js
Normal file
79
server/model/stability.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import mongoose from 'mongoose'
|
||||||
|
import User from './user.js'
|
||||||
|
|
||||||
|
const CheckSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
// 检查日期
|
||||||
|
date: {
|
||||||
|
type: Date,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
// 是否已经检查
|
||||||
|
checked: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
// 检查人
|
||||||
|
checker: {
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
|
// 备注
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 稳定性实验 模型
|
||||||
|
const StabilitySchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
// 实验批次号
|
||||||
|
batch: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
// 实验类型
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
enum: ['长期', '加速', '其它'],
|
||||||
|
},
|
||||||
|
// 检查点列表
|
||||||
|
checks: {
|
||||||
|
type: [CheckSchema],
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
// 是否完成
|
||||||
|
ended: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
// 创建人
|
||||||
|
creater: {
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: 'User',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
// 备注
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
StabilitySchema.pre('save', async function () {
|
||||||
|
try {
|
||||||
|
this.checks = this.checks.sort((a, b) => {
|
||||||
|
return new Date(a.date) - new Date(b.date)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const Stability = mongoose.model('Stability', StabilitySchema)
|
||||||
|
|
||||||
|
export default Stability
|
||||||
31
server/model/standard.js
Normal file
31
server/model/standard.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import mongoose from 'mongoose'
|
||||||
|
|
||||||
|
const StandardSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
batch: {
|
||||||
|
type: String,
|
||||||
|
require: true,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
im: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
ass: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
calibration_date: {
|
||||||
|
type: Date,
|
||||||
|
},
|
||||||
|
expire_date: {
|
||||||
|
type: Date,
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const Standard = mongoose.model('Standard', StandardSchema)
|
||||||
|
|
||||||
|
export default Standard
|
||||||
47
server/model/user.js
Normal file
47
server/model/user.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import mongoose from 'mongoose'
|
||||||
|
import bcrypt from 'bcrypt'
|
||||||
|
|
||||||
|
const UserSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
username: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
index: true,
|
||||||
|
},
|
||||||
|
nickname: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
type: String,
|
||||||
|
enum: ['admin', 'user'],
|
||||||
|
default: 'user',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 保存前自动对密码进行哈希加密
|
||||||
|
UserSchema.pre('save', async function () {
|
||||||
|
this.password = await bcrypt.hash(this.password, 10)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 实例方法:校验密码
|
||||||
|
UserSchema.methods.comparePassword = async function (candidatePassword) {
|
||||||
|
return bcrypt.compare(candidatePassword, this.password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出时始终不返回密码
|
||||||
|
UserSchema.methods.toJSON = function () {
|
||||||
|
const obj = this.toObject()
|
||||||
|
delete obj.password
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
const User = mongoose.model('User', UserSchema)
|
||||||
|
|
||||||
|
export default User
|
||||||
1213
server/package-lock.json
generated
Normal file
1213
server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
server/package.json
Normal file
19
server/package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"author": "hbk01",
|
||||||
|
"type": "module",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "nodemon index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bcrypt": "^6.0.0",
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"jsonwebtoken": "^9.0.3",
|
||||||
|
"mongoose": "^9.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user