1. 项目概述
农业机械租赁服务平台是一个基于SpringBoot框架开发的B2B2C电商系统,主要解决农业生产中农机设备使用率低、农户购买成本高的问题。我在实际开发中发现,这类平台需要特别关注三个核心问题:农机设备的时空调度、租赁流程的灵活配置以及不同用户角色的权限管控。
系统采用典型的三层架构设计,前端使用Vue.js+ElementUI实现响应式界面,后端基于SpringBoot 2.7整合MyBatis-Plus进行数据持久化操作。数据库选用MySQL 8.0,利用其JSON字段类型存储农机设备的动态参数。特别值得注意的是,我们为农机设备设计了"时间片"租赁模式,这是区别于普通电商系统的关键特征。
2. 技术架构解析
2.1 基础技术栈
开发环境配置需要特别注意版本兼容性:
- JDK 1.8(必须使用u191以上版本避免NPE问题)
- SpringBoot 2.7.18(规避了2.7.x系列中的循环依赖缺陷)
- MyBatis-Plus 3.5.3(配套的代码生成器能大幅提升开发效率)
- MySQL 8.0.28(推荐使用AWS RDS云实例)
在pom.xml中需要特别关注这些核心依赖:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2 关键架构设计
- 分布式锁实现:使用Redis + Redisson解决农机库存超卖问题
java复制// 农机库存扣减示例
RLock lock = redissonClient.getLock("equipment:"+equipmentId);
try {
lock.lock(5, TimeUnit.SECONDS);
// 库存检查与扣减逻辑
} finally {
lock.unlock();
}
- 时间片调度算法:处理农机设备的租赁时间冲突
java复制// 时间片冲突检测逻辑
public boolean checkTimeConflict(LocalDateTime start, LocalDateTime end, Long equipmentId) {
return rentalMapper.selectCount(new QueryWrapper<RentalOrder>()
.eq("equipment_id", equipmentId)
.and(wrapper -> wrapper
.between("rental_start", start, end)
.or()
.between("rental_end", start, end)
)) > 0;
}
- 权限控制体系:基于Spring Security的多角色动态权限方案
java复制@PreAuthorize("hasAnyRole('FARMER','ADMIN')")
@PostMapping("/order/create")
public R createOrder(@Valid @RequestBody OrderDTO dto) {
// 订单创建逻辑
}
3. 核心业务实现
3.1 农户端功能实现
- 农机浏览与筛选:
- 采用Elasticsearch实现农机设备的全文检索
- 空间索引支持按距离排序(5km内的农机优先展示)
- 价格筛选使用滑动区间控件,后端采用BETWEEN查询
- 购物车设计要点:
java复制// 购物车数据结构示例
public class CartItem {
private Long equipmentId;
private LocalDate startDate;
private LocalDate endDate;
private Integer quantity;
// 附加服务选项
private List<Long> serviceIds;
}
- 订单状态机设计:
mermaid复制stateDiagram
[*] --> PENDING_PAYMENT
PENDING_PAYMENT --> PAID: 支付成功
PAID --> DELIVERING: 商家确认
DELIVERING --> IN_USE: 用户签收
IN_USE --> RETURNED: 归还设备
RETURNED --> FINISHED: 双方评价
3.2 租赁商端实现
- 资质审核流程:
- 使用阿里云OSS存储资质文件
- 审核记录表设计包含三级审批字段
- 采用状态模式实现审核流程控制
- 设备管理核心表结构:
sql复制CREATE TABLE `farm_equipment` (
`id` bigint NOT NULL AUTO_INCREMENT,
`owner_id` bigint NOT NULL COMMENT '租赁商ID',
`category_id` int NOT NULL COMMENT '农机分类',
`name` varchar(100) NOT NULL,
`spec_params` json DEFAULT NULL COMMENT '规格参数JSON',
`rental_price` decimal(10,2) NOT NULL COMMENT '日租价格',
`deposit_amount` decimal(10,2) DEFAULT NULL,
`status` tinyint NOT NULL DEFAULT '1' COMMENT '1-可租 2-维修中',
PRIMARY KEY (`id`),
KEY `idx_category` (`category_id`),
KEY `idx_owner` (`owner_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 库存动态计算方案:
java复制// 每日可用库存计算
public int getAvailableCount(Long equipmentId, LocalDate date) {
Integer total = equipmentMapper.selectStockTotal(equipmentId);
Integer used = rentalMapper.selectRentedCount(equipmentId, date);
return total - (used == null ? 0 : used);
}
4. 特殊业务场景处理
4.1 农机保险服务集成
- 保险服务接入方案:
- 设计保险产品表与农机类目关联
- 保费计算采用基础保费+设备价值系数
- 保单生成使用模板引擎+PDF导出
- 保险订单同步逻辑:
java复制@Transactional
public void createInsuranceOrder(InsuranceDTO dto) {
// 1. 创建保险记录
insuranceMapper.insert(record);
// 2. 调用第三方保险API
ThirdPartyResponse resp = insuranceService.callAPI(dto);
// 3. 更新保单号
record.setPolicyNo(resp.getPolicyNo());
insuranceMapper.updateById(record);
}
4.2 物流配送调度
- 自提点管理:
- 使用高德地图API计算最近自提点
- 自提凭证采用QR码+短信双重验证
- 自提时间窗口配置(避开农忙时段)
- 配送路线优化算法:
java复制// 基于K-means的配送聚类
public List<DeliveryCluster> clusterOrders(List<Order> orders) {
// 1. 坐标点转换
List<Point> points = convertToPoints(orders);
// 2. 执行聚类算法
KMeansClusterer<Point> clusterer = new KMeansClusterer<>(3, 100);
List<CentroidCluster<Point>> results = clusterer.cluster(points);
// 3. 生成配送路线
return buildRoutes(results);
}
5. 性能优化实践
5.1 缓存策略设计
- 农机详情缓存方案:
java复制@Cacheable(value = "equipment", key = "#id", unless = "#result == null")
public EquipmentDetailVO getEquipmentDetail(Long id) {
return equipmentMapper.selectDetailById(id);
}
@CacheEvict(value = "equipment", key = "#id")
public void updateEquipment(Equipment equipment) {
equipmentMapper.updateById(equipment);
}
- 热点数据预加载:
java复制@Scheduled(cron = "0 0 6 * * ?")
public void preloadHotEquipment() {
List<Long> hotIds = equipmentMapper.selectHotIds();
hotIds.forEach(id -> {
equipmentService.getEquipmentDetail(id);
});
}
5.2 数据库优化
- 农机查询SQL优化:
sql复制-- 原始查询
SELECT * FROM farm_equipment WHERE category_id = 1 AND status = 1;
-- 优化后使用覆盖索引
ALTER TABLE farm_equipment ADD INDEX idx_category_status (category_id, status);
- 订单分表策略:
- 按用户ID哈希分表(user_id % 10)
- 使用Sharding-JDBC实现透明分片
- 历史订单归档方案设计
6. 安全防护措施
6.1 支付安全
- 支付签名验证流程:
java复制public boolean verifySign(PaymentNotify notify) {
String sign = notify.getSign();
String content = buildSignContent(notify);
return RSAUtil.verify(content, sign, publicKey);
}
- 防重放攻击方案:
- 使用Redis记录已处理支付单号
- 设置5分钟有效期
- 异步通知幂等处理
6.2 数据权限控制
- 租户数据隔离方案:
java复制@Interceptor
public class TenantInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Long tenantId = getTenantFromToken(request);
TenantContext.setCurrentTenant(tenantId);
return true;
}
}
- SQL自动注入租户条件:
java复制public class TenantSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methods = super.getMethodList(mapperClass);
methods.add(new TenantSelect());
return methods;
}
}
7. 部署与监控
7.1 容器化部署
- Docker Compose配置要点:
yaml复制services:
app:
image: openjdk:8-jre
environment:
- SPRING_PROFILES_ACTIVE=prod
volumes:
- ./logs:/app/logs
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 5s
retries: 3
- 日志收集方案:
- 使用Logstash收集SpringBoot日志
- 按天滚动存储到Elasticsearch
- 关键业务日志单独标记
7.2 监控告警
- Prometheus监控指标:
java复制@Bean
public MeterRegistryCustomizer<PrometheusMeterRegistry> configurer() {
return registry -> registry.config().commonTags("application", "farm-rental");
}
- 关键业务指标监控:
- 农机出租率(按小时统计)
- 订单转化漏斗分析
- 支付成功率告警
8. 开发经验总结
在实际开发过程中,有几个关键点需要特别注意:
- 时间处理陷阱:
- 农机租赁必须使用LocalDateTime而非时间戳
- 跨天计算要处理TimeZone问题
- 节假日特殊价格需要单独配置
- 库存扣减优化:
java复制// 错误做法:先查询后更新
int stock = getStock();
if(stock > 0) {
updateStock(stock - 1);
}
// 正确做法:原子操作
UPDATE farm_equipment
SET stock = stock - 1
WHERE id = ? AND stock >= 1
- 分布式事务处理:
- 创建订单同时需要冻结库存
- 采用TCC模式实现最终一致性
- 补偿任务处理失败场景
这个项目让我深刻体会到,农业领域的数字化系统需要特别关注业务场景的特殊性。比如农机设备的参数标准化、农村地区的网络条件适配、农户操作习惯的UI设计等,都是区别于常规电商系统的关键点。