
每年三月到五月都是Java毕业设计最热闹的时候。如果你正在找题目看到“SSMJava青年旅舍管理系统”这种组合大概率会纠结两件事一是这个题目到底好不好做二是SSM这个老牌技术栈现在还有没有学习的必要。我做了多年Java开发也带过不少毕设项目今天就把这类系统的完整拆解、编码思路、论文写法一次说清楚。这个系统本质上是一个围绕住宿业务的信息管理平台覆盖客房、订单、会员、入住退房等核心场景适合计算机、软件工程、信息管理这类专业的同学作为毕设选题也适合想系统复习SSM整合的初学者拿来练手。先给一个明确结论青年旅舍管理系统是毕设里少有的“业务不复杂、功能有亮点、答辩好讲”的题目。它不像电商那样需要处理支付和库存的高并发也不像OA系统那样堆一堆审批流而是把CRUD、关联查询、状态流转、多条件检索这些经典基本功全部覆盖了一遍正好卡在“能体现工作量、又不至于做不完”的黄金位置。1. 为什么选青年旅舍管理系统业务场景拆解与系统边界1.1 先搞清楚青年旅舍和普通酒店的区别很多人拿到题目就急着写代码结果做到一半发现不对。青年旅舍虽然本质上是住宿管理但它的业务特点决定了系统设计跟传统酒店管理系统有明显差异。青年旅舍的核心特征是多人间、床位制、按床位售卖而不是按房间售卖。一个六人间可以同时卖给六个不同的散客也可以被一个团队整包。这意味着订单的最小粒度是床位而不是房间。房价的计算方式也更多样可以按床位计价按整间包房计价会员还可以享受折扣凌晨入住加收半天房费连续入住多晚自动累计优惠。除此之外青年旅舍还经常附带一些延伸服务比如公共区域租赁、行李寄存、洗衣服务、活动报名。这些单次计费的服务如果全部手工记账月底对账能头疼死。所以系统在设计时要把这些场景纳入考虑至少要在需求分析阶段提到不然论文里“功能需求分析”那一章会显得很单薄。1.2 系统角色与功能边界划分毕设系统的用户角色不能太多三个正好管理员、前台、会员游客预订端可选做。别一上来就搞五个角色权限模型会让你的代码量翻倍而且答辩时很难解释清楚。管理员负责基础数据维护包括房型管理、床位管理、价格策略、员工账号管理、数据统计。前台负责日常操作包括散客入住登记、退房结算、预订确认、换房操作、押金管理。会员/游客可以浏览房态、在线提交预订、查看个人订单、取消预订。这个角色如果做完整版的Web端登录注册工作量可控如果只是让前台代为登记也没问题。我的建议是做成三个角色的完整版本。原因是毕设评分里很看重“系统的完整性”哪怕会员端功能简单一点只要有独立的登录、浏览、预订、取消流程系统闭环就成立了论文里画用例图也更好看。1.3 核心业务流程从预订到退房的完整状态流转住宿系统的核心是一条状态机订单从创建到关闭中间经历了多个状态。我在做系统时最先画的不是ER图而是这个状态流转图。一个典型的订单流程是这样客人通过Web端提交预订请求系统检查目标日期内指定床位是否空闲。订单生成状态为“待确认”。前台在后台确认后状态变为“已确认”。客人到店前台办理入住系统将该订单关联的房间床位锁定为“已入住”同时记录押金和证件号。退房时前台计算房费、杂费扣除押金后生成结算单床位恢复为“空闲/待清洁”。如果客人未在保留时间前到店前台可以执行“取消”释放床位。这个流程里最关键的字段是订单状态。我的建议是状态值用数字表示而不是字符串0待确认 1已确认 2已入住 3已退房 4已取消 5已完成这样数据库查询时开销小Java代码里用枚举或常量类管理可读性也够。1.4 为什么说这个题目好演示、好答辩答辩时间一般只有五到十分钟。你不可能把每个页面都点一遍必须挑两三条主流程走完。青年旅舍系统的天然优势在于主流程非常直观房态图一点空闲床位一目了然创建订单、确认、入住、退房整个链路短每一步都能在屏幕上看到数据变化。评委看到前后数据一致会直接认为你的系统逻辑是对的。2. SSM技术栈逐个过一遍框架选型逻辑与架构设计要点2.1 都2026年了毕设为什么还选SSM这是很多同学最纠结的问题。网上到处是Spring Boot老师给的题目却写着SSM是不是过时了我的看法很直接SSM不是过时而是变成了教学框架。Spring Boot本质上是Spring的封装核心的IOC容器、AOP、事务管理机制全都在Spring底层。你直接上手Spring Boot很多东西一两行配置就过去了理解是空的。SSM要求你手写Spring配置文件、SpringMVC配置手动把三个框架拼在一起这个过程虽然繁琐但会逼你把组件扫描、视图解析器、数据源、事务管理器、MyBatis的Mapper扫描这些概念全部落到代码上。毕设选SSM还有一个现实原因老项目多参考代码多踩坑记录多。你遇到任何问题搜索引擎上一搜基本都有现成解决方案。对于时间有限的毕业季这个优势很重要。2.2 三层架构与包结构的划分规范SSM虽然是三个框架融合但它的分层模型非常清楚。我把包结构按经典的MVC惯例划分一周后回头看这种规范化的好处是任何类都能快速定位。src/main/java ├── com.youthhostel.controller // Controller层接收请求、返回视图 ├── com.youthhostel.service // Service接口 ├── com.youthhostel.service.impl // Service实现业务逻辑的主要承载点 ├── com.youthhostel.dao // MyBatis的Mapper接口 ├── com.youthhostel.entity // 实体类与数据库表对应 ├── com.youthhostel.utils // 工具类比如分页、MD5加密、日期处理 ├── com.youthhostel.interceptor // 登录拦截器Controller只负责参数接收和视图跳转Service里面写业务判断DAO只做单表或者多表的SQL操作。这个原则一定不能乱。我见过很多毕设代码把业务逻辑全部写在Controller里一个方法几百行系统能跑可一旦需要加功能改动就是灾难。2.3 SSM核心注解在系统里到底怎么用网上热词里常看到“SSM常用注解”实际上SSM项目里真正高频使用的注解就十几个。我在这个系统里的实际用法如下Spring注解Component/Service/Controller/Repository标注不同类型的Bean我实现Service时习惯直接写ServiceMyBatis的Mapper接口用Repository标注。Autowired依赖注入。实测下来我最喜欢的还是构造器注入方式空指针概率比字段注入低。Transactional加在Service实现类的业务方法上。比如订单确认和床位状态更新要保证一致性就必须用事务包起来。Value读properties配置文件里的参数比如文件上传路径、分页大小、上传头像的存储路径。SpringMVC注解RequestMapping/GetMapping/PostMappingURL映射。我习惯给每个Controller设置一个类级别的RequestMapping比如RequestMapping(/admin/room)方法上再用具体路径区分动作。RequestParam接收参数可以设置requiredfalse和defaultValue处理多条件查询很实用。PathVariableREST风格路径参数比如/order/detail/{id}拿订单详情时用得上。ResponseBody返回JSON配合Ajax请求使用房态页面刷新和表单异步校验全靠它。MyBatis注解少量使用ParamMapper接口方法有多个参数时必须用Param(xxx)指定名称否则XML里的#{}取不到值。这是新手很容易踩的坑先提前说。单凭注解还不算完SSM整合的核心难点在配置文件。后面实操部分我会把配置文件的关键内容放出来这里先不展开。3. 数据库设计与核心表结构从订单流倒推表设计3.1 你不是在画表你是在画业务轨迹青年旅舍管理系统最少需要七张表管理员表、会员表、房型表、床位表、订单表、订单明细表、结算记录表。如果做了轮播图公告、操作日志再加两张。我设计数据库的出发点不是“有哪些实体”而是“一条订单从创建到退房经过了哪些数据节点”。举个例子订单表是核心它需要记录订单编号可以用时间戳随机数生成或者数据库自增后补前缀关联的会员ID或散客姓名、手机号房型ID、床位ID、入住日期、离店日期、实际入住时间、实际退房时间订单状态、订单金额、押金、支付方式备注、创建时间、更新时间床位表是整个系统里很特殊的一张表。它挂在房型下面每个房型有多少个床位就初始化多少条记录。床位需要状态字段来标识“空闲/已预订/已入住/停用/待清洁”。之所以不直接用房间的字段是因为多人间里同一个房间的不同床位状态完全不同。3.2 核心建表SQL参考以下是我在实际项目中验证过的核心表SQL你可以直接参考或者按需裁剪。这里不追求把所有字段都列出来重点是结构逻辑。-- 房型表 CREATE TABLE room_type ( id int(11) NOT NULL AUTO_INCREMENT, type_name varchar(50) NOT NULL COMMENT 房型名称如六人间、四人间、大床房, bed_count int(11) NOT NULL COMMENT 床位数量, price decimal(10,2) NOT NULL COMMENT 标准价, member_price decimal(10,2) DEFAULT NULL COMMENT 会员价, area varchar(50) DEFAULT NULL COMMENT 房间面积, description varchar(255) DEFAULT NULL, cover_img varchar(255) DEFAULT NULL COMMENT 房型图片, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 床位表 CREATE TABLE bed ( id int(11) NOT NULL AUTO_INCREMENT, room_type_id int(11) NOT NULL COMMENT 所属房型, room_no varchar(20) NOT NULL COMMENT 房间编号如301, bed_no varchar(20) NOT NULL COMMENT 床位编号如301-2, status tinyint(4) NOT NULL DEFAULT 0 COMMENT 0空闲 1已预订 2已入住 3停用, PRIMARY KEY (id), KEY idx_room_type (room_type_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 订单主表 CREATE TABLE orders ( id int(11) NOT NULL AUTO_INCREMENT, order_no varchar(30) NOT NULL COMMENT 订单号, member_id int(11) DEFAULT NULL COMMENT 会员ID散客为NULL, customer_name varchar(50) NOT NULL COMMENT 入住人姓名, customer_phone varchar(20) NOT NULL, id_card varchar(30) DEFAULT NULL COMMENT 证件号脱敏存储, room_type_id int(11) NOT NULL, bed_ids varchar(100) DEFAULT NULL COMMENT 床位的ID列表多床用逗号分隔, check_in_date date NOT NULL, check_out_date date NOT NULL, total_amount decimal(10,2) NOT NULL COMMENT 应收总额, deposit decimal(10,2) DEFAULT 0.00 COMMENT 押金, status tinyint(4) NOT NULL DEFAULT 0 COMMENT 0待确认 1已确认 2已入住 3已退房 4已取消, check_in_time datetime DEFAULT NULL, check_out_time datetime DEFAULT NULL, remark varchar(255) DEFAULT NULL, create_time datetime NOT NULL, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_member (member_id), KEY idx_date (check_in_date, check_out_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;建表时有几个细节值得注意。首先金额字段用DECIMAL(10,2)而不是FLOAT或DOUBLE这个点答辩时经常被问原因是浮点型存在精度丢失的问题货币计算必须用定点数。其次所有表的字符集统一用utf8mb4不然插入emoji或者特殊符号会报错。第三凡是用于查询条件的字段要建索引比如订单表的会员ID、日期范围。3.3 多了还是少了表数量怎么控制在答辩范围很多同学喜欢上来就搞二十张表结果关联查询写到怀疑人生。毕设不是商业项目表太多意味着联表SQL多、数据维护繁琐、答辩问题多。我的经验是控制在八到十二张之间既显得工作量饱满又不至于失控。可以在七张核心表之外加两张实用性强的小表比如操作日志表和公告表。操作日志表记录谁在什么时候做了什么操作做答辩演示时可以说明系统的可追溯性公告表可以对接前台页面的公告栏让系统界面显得更真实。4. 核心业务模块实现与实操记录编码层面的关键节点4.1 登录模块拦截器、MD5加密与Session管理登录几乎是所有管理系统的第一个模块但最简单的东西最容易写错。我的实现方式是首先用户密码在数据库里不存明文存MD5后的值。在Java里用MessageDigest就可以实现代码量不大。再加一层盐会增加密码复杂度对毕设来说MD5加固定盐足够应付。public class MD5Util { private static final String SALT youth_hostel_2026; public static String encrypt(String password) { String base SALT password; // 使用MD5加密后转十六进制字符串 return DigestUtils.md5DigestAsHex(base.getBytes(StandardCharsets.UTF_8)); } }登录校验逻辑放在Controller里调用Service查询用户名和密码。验证通过后把登录用户对象放到Session中后面所有后台操作都通过拦截器校验Session是否有效。拦截器是SSM里一个必考考点。我实现了一个LoginInterceptor在preHandle方法里判断public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Object loginUser session.getAttribute(loginUser); if (loginUser null) { // 未登录重定向到登录页 response.sendRedirect(request.getContextPath() /login); return false; } return true; }然后在SpringMVC配置里注册拦截器设置拦截路径为/**排除路径为/login、静态资源路径。这里有个细节静态资源如果不排除CSS、JS都会被拦下来页面样式直接崩掉排查起来非常隐蔽。4.2 房态展示的多条件查询与Ajax异步交互房态页面是这个系统的门面。我设计了一个roomStatus.html页面顶部是筛选条件房型、入住日期、离店日期、床位状态。底部是房型卡片每个卡片里列出所有床位用不同颜色区分状态。这个功能涉及两个技术点多条件动态SQL和Ajax异步数据加载。多条件查询我用的是MyBatis的动态SQL。Mapper XML里用if标签拼条件这样四个筛选条件可以自由组合而不是为每种组合写一个SQL。select idfindAvailableBeds resultTypejava.util.Map SELECT b.id, b.room_no, b.bed_no, t.type_name, t.price, t.member_price FROM bed b LEFT JOIN room_type t ON b.room_type_id t.id where if testtypeId ! null and typeId ! AND b.room_type_id #{typeId} /if if teststatus ! null and status ! AND b.status #{status} /if if teststartDate ! null and startDate ! AND b.id NOT IN ( SELECT bed_id FROM order_bed_detail WHERE #{startDate} BETWEEN check_in_date AND check_out_date ) /if /where ORDER BY b.room_no, b.bed_no /selectAjax这端我用jQuery的$.getJSON往Controller发请求Controller返回JSON数组前端通过模板拼接渲染卡片。注意ResponseBody返回的对象必须是普通的POJO或Map不能直接返回字符串拼HTML不然前端拿到的是字符串而非对象JSON解析会报错。4.3 订单创建与前台下单的事务一致性这是整个系统里最需要用心的地方。下单动作不是简单地往订单表插一条数据而是一连串操作检查床位状态、插入订单表、更新床位状态为“已预订”、可选地生成订单明细、更新会员的累计消费。这些操作里任何一步失败都绝不能留下半截数据。所以必须在Service方法上加Transactional让它们同生共死。Override Transactional(rollbackFor Exception.class) public void createOrder(OrderCreateDTO dto) { // 1. 校验床位库存悲观锁或者乐观锁二选一 bedService.lockBeds(dto.getBedIds()); // 2. 计算金额 BigDecimal total calculateAmount(dto); // 3. 插入订单主表 Order order buildOrder(dto, total); orderMapper.insert(order); // 4. 更新床位状态 bedMapper.updateStatusByBedIds(dto.getBedIds(), BedStatus.RESERVED); // 5. 记账流水方便对账 accountMapper.insertAccountRecord(order); }事务要生效必须满足三个条件使用Transactional注解、数据库表是InnoDB引擎、事务管理器配置正确。缺一个事务就是静默失效。我排查过的问题里因为搜索引擎把表建成了MyISAM导致事务失效的情况不是个例。金额计算这里有很多隐藏需求。比如连续入住三晚每晚单价是否一样会员价是否覆盖所有房型凌晨入住是否加收订单取消时手续费怎么算。我的做法是写一个独立的PriceCalculator类把所有计价规则收拢在一起方便测试和修改。4.4 入住退房状态变更与结算清单入住处理的核心是把“已确认”的订单变成“已入住”同时把对应床位状态从“已预订”改成“已入住”记录实际入住时间。退房处理则反过来计算最终消费、更新床位为空闲、记录结算单。这两步都涉及订单状态和床位状态的联动写代码时把状态流转和维护逻辑单独抽出来后面做报表统计会省很多事。结算单我建议单独建一张表不要只写在订单表的备注里。结算单记录房费、押金、赔偿、其他消费、实收金额、结算时间这样论文里的“数据统计”功能才有数据可查。4.5 前端页面后台模板与会员端页面分工SSM项目的前端一般用JSP或者Thymeleaf我的建议是后台管理用JSP搭配AdminLTE或类似模板会员端用纯HTML页面搭配Ajax访问接口。这个组合的好处是后台页面开发效率高会员端可以后续对接移动端。页面数量控制在十个左右登录页、后台首页、房型管理、床位管理、订单列表、订单详情、入住办理、退房结算、会员管理、统计报表。每个页面包含列表查询、新增、修改、删除或状态变更工作量足够撑起论文章节又不会让你在五月前熬穿夜。5. 论文撰写与毕设答辩准备源码之外的隐性工作量5.1 论文目录怎么搭才能匹配系统功能标题里写着“源码论文”说明论文是交付物的一部分甚至比源码更关键。我推荐的目录结构是绪论研究背景与意义、国内外研究现状、论文结构安排。相关技术介绍SSM框架、MVC模式、MyBatis、JSP、MySQL。每个技术写两页这里技术名词要让评委看出你真的用过。系统分析可行性分析、需求分析、用例图、业务流程分析。系统设计总体架构、功能模块设计、数据库设计ER图和表结构说明。系统实现按模块写每个模块给出核心代码片段和界面截图。系统测试功能测试用例表、结果分析。总结与展望。这套结构是最稳妥的标准结构不会出彩但绝对不出错。答辩评分重心通常不在目录创新上而在每个章节是否言之有物。特别是第5章“系统实现”一定要让代码和文字相互印证不能整页贴代码不解释也不能只顾着说功能不给证据。5.2 论文里图怎么画才能加分毕设论文最忌讳的是图上文字全是系统截图。图要分三类用例图画需求、ER图画数据库设计、流程图和时序图画关键业务逻辑。用例图可以用PlantUML或者StartUML快速生成会员、管理员、前台三个角色各画一组用例每组四到六个用例即可。ER图把表之间的关联关系画出来特别是订单表与会员表、床位表的关系这里不需要特别复杂但主外键关系必须画对。时序图就画核心流程比如“预订流程时序图”从用户点击提交开始到Controller、Service、Dao、数据库一步步调用这张图画好了评委一眼就能确认你的架构理解是过关的。5.3 答辩现场最常被追问的十个问题为什么用SSM而不是Spring Boot因为更贴近底层整合逻辑能体现对Spring核心机制的理解同时毕设需要覆盖课程所学知识体系。事务失效有哪些可能原因引擎不是InnoDB、没有配置事务管理器、方法被内部调用绕过代理、异常被吞掉没有抛出。MyBatis和Hibernate有什么区别MyBatis半自动、SQL可控、灵活Hibernate全自动、对象映射、开发效率高但复杂SQL难优化。登录密码怎么保证安全数据库不存明文MD5加盐存储传输过程可以再加密一层。多条件查询是怎么实现的MyBatis动态SQL用if标签拼条件。数据库索引建在哪些字段查询条件、排序字段、外键字段。遇到最大的技术难点是什么答案是“订单状态一致性问题”引用事务管理解决。系统有多少张表为什么这么设计把表清单和设计依据讲清楚。项目的可扩展性体现在哪预留了接口可以替换为Spring Boot可以手机端适配。测试用例是怎么设计的按等价类和边界值分析举例。这些问题并不难关键是你得真动手写过。只要项目是自己敲的答起来基本不会卡壳。6. 常见问题排查与避坑速查表6.1 环境和部署层面的高频报错问题现象根本原因解决方案Tomcat启动后页面404项目没有部署到webapps或者访问路径没有包含项目名确认IDEA中Artifact配置正确访问URL带上/项目名/数据库中文乱码连接URL缺少编码参数在jdbc.properties的URL后追加?useUnicodetruecharacterEncodingutf8JSP页面无法解析EL表达式web.xml版本过旧使用Servlet 3.1以上规范检查web.xml头部声明静态资源404拦截器拦截了静态资源在SpringMVC配置中放行静态资源路径或用mvc:resources映射端口被占用上次运行没有正常停止命令行执行netstat -ano查看PID后结束进程或修改端口号6.2 MyBatis Mapper映射的典型坑Mapper接口和XML文件绑定的前提是两者在同一个包路径下且XML里的namespace必须写接口全限定名。我遇到过最离谱的情况是XML文件放在了java目录而不是resources目录导致发布后文件根本没有被打进classpath运行时报“Invalid bound statement (not found)”。解决办法有两个一是把XML放在src/main/resources/mapper下二是在pom.xml里配置resources标签把src/main/java下的XML也打包进去。第二个方案我不推荐治标不治本。还有一个高频问题SQL里有、符号时在XML里必须写成lt;、gt;不然XML直接报错。比如查询离店日期大于某天的订单![CDATA[ ]]或者干脆用gt;转义。我在写日期范围查询时因为这个符号排查了很久后来统一改成使用where加if并且在比较条件的写法上刻意避开原生符号。6.3 分页插件的使用与手写分页的取舍SSM项目分页两个方案用PageHelper插件或者手写LIMIT offset, size。我的建议是毕设项目用手写方案。PageHelper确实方便一行代码搞定分页但它有一个容易踩的坑如果你没有在查询前设置PageHelper参数或者一个方法里执行了两条SQL分页参数就会被第二条SQL消费掉导致分页失效且数据错乱。手写分页无非就是在Mapper接口里加两个参数ListOrder findOrderPage(Param(offset) int offset, Param(pageSize) int pageSize, Param(status) Integer status);Service里算好offset并绑定到PageInfo对象代码多一点但逻辑完全可控。答辩时如果被问“分页是怎么实现的”手写方案能讲得清清楚楚PageHelper则只能答“调用插件”效果天差地别。6.4 排查问题的方法论不是玄学是流程碰到系统性Bug最忌讳的是乱改代码。我的排查步骤是固定的看控制台堆栈尤其看第一个Caused by这是根因。确认数据库里数据状态很多时候报错逻辑没问题是脏数据导致的。在Service层加日志把参数、中间结果、异常都打出来。从后往前定位从DAO层SQL开始排除再往上到Service最后到Controller。成功修复后做回归测试确保修复没有破坏其他功能。这套流程听着简单但相当多同学卡在第一步连异常堆栈都不看就跑到网上去搜搜出来的内容跟自己的报错根本不是一回事。7. 这个项目怎么扩展才能体现个人特色如果所有人都做一模一样的青年旅舍管理系统答辩撞车在所难免。想拿高分必须在标准功能之外体现一些个人思考。我总结了几个投入低、见效快的扩展方向。方向一可视化数据分析。用ECharts在后台首页展示近七天的入住率曲线、房型热度柱状图、来源渠道占比饼图。实现不难后端写一个统计SQL前端用ECharts的setOption渲染但论文里可以写一章“数据可视化展示”答辩时的画面冲击力很强。方向二操作日志增强可追溯性。用一个AOP切面拦截所有Controller方法自动记录访问日志、操作人、操作时间、请求参数。这个扩展能展示你对Spring AOP的理解属于加分项。方向三Excel导入导出。用POI实现会员信息的批量导入和订单报表导出。网上教程很多做出来之后论文的“实用功能”板块会更有分量。方向四短信或邮件通知模拟版。在订单确认和退房结算时打印一条日志模拟发送通知设计一个通知接口未来的实现类可以替换为真实短信通道。这个设计的妙处在于展示了接口隔离思想。扩展不是越多越好选一个做到完整比三个都做一半强得多。我见过太多同学在答辩前一周才想起要加功能结果既没有充分测试又在演示中翻车。回看整个项目青年旅舍管理系统最值得做的地方不在于技术多先进而在于它让你把SSM的每个环节都亲手打通从环境配置到数据库设计从用户登录到复杂查询从状态流转到数据统计。这些能力是你在毕业前最后一次低成本试错的机会。做这个项目的过程里我最大的体会是毕设系统并不需要惊天动地的功能代码逻辑清晰、结构规范、流程闭环、能清楚讲出自己的设计思路就足以交出一份合格的答卷。如果你正在起步阶段建议先把环境装好、数据库建出来哪怕只跑通登录模块再往下走后面都会顺利得多。这个基础的搭建过程中也会碰到很多细枝末节的问题别怕动手踩坑本身就是整个毕设最有收获的部分。