1. 项目背景与意义
随着摄影爱好者和短视频创作者的快速增长,相机、镜头等摄影器材的购买成本较高,且部分用户仅在特定场景下需要使用专业设备,因此相机租赁需求日益旺盛。传统的线下租赁模式存在信息不透明、库存管理困难、租借流程繁琐、设备归还状态难以追踪等问题。
基于SpringBoot+Vue的相机租赁管理系统,旨在通过信息化手段实现相机设备的在线展示、预约租赁、订单管理、库存跟踪和归还验收等全流程数字化管理。该系统能够有效降低租赁企业的运营成本,提升设备周转效率,同时为用户提供便捷、透明的租赁体验,具有较高的实用价值和推广意义。
2. 系统技术栈
本系统采用前后端分离架构,后端基于SpringBoot框架,前端基于Vue框架,数据库使用MySQL,整体技术栈如下:
| 层次 | 技术选型 | 说明 |
|---|---|---|
| 后端框架 | SpringBoot | 提供RESTful API,负责业务逻辑处理与数据持久化 |
| 持久层 | MyBatis-Plus | 简化数据库操作,支持分页查询与条件构造器 |
| 数据库 | MySQL | 存储用户、设备、订单、租赁记录等核心数据 |
| 前端框架 | Vue 3 + Element Plus | 构建用户界面,提供组件化开发与响应式布局 |
| 构建工具 | Vite | 前端项目构建与开发服务器 |
| HTTP客户端 | Axios | 前后端数据交互,封装请求与响应拦截器 |
| 权限认证 | JWT + Spring Security | 实现用户登录认证与接口访问控制 |
3. 系统核心功能模块
系统主要分为用户端和管理端两大模块,核心功能包括:
- 用户管理:用户注册、登录、个人信息维护。
- 设备管理:相机、镜头的添加、编辑、上下架、库存管理。
- 租赁管理:用户提交租赁订单,管理员审核、确认发货、登记归还。
- 订单管理:订单状态跟踪(待审核、租赁中、已归还、已取消)。
- 费用结算:根据租赁天数自动计算租金,支持押金管理。
- 数据统计:设备租赁排行、订单数量统计、营收概览。
4. 数据库设计
系统核心数据表包括用户表、设备表、订单表等,以下为主要表结构设计:
-- 用户表 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, phone VARCHAR(20), role VARCHAR(20) DEFAULT 'USER', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 设备表 CREATE TABLE camera_device ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, brand VARCHAR(50), model VARCHAR(50), daily_price DECIMAL(10,2) NOT NULL, deposit DECIMAL(10,2), stock INT DEFAULT 1, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 租赁订单表 CREATE TABLE rental_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, user_id BIGINT NOT NULL, device_id BIGINT NOT NULL, start_date DATE NOT NULL, end_date DATE NOT NULL, total_price DECIMAL(10,2), status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );5. 核心代码实现
5.1 后端:SpringBoot 控制器与业务逻辑
以下为设备管理模块的核心代码,包含设备列表分页查询与新增设备接口:
@RestController @RequestMapping("/api/device") public class DeviceController { @Autowired private DeviceService deviceService; @GetMapping("/page") public Result<Page<CameraDevice>> page( @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(required = false) String keyword) { Page<CameraDevice> page = deviceService.queryPage(pageNum, pageSize, keyword); return Result.success(page); } @PostMapping("/add") public Result<?> add(@RequestBody CameraDevice device) { deviceService.save(device); return Result.success("新增设备成功"); } }设备查询分页逻辑实现:
@Service public class DeviceServiceImpl implements DeviceService { @Autowired private CameraDeviceMapper deviceMapper; @Override public Page<CameraDevice> queryPage(Integer pageNum, Integer pageSize, String keyword) { LambdaQueryWrapper<CameraDevice> wrapper = new LambdaQueryWrapper<>(); if (StringUtils.hasText(keyword)) { wrapper.like(CameraDevice::getName, keyword) .or().like(CameraDevice::getBrand, keyword); } wrapper.orderByDesc(CameraDevice::getCreateTime); return deviceMapper.selectPage(new Page<>(pageNum, pageSize), wrapper); } }5.2 后端:租赁订单创建与费用计算
用户提交租赁订单时,系统根据租赁天数自动计算租金,并校验设备库存:
@Service public class RentalOrderServiceImpl implements RentalOrderService { @Autowired private RentalOrderMapper orderMapper; @Autowired private CameraDeviceMapper deviceMapper; @Override @Transactional public Result<?> createOrder(RentalOrder order) { CameraDevice device = deviceMapper.selectById(order.getDeviceId()); if (device == null || device.getStock() <= 0) { return Result.error("设备库存不足"); } // 计算租赁天数 long days = ChronoUnit.DAYS.between(order.getStartDate(), order.getEndDate()); if (days <= 0) { return Result.error("租赁日期不合法"); } BigDecimal total = device.getDailyPrice().multiply(BigDecimal.valueOf(days)); order.setTotalPrice(total); order.setOrderNo("R" + System.currentTimeMillis()); order.setStatus(0); orderMapper.insert(order); // 扣减库存 device.setStock(device.getStock() - 1); deviceMapper.updateById(device); return Result.success("订单提交成功"); } }5.3 前端:Vue 设备列表页面
前端使用 Vue 3 组合式 API 实现设备列表展示与分页查询:
<template> <el-table :data="deviceList" border stripe> <el-table-column prop="name" label="设备名称" /> <el-table-column prop="brand" label="品牌" /> <el-table-column prop="model" label="型号" /> <el-table-column prop="dailyPrice" label="日租金" /> <el-table-column prop="stock" label="库存" /> <el-table-column label="操作"> <template #default="{ row }"> <el-button type="primary" size="small" @click="rent(row)">立即租赁</el-button> </template> </el-table-column> </el-table> </template> <script setup> import { ref, onMounted } from 'vue' import request from '@/utils/request' const deviceList = ref([]) const loadDevices = async () => { const res = await request.get('/api/device/page', { params: { pageNum: 1, pageSize: 10 } }) deviceList.value = res.data.records } const rent = (row) => { // 跳转租赁下单页面 console.log('租赁设备:', row) } onMounted(loadDevices) </script>5.4 前端:Axios 请求封装
import axios from 'axios' import { ElMessage } from 'element-plus' const request = axios.create({ baseURL: '/api', timeout: 10000 }) // 请求拦截器:携带 token request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = 'Bearer ' + token } return config }) // 响应拦截器:统一处理错误 request.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { ElMessage.error(res.msg || '请求失败') return Promise.reject(new Error(res.msg)) } return res }, error => { ElMessage.error('网络异常,请稍后重试') return Promise.reject(error) } ) export default request6. 总结
基于SpringBoot+Vue的相机租赁管理系统,通过前后端分离架构实现了设备管理、租赁下单、订单跟踪和费用结算等核心业务流程。系统界面友好、接口清晰、扩展性强,能够有效提升相机租赁业务的运营效率。后续可进一步引入支付接口、设备定位追踪、信用免押等功能,持续完善系统能力。