毕业设计实战:基于Spring Boot的乐享田园系统毕设全流程指南✨

44 阅读11分钟

毕业设计实战:基于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. 演示流程设计

按照真实业务流程演示:

  1. 农民登录→发布土地信息→管理种植记录
  2. 用户登录→浏览土地→租赁下单→查看种植进度
  3. 管理员登录→管理用户农民→监控订单数据

3. 答辩重点准备

  • 业务逻辑亮点:土地状态管理、防重复租赁、自动到期处理
  • 技术实现亮点:Redis分布式锁、定时任务、缓存优化
  • 系统特色:完整的土地租赁生态、种植过程可视化

结语

基于Spring Boot的乐享田园系统毕设成功的关键在于:抓住土地租赁核心业务,处理好状态同步,保证数据一致性。这套系统虽然业务逻辑相对复杂,但一旦掌握,无论是完成度还是创新性都能得到导师认可。

记住:租赁状态管理、防业务冲突、种植过程跟踪是三大得分点!需要完整源码、数据库脚本、部署文档的同学可以在评论区留言。

点赞收藏这篇,下次找流程不迷路~祝宝子们毕设顺利,轻松毕业!😘