1. 项目概述
作为一名长期从事企业级应用开发的工程师,我最近完成了一个基于微信小程序的汽车租赁系统项目。这个项目让我深刻体会到小程序在传统行业数字化转型中的巨大潜力。下面我将从技术选型、架构设计到具体实现,完整分享这个项目的开发经验。
汽车租赁行业近年来发展迅速,但传统模式存在信息不透明、流程繁琐等问题。我们团队决定利用微信小程序无需安装、即用即走的特性,结合SpringBoot后端的高效开发优势,打造一个轻量级但功能完备的租赁平台。
2. 技术架构设计
2.1 技术栈选型
在项目启动阶段,我们经过多轮技术评估,最终确定了以下技术组合:
前端技术栈:
- 微信小程序原生框架 + UniApp跨平台方案
- Vant Weapp组件库
- ECharts for Weixin数据可视化
选择微信小程序原生开发而非纯UniApp,主要是考虑到:
- 更好的性能表现(特别是长列表渲染)
- 更完善的微信生态API支持
- 更低的用户使用门槛
后端技术栈:
- SpringBoot 2.7.3(长期支持版本)
- MyBatis-Plus 3.5.1(简化CRUD操作)
- Redis 6.2(缓存和会话管理)
- MySQL 8.0(关系型数据库)
- MinIO(对象存储服务)
这个技术组合的亮点在于:
- SpringBoot的自动配置大幅减少了XML配置
- MyBatis-Plus的ActiveRecord模式提升开发效率
- Redis不仅用于缓存,还实现了分布式锁机制
2.2 系统架构设计
我们采用经典的三层架构,但针对小程序特点做了优化:
客户端层 ├─ 微信小程序 └─ 管理端H5 接入层 ├─ API网关(Spring Cloud Gateway) └─ 负载均衡(Nginx) 业务层 ├─ 用户服务 ├─ 车辆服务 └─ 订单服务 数据层 ├─ MySQL(主从复制) └─ Redis集群特别说明几个关键设计决策:
- 将原生的微信登录鉴权与JWT结合,既保证安全性又减少接口调用次数
- 使用Redisson实现分布式锁,解决高并发下的超租问题
- 采用MinIO替代FastDFS,简化文件存储部署复杂度
3. 核心功能实现
3.1 用户认证模块
用户认证是系统的门户,我们实现了完整的OAuth2.0流程:
// 简化后的认证核心代码 @RestController @RequestMapping("/auth") public class AuthController { @Autowired private WxMaService wxMaService; @PostMapping("/login") public R login(@RequestBody LoginDTO dto) { // 1. 小程序code换openid WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(dto.getCode()); // 2. 查询或创建用户 User user = userService.getByOpenid(session.getOpenid()); if(user == null) { user = new User(); user.setOpenid(session.getOpenid()); userService.save(user); } // 3. 生成JWT String token = JwtUtil.generateToken(user.getId()); return R.ok().data("token", token); } }关键点说明:
- 使用微信提供的code2session接口获取openid
- 首次登录自动创建用户记录
- JWT有效期为7天,采用HS512算法签名
3.2 车辆预订流程
车辆预订是核心业务,我们实现了完整的分布式事务:
@Transactional public BookingResult bookCar(Long userId, Long carId, DateRange range) { // 1. 获取分布式锁 RLock lock = redissonClient.getLock("car:" + carId); try { lock.lock(5, TimeUnit.SECONDS); // 2. 检查车辆状态 Car car = carService.getById(carId); if(car.getStatus() != CarStatus.AVAILABLE) { throw new BusinessException("车辆不可用"); } // 3. 检查时间冲突 boolean conflict = bookingService.checkConflict(carId, range); if(conflict) { throw new BusinessException("时间冲突"); } // 4. 创建订单 Order order = new Order(); order.setUserId(userId); order.setCarId(carId); order.setStartTime(range.getStart()); order.setEndTime(range.getEnd()); orderService.save(order); // 5. 更新车辆状态 car.setStatus(CarStatus.BOOKED); carService.updateById(car); return BookingResult.success(order.getId()); } finally { lock.unlock(); } }注意事项:
- 锁的粒度控制在单车级别
- 锁的超时时间设置为5秒,避免死锁
- 采用Spring事务注解保证数据一致性
3.3 支付集成方案
支付模块我们对接了微信支付V3接口:
public PaymentResponse createPayment(Order order, String openid) { WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); request.setOutTradeNo(order.getOrderNo()); request.setAmount(new Amount().setTotal(order.getAmount())); request.setDescription("车辆租赁费用"); request.setPayer(new Payer().setOpenid(openid)); try { return wxPayService.createOrderV3(request); } catch (WxPayException e) { log.error("支付创建失败", e); throw new BusinessException("支付创建失败"); } }支付流程优化点:
- 采用服务端预生成订单号,避免重复支付
- 支付结果通过微信回调通知,而非前端轮询
- 关键日志记录到ELK日志系统
4. 性能优化实践
4.1 缓存策略设计
我们采用多级缓存架构提升系统响应速度:
- 本地缓存:使用Caffeine缓存热点数据
@Bean public CacheManager cacheManager() { CaffeineCacheManager manager = new CaffeineCacheManager(); manager.setCaffeine(Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(1000)); return manager; }- 分布式缓存:Redis缓存车辆信息和配置数据
@Cacheable(value = "cars", key = "#id") public Car getById(Long id) { return baseMapper.selectById(id); }- CDN加速:静态资源通过腾讯云CDN分发
4.2 数据库优化
针对MySQL我们做了以下优化:
- 索引优化:
ALTER TABLE `booking_order` ADD INDEX `idx_car_time` (`car_id`, `start_time`, `end_time`);- 查询优化:
// 使用MyBatis-Plus的QueryWrapper优化查询 public Page<CarVO> queryAvailableCars(QueryCondition cond) { return baseMapper.selectPage(cond.toPage(), new QueryWrapper<Car>() .eq("status", CarStatus.AVAILABLE) .between("daily_price", cond.getMinPrice(), cond.getMaxPrice()) .orderByDesc("create_time")); }- 分库分表:订单表按月分表,解决单表数据量过大问题
5. 安全防护措施
5.1 接口安全
- 防SQL注入:
@RestController @RequestMapping("/car") public class CarController { @GetMapping("/search") public R search(@RequestParam String keyword) { // 使用MyBatis-Plus的自动防注入机制 return R.ok().data("list", carService.list(new QueryWrapper<Car>() .like("name", keyword))); } }- XSS防护:
@Bean public FilterRegistrationBean<XssFilter> xssFilter() { FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new XssFilter()); registration.addUrlPatterns("/*"); return registration; }5.2 数据安全
- 敏感数据加密:
@Column(name = "mobile") @FieldEncrypt private String mobile;- 日志脱敏:
@Bean public SensitiveDataConverter sensitiveDataConverter() { return new SensitiveDataConverter() .addRule("mobile", "(.{3}).*(.{4})", "$1****$2"); }6. 部署架构
我们采用Docker Compose实现一键部署:
version: '3' services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} volumes: - ./data/mysql:/var/lib/mysql redis: image: redis:6.2 ports: - "6379:6379" app: build: . ports: - "8080:8080" depends_on: - mysql - redis部署注意事项:
- 使用.env文件管理环境变量
- 采用Nginx反向代理和负载均衡
- 使用Prometheus + Grafana监控系统状态
7. 典型问题解决方案
7.1 高并发场景下的超租问题
现象:同一辆车被多个用户同时预订 解决方案:
- 使用Redis分布式锁
- 数据库乐观锁
- 引入预约队列机制
7.2 微信支付回调丢失
现象:用户已支付但系统未收到回调 解决方案:
- 实现主动查询机制
- 建立本地事务日志
- 设置补偿任务定时检查
7.3 小程序性能优化
- 图片懒加载
<image lazy-load src="{{car.image}}"></image>- 数据分页加载
Page({ data: { pageSize: 10, loading: false }, onReachBottom() { if(this.data.loading) return; this.setData({loading: true}); this.loadNextPage().finally(() => { this.setData({loading: false}); }); } })8. 项目总结与展望
经过三个月的开发和优化,系统已经稳定运行在客户的生产环境。目前日活用户约2000人,峰值QPS达到500。在开发过程中,我们积累了几个重要经验:
- 小程序端的性能瓶颈往往在图片加载,建议:
- 使用WebP格式图片
- 实现图片懒加载
- 使用CDN加速
- 后端接口设计要特别注意:
- 接口版本管理
- 参数校验
- 错误码规范
- 对于租赁类业务,要特别重视:
- 库存准确性
- 时间冲突检测
- 状态一致性
未来我们计划:
- 增加智能推荐算法
- 对接更多支付渠道
- 实现电子合同签署功能