ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于Node.js+Vue的体育器材租赁管理系统设计与实现

2026/10/1 18:05:11 拓冰建站 浏览量
基于Node.js+Vue的体育器材租赁管理系统设计与实现 先聊个实际现象每年到这个时间点总有一大批计算机相关专业的朋友在纠结毕业设计题目。数据库管理系统被做了无数遍电商系统也快做烂了但你稍微换个思路——把“器材租赁”和“校园/场馆业务”绑定再用Node.js和Vue这套前后端分离的组合去实现立刻就变成一个有真实业务逻辑、有技术发挥空间、还有得写的题目。你拿到的这个“体育器材租赁管理系统设计与实现”就是典型。这篇文章我会直接用实际做项目的角度把“怎么从零把这个系统落地”这件事拆开讲。包含为什么选Node.js而不是Java、系统功能模块怎么划分才算合理、数据库表怎么设计、核心租赁流程怎么用代码实现、前端Vue项目怎么搭建和接口联调、还有那些你在部署和跑通时一定会踩的坑。无论是用来做毕业设计还是想接一个外包练手这套思路都适用。1. 先想清楚再做需求拆解与功能模块设计1.1 这个系统的核心痛点是什么很多人在动手写代码之前就急着建Vue项目这是最常见的错误。体育器材租赁和普通商品购买有本质区别它天然带有“时间维度”和“状态维度”。一个篮球被学生借走两天后还回来这期间别人不能借一个羽毛球拍送到维修点检修在系统里就不能显示“可借”。那些只做增删改查的管理系统根本覆盖不了这类真实业务。我们定的核心需求很简单校园或场馆的管理员能录入器材、查看库存、处理借出和归还学生或用户能浏览可借器材、提交租借申请、查看自己的借还记录。这里有个很关键的取舍——初期不要做在线支付和押金自动结算押金逻辑可以简化为“管理员线下收取后手动标记已付押金”。我在做这个项目时就把支付模块砍掉了一是接入第三方支付需要商户资质二是按时交付比功能堆叠更重要。这个决策直接影响了后面数据库设计和接口设计的工作量。1.2 功能模块与角色划分两类身份三块核心用户角色不要做复杂系统就分两类管理员admin和普通用户user。任何需要审核、上架、下架、处理异常归还的操作都归管理员普通用户只做查询和提交需求。这样可以大幅减少权限判断的复杂度前端用路由守卫拦截后端用中间件校验前后端双重保障。功能模块我拆成三块器材管理器材信息维护名称、类别、数量、图片、存放位置、状态管理可借、已借出、维修中、已下架、库存增减记录。租赁业务用户提交租借申请、管理员确认出借、用户归还、管理员确认归还并检查器材损耗。系统基础登录注册、用户信息维护、借还记录查询、公告或器材分类展示。这三块模块之间是有依赖关系的器材的库存变化由租赁业务驱动而租赁业务本身又依赖器材状态。理解这个关系你才知道哪个表先建、哪个接口先写。1.3 数据库模型设计要点数据库设计是决定这个项目能做多深的胜负手。我先说结论不需要设计得特别复杂但表之间的关联关系一定要干净。核心表我一共设计了六张users用户表。字段包括id、username、passwordbcrypt加密后的哈希值、roleadmin/user、nickname、phone、created_at。categories器材分类表。比如球类、健身器械、骑行装备等。id、name、description。equipments器材表。id、name、category_id、total_count总数、available_count当前可借数量、status、location、image_url、description。orders租赁订单表。这是核心业务表。id、user_id、equipment_id、rent_time借出时间、expected_return_time预计归还时间、actual_return_time实际归还时间、statuspending/rented/returned/cancelled/overdue、deposit押金金额、created_at。order_logs操作日志表记录谁在什么时间做了什么操作。这个表在写论文的时候特别好用能直接当“系统测试与运行记录”素材。announcements公告表。注意user_id要建索引orders表里equipment_id和user_id都要建索引。我当时用MySQL 8.0字符集选utf8mb4避免中文乱码。还有几个容易忽略的设计细节不要用“状态”这一个字段硬扛所有业务流程。订单状态至少要有pending待确认、rented租借中、returned已归还、cancelled已取消再加上overdue逾期需要后端定时任务扫描。器材表里的available_count不要凭空递减必须在订单状态变为rented时通过事务同时扣减库存避免并发下超借。每个器材在归还时要判断是否超时逾期需要在订单上打标记。论文里的“系统特色功能”可以从这里写起因为市面上很多管理系统根本没有逾期的概念。2. 后端选型与核心接口实现Node.js Express2.1 为什么用Node.js而不是Spring Boot你可能会看到很多带“Spring Boot Vue”的题目那是Java技术栈的路子。但既然定了Node.js核心原因一般有三个一是前后端都用JavaScript语言统一心智负担低二是Node.js做轻量级业务系统开发效率高一个Express服务配好路由和中间件半天就能跑起来三是毕业设计的重点在于把业务逻辑讲清楚Node.js写起来干扰项少不会像Java那样被各种配置淹没。后端框架我选的Express 4.x没有用NestJS或Egg.js。理由很简单这个系统数据表不到十张接口也就二十来个Express的灵活性和轻量正好匹配。NestJS的依赖注入和模块化虽然规范但对新手来说学习成本陡增而且论文里解释“为什么这样设计”时会涉及很多与业务无关的概念。环境要求就是Node.js 16包管理用npm。注意Node版本不要太旧Express 4搭配Node 16实测很稳。2.2 JWT登录鉴权怎么落地用户登录后后端签发JWT令牌前端把token存在本地存储里每次请求放到Authorization头。这是目前前后端分离项目的标准做法。具体流程用户提交username和password。后端用bcrypt比对密码哈希通过后生成JWT。JWT的payload里放userId和role密钥放在环境变量里过期时间设成24小时。前端在路由守卫里判断是否有token没有就跳转登录页请求拦截器里加上token响应拦截器里碰到401就清除本地登录态并跳回登录页。这里有个细节不要把role只放在前端做判断后端每个管理接口都要在中间件里验证role。我在express里写了一个authMiddleware和一个adminMiddlewareadminMiddleware会在authMiddleware之后执行逻辑很简单——decode用户信息后判断role是否为admin。2.3 租赁流程的状态机设计这是整个项目最核心的业务逻辑也是论文里最值得大写特写的一块。租赁流程概括成五步用户创建订单状态变为pending器材的available_count暂不扣减。管理员确认出借状态变为rented同时available_count减1。这个动作必须在事务里执行确保订单更新和库存扣减要么都成功要么都失败。用户归还提交归还申请状态变为returning归还确认中。管理员确认归还状态变为returnedavailable_count加1同时检查是否逾期逾期则额外标记overdue。用户取消订单仅限pending状态可以取消已出借的不能随便取消。为什么pending时不扣库存因为管理员还没有实际把器材交给用户这时候扣库存会导致用户申请了但管理员不出借器材却被占用。这个细节在回答“系统如何保证数据一致性”时非常加分。接口设计我列几个核心的POST /api/auth/login 登录GET /api/equipments 器材列表支持按分类和关键字筛选POST /api/orders 创建租借订单PUT /api/orders/:id/confirm 管理员确认出借PUT /api/orders/:id/return 用户申请归还PUT /api/orders/:id/approve-return 管理员确认归还GET /api/orders/my 查询当前用户的订单列表代码就用Express路由加MySQL连接池每张表对应一个routerservice层写业务逻辑。建议连接MySQL用mysql2这个包支持Promise。3. 前端工程与Vue实现从脚手架到业务页面3.1 Vue3 Vite Element Plus 的组合搭建前端我用的是Vue3 Vite Vue Router 4 Pinia Element Plus。Vite比Webpack启动快太多一个dev server几乎秒开。Element Plus的表格、表单、弹窗组件做后台管理界面非常顺手省去了大量自己写样式的时间。创建项目的方式很简单npm create vitelatest sports-equipment-frontend -- --template vue cd sports-equipment-frontend npm install然后装依赖npm install element-plus axios vue-router4 pinia入口页面结构我建议登录页独立全屏登录成功后进入主布局。左侧菜单栏放“器材管理”“订单管理”“借还记录”“公告管理”“用户管理”这些路由右侧是内容区顶部放用户信息、退出按钮。Vite的跨域代理必须配好。开发阶段后端跑在3000端口前端跑在5173端口不配代理的话浏览器会报CORS错误。在vite.config.js里加这一段server: { proxy: { /api: { target: http://localhost:3000, changeOrigin: true } } }这样前端代码里请求路径写成/api/xxx就行上线时再用Nginx处理。3.2 路由设计动态路由还是静态路由老实说这个系统的路由权限用静态路由完全够用。管理员和普通用户看到的页面数量本来就不同直接在前置守卫里判断一下角色就可以了。网上很多教程一上来就吹动态路由说根据后端返回的菜单动态生成路由但对这种规模的系统纯属自找麻烦。动态路由适合那种用户权限粒度很细、菜单随角色变化很大的企业级项目。你这个系统无非是普通用户不显示“器材管理”菜单、管理员不显示“我的租赁”里的某些操作按钮静态路由加个条件判断代码更直观论文里解释也更顺。核心的路由守卫写法router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next(/login) } else if (token to.path /login) { next(/) } else { next() } })管理员页面的鉴权我放在每个页面自己的onMounted里请求一个管理员信息验证接口如果返回403就跳走。这样比单纯在前端判断角色要安全因为接口才是真正的权限关口。3.3 Axios封装拦截器才是关键Axios的请求和响应拦截器一定要封装好不然每个页面都要写一堆重复代码。我的原则是请求拦截器管token注入响应拦截器管错误处理和统一提示。import axios from axios import { ElMessage } from element-plus import router from ../router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response response.data, error { if (error.response) { const status error.response.status if (status 401) { localStorage.removeItem(token) router.push(/login) ElMessage.error(登录已过期请重新登录) } else { ElMessage.error(error.response.data.message || 请求失败) } } else { ElMessage.error(网络异常) } return Promise.reject(error) } )注意响应拦截器里返回的是response.data这样业务代码里直接拿到的就是后端返回的JSON体不需要每处都写response.data.data。页面级别比较重的表格加载、分页、搜索、删除确认、弹窗表单这些都是能直接抄的套路式代码。重点是表单校验规则不要全部堆在提交时才校验比如器材数量不能为负数、归还日期不能早于借出日期这些在表单提交前就要提出来否则用户会一头雾水。4. 环境配置与部署避坑实录4.1 Node安装与npm脚本执行策略经典到必须讲的坑这个坑我几乎可以确定你会遇到在Windows上装好Node然后运行npm命令时直接报错npm : 无法加载文件 D:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本这个问题的根源是Windows PowerShell默认的执行策略是Restricted不允许运行本地脚本文件中带有签名的命令而npm.ps1正好踩中了这条红线。看到这个报错别慌它跟你的Node安装本身没关系是PowerShell策略问题。解决办法以管理员身份打开PowerShell。执行Set-ExecutionPolicy -ExecutionPolicy RemoteSigned。输Y确认然后重新打开终端npm命令就恢复正常了。这里要提醒一句不要为了省事把执行策略改成UnrestrictedRemoteSigned已经足够日常开发使用安全性也好很多。还有一个配套问题是Node版本管理。如果你以后需要维护多个项目不同项目要求的Node版本可能不一样建议直接用nvmNode Version Manager。Windows下用nvm-windowsmacOS和Linux用nvm官方脚本安装后可以自由切换Node 14/16/18再也不用给每个项目单独重装环境。4.2 前后端分离部署与跨域处理部署环节是很多人的老大难但原理其实很简单。前后端分离项目意味着有两个服务地址前端静态页面由Nginx托管后端Node服务跑在指定端口。用户访问前端域名浏览器去请求API时如果前端域名和后端域名不一致就会产生跨域问题。开发阶段用Vite的proxy就解决了。但生产环境如果只用proxy那是不行的正确做法是在Nginx里配置反向代理把/api路径的请求转发给本机的Node服务。参考配置server { listen 80; server_name your-domain.com; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }注意前端路由用的history模式刷新页面时会让Nginx去找对应的物理路径会404所以try_files那行必须写把请求都引导回index.html让Vue Router自己去匹配路由。后端部署直接用PM2管理进程。安装方式npm install -g pm2启动pm2 start app.js --name sports-equipment-api。PM2的好处是崩溃自动重启服务器重启后进程不会自己恢复需要加一条启动项配置。这些细节在部署文档里都要写清不然很被动。4.3 常见问题速查表挑几个我在这个项目里实测遇到过的典型问题列一下问题现象直接原因解决办法npm报PowerShell禁止运行脚本执行策略限制管理员PowerShell执行 Set-ExecutionPolicy RemoteSigned前端请求接口报CORS未配置跨域代理或后端未允许跨域开发用Vite proxy生产用Nginx反向代理表格数据json字段读不到图片静态资源没有走统一访问路径后端设置express.static静态目录前端拼接完整URL并发借同一件器材导致超借库存扣减没有事务控制用数据库事务保证订单更新与库存扣减原子性中文乱码数据库字符集不对建库时用utf8mb4刷新页面404history路由模式Nginx配置try_files指向index.htmltoken过期后页面还在响应拦截器没处理401拦截器里统一清理token并跳登录4.4 一个细节图片上传不能只做一半器材管理里基本都会用到图片上传。我的建议是用multer这个包处理上传保存到后端服务器的uploads目录同时用express.static把该目录暴露为静态资源访问路径。数据库里存相对路径前端拿到后拼接成完整地址。上传时注意限制文件类型和大小图片只允许jpg/png大小别超过2MB否则后端接口容易被撑爆。校验逻辑放在muler的fileFilter里别只靠前端拦截防不住故意绕过前端发大文件的请求。另一个容易被忽略的点删除器材时要判断该器材是否仍存在未归还的订单。如果用户还在租着这个东西就不能从库里直接删掉只能把状态改为“已下架”。否则历史订单查询的时候关联表会变成null前端显示直接崩。最后分享一个让答辩/交付更有分量的经验如果你的目标是毕业设计答辩有一个做法非常有用在系统里加一个简单的数据统计页面。统计今日租借次数、器材利用率、逾期订单数量这些指标。不需要复杂的报表工具后端查一下表然后返回聚合数据前端用Element Plus的统计卡片加一个简单柱状图就能实现。这个小小的功能带来的收益远大于开发成本答辩时老师最怕的就是看到“只能做增删改查”的系统一旦展示出数据分析和预警能力整个项目的技术含金量立马不一样。做这个项目我最大的体会是技术栈本身不是核心难点真正的难点在于“业务流程想清楚”。不管是Node.js还是Vue它们只是工具把设备从“上架”到“出借”再到“归还”和“逾期处理”整条链路设计得严丝合缝才是这个系统真正的价值所在。按照上面的思路一步步来你会发现从头到尾其实没那么复杂先把需求拆干净剩下的只是时间问题。