毕业设计实战:基于Spring Boot的乐享田园系统毕设全流程指南✨
去年做乐享田园系统毕设时,我在土地租赁和订单状态的关联逻辑上卡了整整4天——一开始没处理"土地重复租赁"问题,同一块土地能被多个用户同时租赁,导师演示时直接发现了这个重大业务漏洞😫 经过反复修改优化,终于总结出了这套乐享田园系统毕设实战经验!
一、需求分析:抓住土地租赁核心业务
刚开始我想做一个"万能农业平台",包含了农产品销售、农业技术、农资商城等模块,结果导师说"重点不突出,核心土地租赁流程不清晰"。后来明白,乐享田园系统要围绕土地-租赁-种植这个核心业务链展开。
1. 核心用户角色与功能
系统主要面向三类用户,功能定位要精准:
-
管理员端(平台管理核心):
- 农民管理:审核农民资质、管理农民信息、信用评级
- 用户管理:管理租地用户、处理用户建议、维护用户信息
- 订单监管:监控租赁订单、处理异常情况、数据统计分析
- 系统维护:管理基础数据、维护系统稳定
-
农民端(土地供应核心):
- 土地管理:发布土地信息、设置租赁价格、管理土地状态
- 订单管理:处理租赁申请、查看订单详情、管理租约状态
- 种植管理:记录种植过程、上传生长照片视频、更新种植进度
- 评价管理:回复用户评价、维护土地口碑
-
用户端(租赁使用核心):
- 土地浏览:查看可租土地、了解土地详情、收藏心仪土地
- 租赁下单:选择租赁期限、签订电子合同、完成租赁支付
- 订单管理:查看租赁记录、续租土地、评价租赁体验
- 种植关注:查看种植进度、了解作物生长情况
2. 需求分析实战技巧
- 实地调研很重要:我走访了几个田园综合体,发现他们最需要"土地状态实时更新"功能,于是重点设计了土地状态管理
- 绘制业务流程图:用DrawIO画出"用户选地→租赁下单→农民确认→种植管理"的完整流程,答辩时清晰展示业务逻辑
- 明确业务规则:如"同一土地不能重复租赁""租赁到期前可续租""用户评价后农民可回复"等约束条件
3. 可行性分析要点
- 技术可行性:Spring Boot+MySQL技术成熟,开发效率高
- 经济可行性:全部使用开源工具,零开发成本
- 操作可行性:界面设计参考主流租赁平台,用户上手快
二、技术选型:现代化开发方案
曾经尝试用传统的SSM框架,结果XML配置太繁琐,最终选择Spring Boot,开发效率大大提升!
1. 技术栈选择理由
| 技术组件 | 选择理由 | 避坑提醒 |
|---|---|---|
| Spring Boot 2.7 | 自动配置,快速启动 | 不要用3.x,兼容性问题多 |
| MySQL 8.0 | 事务支持完善,适合订单系统 | 记得配置事务隔离级别 |
| MyBatis Plus | 简化CRUD操作,提高效率 | 注意代码生成器的配置 |
| Redis | 缓存热点数据,提升性能 | 合理设置缓存过期时间 |
| Bootstrap 3.x | 组件丰富,响应式布局 | 不要用5.x,学习成本高 |
2. 开发环境搭建
# application.yml 配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/farm_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: localhost
port: 6379
database: 0
三、数据库设计:土地租赁业务数据建模
最初设计时把土地信息和租赁订单放在同一张表,导致查询性能低下。重新设计后按业务模块分表,系统响应速度大幅提升。
1. 核心数据表设计
土地表(tudi)
CREATE TABLE `tudi` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`shangjia_id` int(11) NOT NULL COMMENT '农民ID',
`tudi_name` varchar(200) NOT NULL COMMENT '土地名称',
`tudi_photo` varchar(255) DEFAULT NULL COMMENT '土地照片',
`tudi_file` varchar(255) DEFAULT NULL COMMENT '合同文件',
`tudi_address` varchar(200) DEFAULT NULL COMMENT '所在地址',
`tudi_types` int(11) DEFAULT NULL COMMENT '土地类型',
`tudi_haohuai_types` int(11) DEFAULT '1' COMMENT '土地状态:1优/2良/3一般',
`tudi_old_money` decimal(10,2) DEFAULT '0.00' COMMENT '土地原价/月',
`tudi_new_money` decimal(10,2) DEFAULT '0.00' COMMENT '现价/月',
`tudi_clicknum` int(11) DEFAULT '0' COMMENT '点击次数',
`tudi_content` text COMMENT '土地介绍',
`tudi_zulin_types` int(11) DEFAULT '1' COMMENT '是否租赁:1可租/2已租',
`shangxia_types` int(11) DEFAULT '1' COMMENT '是否上架:1上架/2下架',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_shangjia` (`shangjia_id`),
KEY `idx_zulin` (`tudi_zulin_types`),
KEY `idx_shangxia` (`shangxia_types`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='土地表';
土地租赁订单表(tudi_order)
CREATE TABLE `tudi_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tudi_order_uuid_number` varchar(200) NOT NULL COMMENT '租赁单号',
`tudi_id` int(11) NOT NULL COMMENT '土地ID',
`yonghu_id` int(11) NOT NULL COMMENT '用户ID',
`insert_time` datetime NOT NULL COMMENT '租赁时间',
`daoqi_time` datetime NOT NULL COMMENT '到期时间',
`xuzu_times` int(11) DEFAULT '0' COMMENT '续租次数',
`pingjia_types` int(11) DEFAULT '2' COMMENT '是否评价:1是/2否',
`order_status` int(11) DEFAULT '1' COMMENT '订单状态:1生效中/2已到期/3已取消',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_number` (`tudi_order_uuid_number`),
KEY `fk_tudi` (`tudi_id`),
KEY `fk_yonghu` (`yonghu_id`),
KEY `idx_daoqi` (`daoqi_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='土地租赁订单表';
种植详情表(tudi_zhongzhi)
CREATE TABLE `tudi_zhongzhi` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tudi_order_id` int(11) NOT NULL COMMENT '土地订单ID',
`shangjia_id` int(11) NOT NULL COMMENT '农民ID',
`tudi_zhongzhi_name` varchar(200) NOT NULL COMMENT '标题',
`tudi_zhongzhi_photo` varchar(255) DEFAULT NULL COMMENT '照片',
`tudi_zhongzhi_video` varchar(255) DEFAULT NULL COMMENT '视频',
`tudi_zhongzhi_content` text COMMENT '详情',
`zhongzhi_stage` int(11) DEFAULT '1' COMMENT '种植阶段:1播种/2生长/3成熟',
`insert_time` datetime DEFAULT NULL COMMENT '添加时间',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `fk_order` (`tudi_order_id`),
KEY `fk_shangjia` (`shangjia_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='种植详情表';
2. 表关系设计要点
- 状态字段设计:土地状态(可租/已租)、订单状态(生效中/已到期/已取消)、种植阶段(播种/生长/成熟)
- 唯一约束:租赁单号唯一,防止重复
- 级联更新:租赁订单创建时自动更新土地状态
四、核心功能实现与代码详解
1. 土地租赁模块(核心业务功能)
Service层实现:
@Service
public class TudiOrderServiceImpl implements TudiOrderService {
@Autowired
private TudiOrderMapper tudiOrderMapper;
@Autowired
private TudiMapper tudiMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
@Transactional
public String createOrder(TudiOrderEntity order) {
// 1. 检查土地状态(使用Redis缓存防止重复提交)
String lockKey = "tudi_lock:" + order.getTudiId();
Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", Duration.ofSeconds(10));
if (!lockAcquired) {
throw new RuntimeException("操作过于频繁,请稍后重试");
}
try {
TudiEntity tudi = tudiMapper.selectById(order.getTudiId());
if (tudi.getTudiZulinTypes() != 1) {
throw new RuntimeException("该土地已被租赁,无法重复租赁");
}
if (tudi.getShangxiaTypes() != 1) {
throw new RuntimeException("该土地已下架,无法租赁");
}
// 2. 生成唯一订单号
String orderNo = "TD" + System.currentTimeMillis() + RandomUtil.randomNumbers(4);
order.setTudiOrderUuidNumber(orderNo);
// 3. 设置订单状态
order.setOrderStatus(1); // 生效中
order.setPingjiaTypes(2); // 未评价
order.setInsertTime(new Date());
// 计算到期时间(默认租赁1年)
Calendar calendar = Calendar.getInstance();
calendar.setTime(order.getInsertTime());
calendar.add(Calendar.YEAR, 1);
order.setDaoqiTime(calendar.getTime());
tudiOrderMapper.insert(order);
// 4. 更新土地状态
tudi.setTudiZulinTypes(2); // 已租赁
tudiMapper.updateById(tudi);
return orderNo;
} finally {
// 释放锁
redisTemplate.delete(lockKey);
}
}
@Override
@Transactional
public void xuzuOrder(Integer orderId, Integer xuzuMonths) {
TudiOrderEntity order = tudiOrderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (order.getOrderStatus() != 1) {
throw new RuntimeException("只有生效中的订单才能续租");
}
// 续租逻辑
Calendar calendar = Calendar.getInstance();
calendar.setTime(order.getDaoqiTime());
calendar.add(Calendar.MONTH, xuzuMonths);
order.setDaoqiTime(calendar.getTime());
order.setXuzuTimes(order.getXuzuTimes() + 1);
tudiOrderMapper.updateById(order);
}
@Override
@Scheduled(cron = "0 0 0 * * ?") // 每天凌晨执行
public void checkOrderExpire() {
// 检查到期订单
List<TudiOrderEntity> expireOrders = tudiOrderMapper.selectList(
new LambdaQueryWrapper<TudiOrderEntity>()
.eq(TudiOrderEntity::getOrderStatus, 1) // 生效中
.lt(TudiOrderEntity::getDaoqiTime, new Date()) // 已到期
);
for (TudiOrderEntity order : expireOrders) {
// 更新订单状态
order.setOrderStatus(2); // 已到期
tudiOrderMapper.updateById(order);
// 更新土地状态
TudiEntity tudi = tudiMapper.selectById(order.getTudiId());
tudi.setTudiZulinTypes(1); // 恢复为可租
tudiMapper.updateById(tudi);
}
}
}
Controller层:
@RestController
@RequestMapping("/api/order")
public class TudiOrderController {
@Autowired
private TudiOrderService tudiOrderService;
@PostMapping("/create")
public Result createOrder(@RequestBody TudiOrderEntity order, HttpServletRequest request) {
try {
// 获取当前登录用户
YonghuEntity user = (YonghuEntity) request.getSession().getAttribute("user");
order.setYonghuId(user.getId());
String orderNo = tudiOrderService.createOrder(order);
return Result.ok("租赁成功").put("orderNo", orderNo);
} catch (Exception e) {
return Result.error("租赁失败:" + e.getMessage());
}
}
@PostMapping("/xuzu")
public Result xuzuOrder(Integer orderId, Integer months) {
try {
tudiOrderService.xuzuOrder(orderId, months);
return Result.ok("续租成功");
} catch (Exception e) {
return Result.error("续租失败:" + e.getMessage());
}
}
}
2. 种植管理模块(农民端核心功能)
Service层实现:
@Service
public class ZhongzhiServiceImpl implements ZhongzhiService {
@Autowired
private TudiZhongzhiMapper zhongzhiMapper;
@Autowired
private TudiOrderMapper orderMapper;
@Override
@Transactional
public void addZhongzhi(TudiZhongzhiEntity zhongzhi) {
// 验证农民是否有权限管理该订单
TudiOrderEntity order = orderMapper.selectById(zhongzhi.getTudiOrderId());
if (order == null || !order.getShangjiaId().equals(zhongzhi.getShangjiaId())) {
throw new RuntimeException("无权管理该订单的种植记录");
}
zhongzhi.setInsertTime(new Date());
zhongzhiMapper.insert(zhongzhi);
}
@Override
public PageUtils queryZhongzhiPage(Map<String, Object> params, Integer shangjiaId) {
Page<TudiZhongzhiEntity> page = new Page<>(
Integer.parseInt(params.get("page").toString()),
Integer.parseInt(params.get("limit").toString())
);
LambdaQueryWrapper<TudiZhongzhiEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TudiZhongzhiEntity::getShangjiaId, shangjiaId);
// 按订单筛选
if (params.get("orderId") != null) {
wrapper.eq(TudiZhongzhiEntity::getTudiOrderId, params.get("orderId"));
}
// 按种植阶段筛选
if (params.get("stage") != null) {
wrapper.eq(TudiZhongzhiEntity::getZhongzhiStage, params.get("stage"));
}
wrapper.orderByDesc(TudiZhongzhiEntity::getInsertTime);
IPage<TudiZhongzhiEntity> result = zhongzhiMapper.selectPage(page, wrapper);
return new PageUtils(result);
}
}
3. 土地搜索与推荐模块
Service层实现:
@Service
public class TudiSearchServiceImpl implements TudiSearchService {
@Autowired
private TudiMapper tudiMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public PageUtils searchTudi(Map<String, Object> params) {
Page<TudiEntity> page = new Page<>(
Integer.parseInt(params.get("page").toString()),
Integer.parseInt(params.get("limit").toString())
);
LambdaQueryWrapper<TudiEntity> wrapper = new LambdaQueryWrapper<>();
// 基础条件:上架且可租
wrapper.eq(TudiEntity::getShangxiaTypes, 1)
.eq(TudiEntity::getTudiZulinTypes, 1);
// 关键词搜索
if (StringUtils.isNotBlank((String) params.get("keyword"))) {
String keyword = (String) params.get("keyword");
wrapper.and(w -> w.like(TudiEntity::getTudiName, keyword)
.or()
.like(TudiEntity::getTudiAddress, keyword)
.or()
.like(TudiEntity::getTudiContent, keyword));
}
// 土地类型筛选
if (params.get("tudiTypes") != null) {
wrapper.eq(TudiEntity::getTudiTypes, params.get("tudiTypes"));
}
// 价格区间筛选
if (params.get("minPrice") != null) {
wrapper.ge(TudiEntity::getTudiNewMoney, new BigDecimal(params.get("minPrice").toString()));
}
if (params.get("maxPrice") != null) {
wrapper.le(TudiEntity::getTudiNewMoney, new BigDecimal(params.get("maxPrice").toString()));
}
// 排序
String sort = (String) params.get("sort");
if ("price_asc".equals(sort)) {
wrapper.orderByAsc(TudiEntity::getTudiNewMoney);
} else if ("price_desc".equals(sort)) {
wrapper.orderByDesc(TudiEntity::getTudiNewMoney);
} else {
wrapper.orderByDesc(TudiEntity::getTudiClicknum); // 默认按热度
}
IPage<TudiEntity> result = tudiMapper.selectPage(page, wrapper);
return new PageUtils(result);
}
@Override
public List<TudiEntity> getRecommendTudi(Integer limit) {
String cacheKey = "recommend_tudi:" + limit;
// 尝试从缓存获取
List<TudiEntity> cacheResult = (List<TudiEntity>) redisTemplate.opsForValue().get(cacheKey);
if (cacheResult != null) {
return cacheResult;
}
// 缓存不存在,查询数据库
List<TudiEntity> result = tudiMapper.selectList(
new LambdaQueryWrapper<TudiEntity>()
.eq(TudiEntity::getShangxiaTypes, 1)
.eq(TudiEntity::getTudiZulinTypes, 1)
.orderByDesc(TudiEntity::getTudiClicknum)
.last("LIMIT " + limit)
);
// 存入缓存,过期时间1小时
redisTemplate.opsForValue().set(cacheKey, result, Duration.ofHours(1));
return result;
}
}
五、系统测试:确保租赁业务流程可靠
曾经以为功能实现就完成了,结果测试时发现土地被租赁后状态没更新,紧急加了状态同步逻辑。
1. 功能测试用例
土地租赁测试:
| 测试场景 | 操作步骤 | 预期结果 |
|---|---|---|
| 土地已被租赁 | 用户租赁已租土地 | 提示"该土地已被租赁" |
| 土地已下架 | 用户租赁下架土地 | 提示"该土地已下架" |
| 正常租赁 | 用户租赁可租土地 | 提示"租赁成功",土地状态更新 |
| 重复提交 | 用户快速重复提交 | 提示"操作频繁",防止重复下单 |
订单管理测试:
| 测试场景 | 操作步骤 | 预期结果 |
|---|---|---|
| 订单续租 | 用户续租生效中订单 | 到期时间延长,续租次数+1 |
| 到期检查 | 系统检查到期订单 | 自动更新订单状态,恢复土地可租状态 |
2. 业务规则测试
- 状态一致性:租赁订单创建/到期时同步更新土地状态
- 权限控制:农民只能管理自己土地的种植记录
- 防重复提交:使用Redis锁防止重复租赁
- 缓存策略:热点数据合理缓存,提升性能
六、部署与演示准备
1. 数据库初始化脚本
-- 插入测试数据
INSERT INTO `shangjia` VALUES
(1, 'farmer1', '123456', '张农民', '13800138000', 'farmer1@qq.com', '/images/farmer1.jpg', 1, 0.00, '经验丰富的农民,拥有多块优质土地', 0, NOW());
INSERT INTO `tudi` VALUES
(1, 1, '阳光田园001号', '/images/tudi1.jpg', '/contracts/tudi1.pdf', '北京市海淀区xx路', 1, 1, 500.00, 400.00, 0, '阳光充足,土壤肥沃的优质土地', 1, 1, NOW());
INSERT INTO `yonghu` VALUES
(1, 'user1', '123456', '李用户', '13800138001', '110101199001011234', '/images/user1.jpg', 1, 'user1@qq.com', 1000.00, NOW());
2. 演示流程设计
按照真实业务流程演示:
- 农民登录→发布土地信息→管理种植记录
- 用户登录→浏览土地→租赁下单→查看种植进度
- 管理员登录→管理用户农民→监控订单数据
3. 答辩重点准备
- 业务逻辑亮点:土地状态管理、防重复租赁、自动到期处理
- 技术实现亮点:Redis分布式锁、定时任务、缓存优化
- 系统特色:完整的土地租赁生态、种植过程可视化
结语
基于Spring Boot的乐享田园系统毕设成功的关键在于:抓住土地租赁核心业务,处理好状态同步,保证数据一致性。这套系统虽然业务逻辑相对复杂,但一旦掌握,无论是完成度还是创新性都能得到导师认可。
记住:租赁状态管理、防业务冲突、种植过程跟踪是三大得分点!需要完整源码、数据库脚本、部署文档的同学可以在评论区留言。
点赞收藏这篇,下次找流程不迷路~祝宝子们毕设顺利,轻松毕业!😘