
1. 先说结论这套系统能做什么适合谁基于SpringBoot和Vue的实验报告管理系统从名字就能看出来这是一个典型的前后端分离的全栈项目后端用SpringBoot处理业务逻辑和数据前端用Vue做用户界面核心场景是高校实验室里实验报告的线上化流转。我之前带过的几个毕设小组都选了或者参考了这类题目自己也完整拆过几套源码说实话这类系统不算难但它覆盖的知识点非常全从登录鉴权、文件上传、富文本编辑到权限控制、数据统计、前后端联调全走了一遍。对想拿一个“完整度”较高的项目来写毕设、或者想练手全栈的同学来说性价比很高。它的业务闭环很简单教师端创建实验、布置实验报告任务学生端在线填写报告、提交实验数据教师端批改打分、给出评语最后系统对成绩做汇总导出。整体就是一个带有文件交互和状态流转的教学管理工具。包里的“源码lw部署文档讲解”结构也很典型——lw就是论文文档配合部署文档和视频讲解基本就是给你搭好了一个可以直接运行、可以直接写论文、可以体系化讲清“为什么这么做”的完整项目。下面我从架构、数据库、前后端实现、部署再到问题排查逐个说透。2. 系统整体设计与需求拆解2.1 先理解业务角色谁会用它用它能做什么在拆代码之前先把系统里的角色理清。这个系统把用户分成三类管理员管人用户、院系、班级管基础配置实验课程、实验项目、公告还可以看全校数据。教师维护自己负责的实验课程创建实验任务、发布实验报告要求、批改学生提交的实验报告、录入成绩并导出。学生查看被分配的实验任务在线编写实验报告包括文字描述、实验数据、结果分析上传附件图表、代码包、数据文件提交后查看批改结果和成绩。这三个角色的权限差异决定了整个系统的设计走向。我最开始看这类源码时容易犯一个错误先去抠某个页面的样式结果业务关系都没理清。正确顺序应该先看数据库设计因为数据表结构基本就是业务关系的实体化把表看懂了功能模块就是水到渠成的事。2.2 为什么选SpringBootVue而不是别的组合其实做这类管理系统可选的方案很多ServletJSP、SSM、SpringBootThymeleaf甚至PythonDjango也能做。但SpringBootVue能成为现在毕设和商用小系统的绝对主流原因很实在SpringBoot把后端配置成本压到极低。内嵌Tomcat不需要单独打war包扔到Tomcat里依赖用spring-boot-starter-parent统一管理版本冲突少application.yml集中配置数据源、端口、文件存储路径。Vue在前端数据渲染上的开发效率高。表格、表单、弹窗这类管理后台最常见的东西VueElement UI基本是现成的数据双向绑定让表单校验和状态管理非常直观。前后端分离后分工更清晰。所有接口走RESTful风格JSON交互前端只负责视图展示和数据请求后端只用关注业务逻辑调试时用Postman单独测接口出问题能快速定位是前端还是后端的锅。部署方式灵活。后端打包成jar直接跑前端构建成静态资源可以单独部署到Nginx也可以直接扔进SpringBoot的static目录实现“一体化部署”对服务器资源紧张的场景非常友好。像我之前帮人看过一套代码后端就是SpringBoot 2.7.18 MyBatis-Plus前端是Vue 2 Element UI Axios这套组合的生态资料极其丰富GitHub上案例也多出了问题随便一搜就有答案。2.3 核心业务流程闭环系统的核心业务就一条线实验管理 → 报告布置 → 学生填写 → 提交审核 → 批改打分 → 成绩汇总。具体走一遍流程是这样的管理员维护好院系、班级、学生、教师的基础数据。教师创建实验课程再在课程下绑定班级并发布实验任务。实验任务里写清楚实验名称、实验要求、提交截止时间、报告模板说明。学生登录后看到自己待完成的实验点击“填写报告”在富文本编辑器里写内容可上传多个附件。学生在截止时间前可以反复修改报告每次保存是草稿状态点击提交后进入待批改状态提交后不能再改。教师端看到待批改列表批改打分、写评语可选择退回让学生重新修改。学生查看成绩和历史记录教师可以把某门课的成绩导出Excel管理员看全局统计。这里有几个常见的状态设计细节实验任务状态未开始/进行中/已截止、报告状态草稿/待批改/已通过/已退回、附件类型区分图片/压缩包/PDF。这些状态字段在每个源码里的实现大同小异但设计思路都差不多——用int或String存状态码配合前端tag标签展示不同颜色。3. 数据库设计与核心表结构3.1 数据表如何划分我翻过的几套实验报告系统源码表数量一般在10到18张之间。不管怎么命名核心的几张表一定是存在的表名作用关键字段user统一用户表username, password, role, avatarcollege学院表name, codeclazz班级表name, college_idcourse实验课程表name, teacher_id, clazz_id, termexperiment实验任务表course_id, name, requirement, deadline, statusreport实验报告表experiment_id, student_id, content, score, comment, status, submit_timeattachment附件表report_id, file_name, file_url, file_type这套表的设计逻辑是典型的“基础数据业务数据”分层前四张表解决“谁在哪上课”后两张表解决“课上要交什么东西、交得怎么样”。一对多关系靠外键字段关联比如一个experiment有多个reportreport的主键关联到student。3.2 几个值得注意的字段设计password字段存储源码里一般会用MD5或BCrypt加密存储不存明文。我之前见过有同学自己搭系统时直接把密码存明文这是个非常严重的坏习惯。即使只是毕设项目也要有基本的加密意识。content字段存富文本实验报告正文内容比较长还有格式标签所以MySQL里一般用longtext或mediumtext存HTML片段。前端用富文本编辑器提交HTML后端原样存入数据库读取时用v-html渲染。score字段做精度处理如果成绩需要支持小数比如实验报告按百分制多位老师评分取平均字段类型用decimal(5,1)或decimal(5,2)避免float的精度问题。时间字段统一创建时间、更新时间建议统一用datetime由后端统一生成Java 8的LocalDateTime避免数据库时区不一致的问题。3.3 一个常见的反例把所有东西塞到一张表里有些简化版源码会把实验、报告、附件全塞进一张表字段写一堆看起来省事但后期扩展非常痛苦。比如我要给报告增加“批改次数”字段一张表结构就得大改而拆分设计后只需要改动report表加一个字段或者直接加一张批改记录表不影响其他逻辑。而且拆表之后MyBatis-Plus的分页查询、条件构造器用起来也更顺手。如果你拿到的源码表设计不太合理我的建议是不要大改在现有基础上增加扩展表即可。比如想要“实验成绩统计报表”功能可以新增statistic表或者在report表加冗余字段。因为改主表结构意味着所有增删改查SQL、前端表单、后端VO全都要跟着动工作量会翻好几倍。4. 后端落地SpringBoot的业务实现细节4.1 项目结构怎么分层拿到源码后先看后端的包结构。标准的SpringBoot项目一般层次非常清晰用我们拆的这套系统来举例src/main/java/com/example/labreport/ ├── controller/ # 接收HTTP请求返回结果 ├── service/ # 业务逻辑层 │ ├── impl/ # 接口实现类 ├── mapper/ # MyBatis-Plus数据访问层 ├── entity/ # 数据库实体类 ├── vo/ # 前端展示对象组合多个表数据 ├── dto/ # 前端传给后端的参数对象 ├── config/ # 配置类拦截器、跨域、文件上传配置 ├── common/ # 统一返回结果、异常处理、工具类 └── LabReportApplication.java # 启动类重点看几个文件统一返回结果类Result好的项目所有接口都返回同样的结构比如{code: 200, message: success, data: {...}}。前端Axios请求拦截器里统一判断res.code如果code非200全局弹出报错信息。这套写法是现在的主流规范也符合团队协作习惯。JWT拦截器登录接口会返回一个token前端拿到后存到localStorage之后每次请求都带上Authorization头。后端配置一个拦截器对需要认证的路径校验token是否有效、是否过期。注意拦截器要放行登录接口和swagger文档路径不然联调时会被坑。全局异常处理器RestControllerAdvice所有业务异常、参数校验异常、数据库异常集中到这里统一处理返回前端友好的错误信息不把异常的堆栈直接抛给前端。4.2 实验报告的核心接口设计后端接口设计基本围绕资源实体来命名RESTful风格。核心接口有这几个方法路径说明POST/api/auth/login登录返回token和用户信息GET/api/course/mycourses教师查看自己名下课程POST/api/experiment教师创建实验任务POST/api/report/saveDraft学生保存草稿POST/api/report/submit学生提交报告GET/api/report/teacherList教师查看待批改列表POST/api/report/review教师批改打分POST/api/file/upload附件上传其中比较容易被忽略的是草稿保存和正式提交的区分。业务上草稿可以反复保存不限制次数提交后则不能再改所以report表里要有status字段区分状态后端接口上也要做判断如果report已处于待批改状态saveDraft接口应拒绝操作并返回提示。这种细节最能体现项目是否完整。发一个具体的接口实现示例report提交PostMapping(/submit) public Result submit(RequestBody ReportSubmitDTO dto, RequestAttribute(userId) Long userId) { Report report reportService.getById(dto.getReportId()); if (report null || !report.getStudentId().equals(userId)) { return Result.error(报告不存在或无权操作); } if (report.getStatus() ! ReportStatus.DRAFT) { return Result.error(当前状态不可提交); } // 校验是否已过截止时间 Experiment experiment experimentService.getById(report.getExperimentId()); if (experiment.getDeadline().isBefore(LocalDateTime.now())) { return Result.error(已过提交截止时间); } report.setStatus(ReportStatus.WAITING_REVIEW); report.setSubmitTime(LocalDateTime.now()); reportService.updateById(report); return Result.success(); }注意上面用到了RequestAttribute(userId)——这是在拦截器里解析token后把当前用户id塞进request的一个属性业务代码里直接就能拿到不用每个接口都重复解析JWT。4.3 文件上传与预览的实现重点实验报告系统绕不开文件上传。学生可能需要上传实验数据Excel、代码压缩包、或者实验现场照片。这部分有几件事必须处理配置文件上传大小限制SpringBoot默认单文件大小是1MB不够用。需要在application.yml里调大spring: servlet: multipart: max-file-size: 50MB max-request-size: 100MB存储路径不要写死在代码里用配置项存相对路径运行时获取系统绝对路径。比如配置file.upload-path: ./upload/然后通过Value(${file.upload-path})注入启动后在项目根目录生成upload文件夹。文件重命名上传的文件名统一改成UUID原始后缀避免中文名乱码、重名覆盖的问题。预览问题图片类的可以加载URL直接预览pdf文件由前端iframe或浏览器原生预览docx/xlsx类的就只能下载后打开。前端做好文件类型标识不同后缀用不同图标和操作按钮。4.4 后端最容易踩的坑我在联调这套系统时踩过几个印象比较深的坑分享出来跨域问题前端跑在8080端口后端跑在8081浏览器会发CORS预检请求。解决方式要么在后端配CorsFilter要么在前端Vite/DevServer里配代理。配代理更推荐因为最终部署时也走的是代理模式。LocalDateTime序列化格式问题Java 8的LocalDateTime默认序列化出来是数组前端根本没法用。要么在application.yml里配置jackson的date-format和time-zone要么在实体字段上统一加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。MyBatis-Plus分页要额外配置很多人导入MyBatis-Plus后直接调selectPage发现limit不生效因为没注册MybatisPlusInterceptor的分页插件。我见过不止一个学员卡在这个问题上。逻辑删除字段如果表里有deleted字段记住实体上加TableLogic不然MyBatis-Plus的自动填充不会生效删除操作会变成物理删除。5. 前端工程化Vue与接口联调5.1 路由设计和权限控制前端工程以Vue框架为例拆开看主要包含src/ ├── api/ # 接口请求模块 ├── assets/ # 静态资源 ├── components/ # 公共组件富文本、上传、分页 ├── router/ # 路由配置 ├── store/ # 全局状态管理 ├── views/ # 页面视图 │ ├── admin/ # 管理员页面 │ ├── teacher/ # 教师页面 │ └── student/ # 学生页面 └── utils/ # 工具hrequest封装、token处理路由这块建议用动态路由路由守卫。什么叫动态路由就是根据登录用户的角色动态生成可访问的路由表。管理员不能进学生页面教师看不到管理员菜单。实现方式是在路由配置里给每条路由加meta.roles字段然后在全局前置守卫里判断router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (!token to.path ! /login) { next(/login); } else if (token) { const userRole store.state.user.role; if (to.meta.roles !to.meta.roles.includes(userRole)) { next(/403); // 无权限页面 } else { next(); } } else { next(); } });权限控制不好做的话至少保证侧边栏菜单按角色渲染按钮级别控制在后端再做一层校验双保险。我不推荐在纯前端做所有权限控制因为接口直接从浏览器调用时可以绕过前端限制真正的数据安全一定要后端拦截器里做角色校验。5.2 Axios封装与token注入源码里的API模块通常已经封装好了一个request实例。重点看这几个点请求拦截器注入token从localStorage取token放到请求头。响应拦截器统一处理错误http状态200但业务code失败时调用Element UI的Message组件弹出错误提示http状态401时清空用户信息并跳转登录页。baseURL怎么配开发环境配合Vite代理时写/api因为代理会把/api转发到后端地址生产环境如果前后端分别部署就写后端实际域名地址。这里我最想强调一个点一定不要用打包工具里的全局变量硬编码接口地址。比如把后端的地址直接写成http://localhost:8081你开发时没问题最后部署到服务器上要改一堆地方。正确做法是新建.env.development和.env.production文件分别配置不同的VITE_API_BASE构建时自动切换环境变量。5.3 富文本编辑器和文件预览组件的选型实验报告内容不是简单的单行文本需要有标题、段落、表格、插入图片所以必须接富文本编辑器。Vue 2时代常见的方案是wangEditor或quillVue 3用Tiptap、Element Plus自带的编辑器也有直接用Markdown编辑器的。我拆的这套用的应该是一个主流的富文本组件这类组件的核心用法套路是通过v-model绑定HTML字符串上传图片的处理编辑器自带base64转图片上传接口后端返回图片URL再由编辑器插入到正文中提交保存数据时把HTML字符串提交给后端编辑时再回显到组件中富文本组件有个坑用户在编辑器里粘贴的内容可能带大量的内联样式会导致存储内容非常臃肿。解决方式可以在后端做一次HTML清洗只保留标签和白名单样式或者前端提交前用DOMPurify做消毒处理——这个方案顺带还能防XSS攻击属于加分项。文件预览按类型拆图片直接img :srcfileUrlPDFiframe :srcfileUrl或者用pdf.js库Excel/Word/压缩包不尝试预览提供下载按钮前端下载时有个常见的坑如果后端接口直接返回文件流前端需要判断响应头类型用Blob接收并创建临时下载链接。不要用window.open直接访问下载接口因为token在请求头里浏览器地址栏不会带上token会提示未授权。5.4 前端状态管理要不要用Vuex/Pinia像这种毕业设计级别的项目全局状态不需要存太多东西当前登录用户信息用户名、角色、头像、token。其他业务页面的数据尽量在页面内部管理和请求不塞到全局仓库里否则刷新页面数据丢失后要重新拉取徒增复杂度。源码里Vuex模块一般是user模块里面放了login、logout、getUserInfo几个action够用了。6. 构建部署与运行环境6.1 环境版本选择运行这套系统前先检查环境。我之前遇到过花了好几个小时排查问题最后发现是JDK版本不匹配。SpringBoot 2.7.x对应JDK 8或11SpringBoot 3.x至少JDK 17。如果源码是SpringBoot 2.x你电脑装的是JDK 21大概率会报错因为SpringBoot 2.x对高版本JDK支持不全。推荐的稳定环境组合JDK 1.8或JDK 11Maven 3.6.3Node.js 14.18Vue 2项目或Node.js 16.20Vue 3项目MySQL 5.7或MySQL 8.0IDEA 2020.3新版本IDEA对SpringBoot项目兼容性没问题6.2 搭建本机运行步骤拿到源码后按这个顺序启动最不容易出错导入数据库脚本在MySQL中新建一个数据库比如lab_report导入项目根目录下的sql文件夹里的脚本文件。注意MySQL 8.0的字符集要设置为utf8mb4不然插入emoji或特殊符号会报错。修改后端配置打开application.yml确认数据源URL、用户名、密码还有文件上传路径。启动后端在IDEA里直接运行启动类或者用Maven命令打包后运行mvn clean package -DskipTests java -jar target/lab-report-0.0.1.jar启动前端进入前端目录安装依赖并启动开发服务器npm install npm run serve访问系统浏览器打开http://localhost:8080前端端口后端接口默认http://localhost:8081。用系统管理员账号登录通常源码的README里会写初始账号密码比如admin/admin123。6.3 打包部署到服务器这是一种非常常见的部署形态后端单独部署前端也单独部署适合学生把自己的服务器作为线上展示。后端mvn clean package -DskipTests然后把target目录下的jar包传到服务器用nohup后台启动nohup java -jar lab-report-0.0.1.jar --server.port8080 app.log 21 前端npm run build构建产物在dist目录把这个目录上传到Nginx的HTML目录然后配置Nginx反向代理。Nginx配置的关键点在于静态文件直接访问接口proxy_pass转发到后端端口。server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files那行是解决前端history路由刷新404的关键。不写这行配置你在前端点路由能跳转但一刷新页面就404了因为Nginx找不到对应的静态文件路径需要全部交给index.html处理。6.4 部署文档里有哪些隐藏问题源码包里附送的部署文档说实话质量参差不齐。我看过一些写得好的会包含数据库初始化脚本、环境准备步骤、启动常见问题写得差的可能就是流水账式随便合了几段。如果部署文档讲的比较泛就用这个通用流程去跑先看数据库脚本能否成功导入再看后端能否启动日志里出现Started LabReportApplication in xxx seconds说明启动成功最后看前端能否通过代理访问到后端接口。按这个顺序排查能覆盖80%以上的问题。7. 把“源码lw部署文档讲解”用出价值的方法7.1 这类资源包的真正价值很多人买/下载这类项目源码第一反应是“改个名字和Logo交上去就完事”。这个想法我不推荐。首先每个学校的毕设要求、查重系统、答辩侧重点都不一样更重要的是导师答辩时随便问一个“为什么这么设计”“这个表为什么这么建”没有真正理解源码的人会当场卡壳。我的建议是把这套项目当“参考骨架”而不是“成品答案”把数据库表结构吃透理解业务关系把登录鉴权、文件上传、批改流程从头到尾捋一遍挑出两三个模块自己动手改一改加一个字段、加一个页面、加一个接口最后再写lw时用自己的语言把改动的部分讲清楚这样做有一个额外的好处论文的“系统实现”章节就会有真实内容可写而不是憋半天挤不出字来。7.2 你可以做哪些“低成本改造”这里给大家列几个改造方向既能体现独立思考工作量又不会太大增加评论/留言功能报告批改时除了分数加一个互评/讨论区。增加一张message表前端在原报告详情页加一个评论区即可技术点覆盖了外键关联和动态渲染。增加成绩分析图表教师端加一个“实验成绩分布”页面按分数段统计学生人数用ECharts画柱状图。后端写一个聚合查询接口前端有成熟的图表组件几天就能做完但论文亮点一下子就提升了。增加报告导出PDF后端用itext或flying-saucer库将富文本内容转PDF导出。这是一个非常硬核的功能做出来之后写在论文里非常有说服力。加入消息通知当老师批改完报告学生登录后能看到未读消息提醒。可以用简单的系统通知表实现不依赖WebSocket都能做出来。7.3 答辩前需要重点准备的知识点拿着这套源码去答辩导师大概率会问你这些问题JWT的认证流程是怎样的你要能说清楚用户登录→后端生成token→前端存储→每次请求带token→拦截器校验→解析用户信息。SpringBoot自动配置原理是什么至少要能说出SpringBootApplication包含EnableAutoConfiguration再配合starter的META-INF/spring.factories加载自动配置类。Vue父子组件怎么通信props向下传数据emit向上触发事件还有provide/inject、Vuex/store。数据库表为什么这么设计顺着外键关系说一遍就足够了不需要太深。系统安全性怎么保障密码加密、token过期、权限拦截、SQL注入防护MyBatis预编译、上传文件类型校验这些都能说把每个点对应到系统里的具体实现。8. 常见问题排查实录8.1 前端运行报Failed to load resource: net::ERR_CONNECTION_REFUSED这个错误基本是前端请求后端接口时连接被拒绝。先确认后端是否成功启动再看代理配置。如果用Vite检查vite.config.js里proxy目标地址写的是不是后端实际端口。用Vue CLI检查vue.config.js里的devServer.proxy配置。注意代理配置生效要重启前端服务光保存配置不会热更新。8.2 数据库连不上报Access denied for user账号密码问题拿到的源码里默认密码可能是root/123456但你自己MySQL的密码不一样。这是最最常见的问题。改application.yml里的username和password时注意如果MySQL是8.0版本驱动类要配com.mysql.cj.jdbc.DriverURL里要加useSSLfalseserverTimezoneAsia/Shanghai不然容易报时区或SSL异常。8.3 上传文件报MaxUploadSizeExceededException上传大小超限前面说了SpringBoot默认单文件上限1MB大文件就会触发这个异常。在application.yml里调大max-file-size之后重启后端。如果还不行检查是否有Nginx代理层Nginx默认client_max_body_size是1m也要在server层改成client_max_body_size 100m;。8.4 前端页面空白控制台报Cannot read properties of undefined (reading xxx)这个一般是接口返回的数据结构没有正常回填。先打开浏览器Network面板看接口是否返回数据。很多源码里接口路径和封装的RequestMapping值不一致或者前端期望的字段名和后端实体字段名不完全匹配比如后端是teacherName前端错写成了teacher_name排查时优先对这一层。8.5 IDEA启动Maven项目卡在Resolving dependenciesMaven下载依赖速度慢或失败。可以换成阿里云镜像源在settings.xml里配置mirror地址。另外IDEA右侧Maven面板里注意看是否有“找不到jdk 1.8”之类的提示Project Structure里要把SDK切换成正确的版本。8.6 汇总一张速查表症状大概率原因解决方法后端启动报Port 8081 was already in use端口被占用换端口或杀掉占用进程netstat -ano查PID前端接口401返回未登录token没传或已过期重新登录检查Axios请求头注入逻辑富文本内容里有JS弹窗存在XSS漏洞引入DOMPurify对内容做消毒学生提交后状态一直是草稿前端调错接口/后端状态判断有问题检查report状态机流转时间显示与国际标准时间差8小时MySQL连接时区配置URL加serverTimezoneGMT%2B8前端打包后刷新404history模式路由未配置Nginx加try_files配置兜底9. 一些补充的实操心得这段时间反复拆这套系统我的一个直观感受是实验报告管理系统这种业务看似常规但涉及到的工作量非常全面从数据库设计到权限控制再到文件处理每个环节都有可以深挖的技术点。如果只是用它来交差那很浪费如果愿意把每个模块的逻辑注入自己的理解它完全能支撑一篇完整且加分的中级全栈项目论文。有几个比较实用的小建议放在最后说不要把部署环境搞得太复杂。我见过有同学非要在Windows本机搞Docker部署这套项目折腾了半天发现容器内时区、文件路径问题一大堆。建议先在本机正常跑通理解每一步的作用再考虑Docker化部署。本机跑通的时间成本比在Docker里排错低十倍。备份你调试好的版本。有一次我给项目加“退回修改”功能改动覆盖了原来正常的提交流程想回滚发现没做版本管理。搞毕设项目也建议用Git管理每个功能做好可以commit一次出问题可以随时退回。这个习惯在你毕业上班后也是基本操作。善用接口文档工具。前后端分离项目建议用SwaggerKnife4j更好用界面比默认的Swagger好看后端把接口注释写好前端调试时可以直接在Swagger页面上试接口。之前有人在部署文档里说用Postman挨个测但SpringBoot项目配好Swagger之后不用额外装工具浏览器打开就能测试方便很多。最后关于“讲解”视频不要只看一遍就丢在一边。建议动手之前先完整看一遍记住整体流程自己跑通后再边操作边看核对自己每一步是否走对。很多问题其实在讲解视频里已经提到了提前知道能少踩很多坑。这套系统的核心链路并不复杂但“简单”不代表“没有价值”。把一个CRUD系统做到状态管理严谨、权限闭环完整、交互反馈友好本身就是非常扎实的工程能力体现。希望这篇文章能帮你把技术点串起来别忘了读源码时如果突然觉得哪一步想不通就停下来写写画画把数据流走一遍很多疑问马上就有了答案。