SpringBoot+Vue设备租赁系统开发实战

SpringBoot+Vue设备租赁系统开发实战 1. 项目概述这个网上租赁系统管理系统是我去年为一个本地设备租赁公司开发的实际项目采用SpringBootVue的前后端分离架构完整实现了从设备管理、用户预约到订单结算的全流程数字化。系统上线后帮助客户将业务处理效率提升了60%特别适合中小型租赁企业进行数字化转型参考。不同于简单的CRUD系统这个项目在权限控制、库存预警和支付对接等业务关键点做了深度优化。我会从技术选型、核心模块实现到部署上线的完整过程进行拆解分享那些在文档里找不到的实战经验。2. 技术栈选型解析2.1 后端技术组合选择SpringBoot 2.7 MyBatis-Plus的组合主要基于快速启动SpringBoot的自动配置让项目初始化时间缩短70%动态SQL优势MyBatis-Plus的Lambda表达式让复杂查询代码量减少40%事务控制通过Transactional注解实现多表操作的原子性数据库选用MySQL 8.0而非5.7版本主要考虑-- JSON字段支持存储设备动态参数 ALTER TABLE equipment ADD spec JSON COMMENT 设备规格参数; -- 窗口函数用于租赁热榜统计 SELECT item_id, RANK() OVER(ORDER BY rent_count DESC) AS hot_rank FROM rental_stats;2.2 前端技术方案Vue 3 Element Plus的组合带来Composition API使代码复用率提升50%基于WebSocket的实时库存提醒动态路由实现权限级别的菜单渲染典型的路由守卫配置router.beforeEach((to, from, next) { if (to.meta.requiresAuth !store.getters.isLoggedIn) { next({ path: /login, query: { redirect: to.fullPath } }) } else { next() } })3. 核心业务模块实现3.1 租赁流程状态机设计采用状态模式实现订单状态流转public interface RentalState { void handleRequest(RentalContext context); } // 具体状态实现 public class PendingState implements RentalState { Override public void handleRequest(RentalContext context) { if(paymentService.checkPaid()){ context.setState(new ConfirmedState()); } } }状态转换规则[预订中] --支付成功-- [已确认] --超时未支付-- [已取消] [已确认] --取货-- [使用中] [使用中] --归还-- [待评价]3.2 库存并发控制方案采用乐观锁解决超租问题Update(UPDATE equipment SET stock stock - #{num} WHERE id #{id} AND stock #{num}) int reduceStock(Param(id) Long id, Param(num) int num);配合Redis实现分布式锁public boolean tryLock(String key, long expireSec) { return redisTemplate.opsForValue() .setIfAbsent(key, 1, expireSec, TimeUnit.SECONDS); }4. 关键问题解决方案4.1 支付结果异步通知处理微信支付回调的注意事项验证签名防止伪造请求处理幂等性问题相同通知可能多次触发设置合理的超时和重试机制回调处理核心代码PostMapping(/pay/notify) public String handleNotify(RequestBody String xmlData) { // 1. 验签 if(!wxPayService.verifySign(xmlData)){ return xmlreturn_codeFAIL/return_code/xml; } // 2. 幂等检查 if(orderService.checkPaid(orderNo)){ return successResponse; } // 3. 业务处理 orderService.processPayment(orderNo); }4.2 定时任务设计使用Spring Scheduled要注意集群环境下需要加分布式锁长时间任务要配置线程池异常处理要记录完整上下文每日结算任务配置示例Scheduled(cron 0 0 2 * * ?) public void dailySettlement() { if(lockService.tryLock(settlement_lock, 60)){ try { settlementService.processAllShops(); } finally { lockService.unlock(settlement_lock); } } }5. 性能优化实践5.1 接口响应优化针对/list接口的优化措施添加二级缓存Redis Caffeine使用DTO投影减少数据传输量启用Gzip压缩优化前后对比指标优化前优化后平均响应时间320ms85ms吞吐量150QPS600QPS5.2 数据库优化关键索引设计-- 订单查询优化 ALTER TABLE rental_order ADD INDEX idx_user_status (user_id, status); -- 设备分类查询 ALTER TABLE equipment ADD INDEX idx_category_stock (category_id, stock);特别注意varchar字段索引长度控制在20个字符以内过长的索引会影响写入性能6. 安全防护措施6.1 认证授权体系JWT实现要点使用RS256非对称加密刷新令牌机制黑名单处理令牌生成示例public String generateToken(UserDetails user) { return Jwts.builder() .setSubject(user.getUsername()) .setExpiration(new Date(System.currentTimeMillis() 3600000)) .signWith(SignatureAlgorithm.RS256, privateKey) .compact(); }6.2 敏感数据保护采用AES加密存储字段Column(columnDefinition VARBINARY(255)) Convert(converter CryptoConverter.class) private String bankCardNo;加密转换器实现public class CryptoConverter implements AttributeConverterString, byte[] { private static final SecretKeySpec key new SecretKeySpec(...); Override public byte[] convertToDatabaseColumn(String attribute) { // AES加密实现 } }7. 部署与监控7.1 Docker化部署后端Dockerfile关键配置FROM openjdk:17-jdk-alpine VOLUME /tmp ARG JAR_FILEtarget/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT [java,-Djava.security.egdfile:/dev/./urandom,-jar,/app.jar]前端Nginx配置要点location / { try_files $uri $uri/ /index.html; gzip_static on; expires 1y; add_header Cache-Control public; }7.2 监控方案SpringBoot Actuator配置management: endpoints: web: exposure: include: health,metrics,prometheus metrics: export: prometheus: enabled: true关键监控指标订单创建成功率支付回调平均处理时间库存更新并发冲突率8. 踩坑实录MyBatis批量插入性能问题 初始方案使用foreach拼接SQL当批量插入1000条数据时耗时8秒。改用BatchExecutor后降至200msSqlSession session sqlSessionFactory.openSession(ExecutorType.BATCH); try { Mapper mapper session.getMapper(Mapper.class); for (Item item : items) { mapper.insert(item); } session.commit(); } finally { session.close(); }Vue路由懒加载导致的空白页 开发环境正常但生产环境出现白屏原因是路由组件使用了动态import// 错误写法生产环境失效 component: () import(/views/${filePath}.vue) // 正确写法 component: () import(/views/equipment/list.vue)MySQL时区陷阱 发现订单创建时间比实际晚8小时解决方案spring: datasource: url: jdbc:mysql://localhost:3306/rental?serverTimezoneAsia/Shanghai这个项目让我深刻体会到业务系统的复杂度往往不在于技术本身而在于对领域规则的理解和异常情况的处理。比如租赁业务中的信用积分计算规则就迭代了5个版本才稳定下来。建议开发类似系统时前期一定要花足够时间梳理业务状态机。