最近在帮学弟学妹们看毕业设计,发现很多基于 Spring Boot 的租车系统项目,虽然功能基本都能跑通,但代码质量实在是一言难尽。最常见的几个问题就是:Controller 里塞满了业务逻辑、Service 层全是重复的 CRUD、下单接口不加锁导致超卖、接口重复提交也没做防护。这些问题不仅影响系统稳定性,在答辩时也容易被老师问住。

正好我自己也在尝试用一些 AI 辅助编码工具,比如 GitHub Copilot 和通义灵码,来提升日常开发效率。我就想,能不能把这些工具用到毕业设计里,既快速完成功能,又能保证代码有一定的质量?经过一番实践,效果出乎意料的好。下面我就以“车辆预订”这个核心模块为例,分享一下如何用 AI 辅助,高效、高质量地完成一个 Spring Boot 租车系统。

图片

1. 从痛点出发:传统手写代码的“坑”

在开始之前,我们先明确一下手动开发这类系统时常见的痛点,这也是我们引入 AI 辅助想要解决的问题:

  1. 重复的 CRUD 编码:租车系统离不开用户、车辆、订单、支付等实体,每个实体的增删改查(CRUD)代码结构高度相似。手动编写不仅枯燥,还容易因复制粘贴引入低级错误,比如字段名没改、条件判断遗漏。
  2. 模糊的事务边界:像“下单”这种操作,通常涉及检查车辆状态、扣减库存(或标记为已预订)、生成订单、初始化支付流水等多个步骤。手动管理 @Transactional 注解的范围和回滚条件,对新手来说容易出错,可能导致数据不一致。
  3. 缺失的接口幂等性:网络波动或用户重复点击,可能导致同一个下单请求被提交多次。如果没有防重机制(如 Token 机制或唯一键约束),就会产生重复订单,这是业务上不允许的。
  4. 并发安全忽视:热门车辆在促销时可能被多人同时预订。如果只是简单查询 status=0(可租)然后更新,在高并发下极有可能发生超订(一辆车被租给多个人)。

2. 技术选型:AI 辅助 vs. 传统手写

为了解决上述痛点,我对比了两种开发模式在几个关键环节的效率:

  • 实体与 CRUD 代码生成

    • 传统手写:需要手动创建 Entity、Mapper、Service、Controller 等类,逐个编写字段和方法。一个简单的实体全套下来,熟练工也要 10-15 分钟。
    • AI 辅助:在 IDEA 中安装 Copilot 或通义灵码后,你只需要先创建好数据库表,或者写好 Entity 类的字段。然后,在 Mapper 接口文件中,你只需输入 // 根据车辆ID查询 这样的注释,AI 就会自动补全 Car selectById(Long id); 方法。更进一步,你可以在 Service 接口里写 // 分页查询可用车辆,AI 能帮你生成包含分页参数和条件查询的方法签名,甚至自动在对应的 ServiceImpl 和 Controller 中生成骨架代码。效率提升超过 60%。
  • DTO 与 VO 转换

    • 传统手写:需要手动创建 CarDTOCarVO 等类,并在 Service 或 Controller 里写 BeanUtils.copyProperties 或大量的 setter 代码进行转换,容易遗漏字段。
    • AI 辅助:你可以写一个注释:// 将Car实体转换为CarResponseVO,然后开始写 public CarResponseVO convertToVo(Car car) {,AI 会自动补全方法体,将 car 的各个字段赋值给 vo 对象,非常准确。对于复杂的多表关联查询结果转换,AI 也能根据上下文给出不错的建议。
  • 异常处理与事务注解

    • 传统手写:容易忘记在 Service 方法上加 @Transactional,或者在 Controller 里做事务操作。异常处理也常常是简单的 try-catch 打印日志,没有统一的全局异常处理机制。
    • AI 辅助:当你写一个 public Order createOrder(OrderRequest request) 方法时,AI 可能会提示你加上 @Transactional(rollbackFor = Exception.class)。如果你在方法内抛出了一个自定义的 BusinessException,AI 也能识别出这是需要回滚的业务异常。你可以利用 AI 快速生成全局异常处理器 (@ControllerAdvice) 的代码框架。

3. 核心实现:AI 助力构建健壮的预订模块

我们以最核心的“车辆预订”接口为例,展示如何结合 Spring Boot、MyBatis-Plus、Redis 和 AI 工具来完成开发。

技术栈:Spring Boot 2.7 + MyBatis-Plus + Redis (Redisson客户端) + JWT

第一步:利用 AI 生成基础实体和 Mapper 首先,创建车辆 (Car) 和订单 (Order) 实体。你只需定义关键字段(如 carId, model, status, stock 等),AI 会提示你加上 @TableName, @TableId 等 MyBatis-Plus 注解。对于 BaseMapper<Car>,AI 能自动补全所有通用 CRUD 方法,无需手写 XML。

第二步:设计并生成 Service 层代码(关键)OrderService 接口中,我们定义下单方法:

/**
 * 创建车辆租赁订单
 * @param request 订单请求,包含车辆ID、租期等信息
 * @return 订单ID
 */
Long createRentalOrder(OrderCreateRequest request);

然后,在 OrderServiceImpl 中实现这个方法。这里是我们需要 AI 重点辅助的地方。我们可以先写出方法签名和基本的注释,然后让 AI 帮我们填充核心逻辑。一个经过优化的、考虑并发和幂等的实现骨架如下:

@Override
@Transactional(rollbackFor = Exception.class) // AI 常能自动建议加上事务注解
public Long createRentalOrder(OrderCreateRequest request) {
    // 1. 参数基础校验 (AI 可生成非空、范围等校验代码)
    if (request.getCarId() == null || request.getRentalDays() <= 0) {
        throw new BusinessException(ErrorCode.PARAMS_ERROR);
    }

    // 2. 幂等性控制:基于请求唯一标识(如前端传来的orderToken)查询Redis
    String orderToken = request.getOrderToken();
    if (StringUtils.isNotBlank(orderToken)) {
        String key = "order:token:" + orderToken;
        // AI 可能生成 redisTemplate.opsForValue().get(key) 的代码
        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
            throw new BusinessException(ErrorCode.REPEATED_REQUEST);
        }
    }

    // 3. 防超订:使用 Redisson 分布式锁锁定具体车辆资源
    String lockKey = "car:lock:" + request.getCarId();
    RLock lock = redissonClient.getLock(lockKey);
    try {
        // 尝试加锁,最多等待3秒,锁持有10秒自动释放以防死锁
        boolean isLocked = lock.tryLock(3, 10, TimeUnit.SECONDS);
        if (!isLocked) {
            throw new BusinessException(ErrorCode.SYSTEM_BUSY);
        }

        // 4. 在锁内查询车辆状态与库存(防止并发下的脏读)
        Car car = carService.getById(request.getCarId());
        if (car == null || car.getStatus() != CarStatus.AVAILABLE.getCode()) {
            throw new BusinessException(ErrorCode.CAR_NOT_AVAILABLE);
        }
        // 假设库存字段为 availableStock
        if (car.getAvailableStock() < 1) {
            throw new BusinessException(ErrorCode.CAR_STOCK_NOT_ENOUGH);
        }

        // 5. 扣减库存(乐观锁版本号更新,AI 可能生成 updateById 但需要你引导用 updateWrapper 带版本条件)
        boolean updateSuccess = carService.lambdaUpdate()
                .eq(Car::getId, car.getId())
                .eq(Car::getVersion, car.getVersion()) // 版本号条件
                .set(Car::getAvailableStock, car.getAvailableStock() - 1)
                .set(Car::getVersion, car.getVersion() + 1)
                .update();
        if (!updateSuccess) {
            // 乐观锁更新失败,说明数据已被其他线程修改,可重试或直接抛异常
            throw new BusinessException(ErrorCode.OPERATION_FAILED);
        }

        // 6. 生成订单实体并保存
        Order order = new Order();
        // AI 可以帮助完成属性映射,如 order.setUserId(UserContext.getCurrentUserId());
        order.setCarId(request.getCarId());
        order.setOrderNo(IdUtil.generateOrderNo()); // 生成唯一订单号
        order.setStatus(OrderStatus.UNPAID.getCode());
        // ... 设置其他字段
        orderMapper.insert(order);

        // 7. 幂等Token写入Redis,设置过期时间(如5分钟)
        if (StringUtils.isNotBlank(orderToken)) {
            redisTemplate.opsForValue().set("order:token:" + orderToken, "1", 5, TimeUnit.MINUTES);
        }

        return order.getId();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new BusinessException(ErrorCode.SYSTEM_ERROR);
    } finally {
        // 8. 无论如何,最终释放锁
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

AI 在生成上述代码时,能极大地减少你编写样板代码的时间,如 Redis 操作、异常捕获、锁的获取与释放模板。你需要做的是提供清晰的注释引导,并在生成后检查关键逻辑(如乐观锁更新、异常处理是否合理)。

图片

4. 性能与安全考量

  • 压力测试 (QPS):使用 JMeter 对优化后的下单接口进行压测。在配置了 Redis 防重和分布式锁之后,相较于无防护的版本,系统在防止超订和重复订单方面达到了 100% 的准确率。由于锁的引入,吞吐量 (QPS) 会有所下降,但通过缩小锁粒度(只锁具体车辆ID)和控制锁等待时间,在单机环境下仍能保持数百 QPS,完全满足毕业设计答辩的演示需求。可以将压测结果(如:无防护下出现超卖,有防护后零错误)做成图表,是答辩的亮点。

  • JWT 鉴权与 Token 刷新:使用 jjwt 库实现 JWT。AI 可以帮助生成 JwtUtil 工具类,包含生成 Token、解析 Token、刷新 Token 等方法。关键点是设计刷新机制:在 Token 即将过期时,前端携带旧 Token 调用刷新接口,后端验证后签发新 Token。AI 可以辅助生成刷新接口的逻辑,注意需要将旧 Token 加入短期的黑名单(存 Redis),防止同一 Token 被多次刷新。

  • SQL 注入防护:坚持使用 MyBatis-Plus 的 Lambda 查询或明确的 @Param 注解,AI 生成的 SQL 片段也会基于这些安全的写法,从根本上避免拼接字符串导致的 SQL 注入问题。

5. 生产环境避坑:AI 生成代码的“质检”

AI 不是万能的,它生成的代码需要经过严格的人工 Review 和测试:

  1. N+1 查询问题:AI 在生成关联查询时,可能会写出在循环中查询数据库的代码。例如,查询一个订单列表,然后在循环里根据每个订单的 carId 去查车辆信息。你需要识别出这种情况,并引导 AI 或手动修改为使用 MyBatis-Plus 的 @TableField 关联查询或者手动编写连表 SQL,一次性查出所有数据。

  2. 空指针未校验:AI 生成的代码有时会假设对象不为空。例如,car.getModel().getName(),如果 cargetModel() 可能为 null,这里就会 NPE。你必须对关键的链式调用进行空指针判断,或者使用 Optional 进行包装。

  3. 事务传播行为不当:AI 可能会在不需要事务的方法上也加上 @Transactional,或者传播行为使用不当。你需要根据业务逻辑,仔细检查每个 @Transactional 注解的 propagation 属性是否合理。

  4. 异常处理过于宽泛:AI 可能生成 catch (Exception e) 然后只打印日志,这可能会“吃掉”重要的业务异常,导致事务不回滚。需要确保业务异常被抛出,或者进行恰当的处理。

人工 Review 流程建议

  • 功能正确性:对照需求,走查核心逻辑分支。
  • 数据一致性:检查事务边界和锁的使用是否合理。
  • 性能:检查是否有循环查询、大对象序列化等潜在性能瓶颈。
  • 安全:检查输入校验、权限校验、敏感信息是否脱敏。
  • 代码风格:虽然 AI 生成的代码风格通常不错,但仍需统一团队规范(如命名、注释)。

6. 总结与思考

通过将 AI 辅助工具引入 Spring Boot 租车毕设的开发,我个人的体验是:在熟悉了工具的使用模式后,编码效率的提升是实实在在的,尤其是在搭建项目骨架、编写重复的 CRUD 和模板代码时,估计能节省 40%-50% 的时间。更重要的是,AI 能基于海量开源代码,提供一些“最佳实践”的代码片段,比如分布式锁的写法、事务注解的放置,这对初学者理解这些概念很有帮助。

给你的建议:不妨选择你毕设中的一个模块(比如“用户管理”或“车辆信息管理”),尝试用 GitHub Copilot 或通义灵码从头开始重构。感受一下 AI 如何响应你的注释,如何补全代码。完成后,对比一下手写版本和 AI 辅助版本在代码结构、异常处理、注释完整性上的区别。

最后,留一个思考题:“AI 生成代码的责任边界在哪里?” 当代码出现 Bug 甚至安全漏洞时,责任在于提示词不清晰的开发者,还是在于生成代码的 AI?我的看法是,AI 是强大的“副驾驶”,但“驾驶员”始终是我们自己。我们对最终代码的质量、安全性和业务正确性负有全部责任。AI 提高了我们驾驶(编码)的速度和舒适度,但路线规划(架构设计)和交通规则(代码规范)仍需我们牢牢掌握。用好 AI,让它成为你完成优秀毕业设计的得力助手。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐