先说个真实的感受:每年到了毕业设计或者课程设计的高峰期,“基于SpringBoot的XXX管理系统”这一类题目,永远是出现频率最高的。原因很简单——SpringBoot足够轻量,开发效率高,生态成熟,拿来做一个装潢公司管理系统这种典型的企业级CRUD业务系统,再合适不过。而“天盛装潢公司管理系统”这个题目,表面上是一个常规的信息管理项目,但实际上它覆盖了客户管理、项目进度、材料清单、合同收款、角色权限这些复杂的业务维度,比普通的图书管理、学生管理系统要更有说头,也更容易在答辩时拿高分。
这篇文章,我就结合这套系统的源码、部署文档和配套讲解,完整拆解一下这种类型的项目到底应该怎么设计、怎么落地、怎么把代码跑起来,以及过程中最容易踩的坑是什么。适合正在做毕设/课设的学生、想快速搭建企业管理后台的开发者,还有那些买了源码却跑不起来的同学参考。
1. 项目设计与功能拆解:装潢公司的业务到底要管什么
1.1 装潢业务的核心痛点与系统定位
很多同学拿到这类项目的第一反应是:“不就是一个CRUD吗?客户表、订单表、增删改查呗。”这个理解没有错,但如果真按这个思路做下去,你会发现项目做完之后既讲不出亮点,也经不起答辩老师的追问。
装潢公司和普通的销售型公司有个本质区别:它的业务链条非常长。从客户首次咨询、量房设计、报价签合同,到材料采购、施工进场、节点验收,再到尾款结算和售后回访,中间涉及设计师、项目经理、工长、材料商、财务等多方角色。任何一个环节脱节,都会直接导致工期延误或者成本超支。
所以这套系统的定位不是简单的“信息记录工具”,而是一个装潢业务全流程管理平台。它的核心价值体现在三个方面:一是把客户从“线索”到“成交”再到“交付”的全生命周期管起来;二是让项目进度和材料消耗实时可见,减少沟通成本;三是通过数据报表让老板能看清公司的经营状况——哪些项目在赚钱、哪些项目拖了工期、哪个员工手上积压的客户最多。
理解了这层业务背景,你再看系统的功能模块,思路就完全通了。
1.2 功能模块划分:从客户到施工的全链路覆盖
根据这套系统的源码结构,它大体包含以下几大核心模块:
| 模块名称 | 核心功能 | 业务作用 |
|---|---|---|
| 客户管理 | 客户信息登记、跟进记录、状态流转(意向/已签约/已完工) | 销售漏斗的数字化管理 |
| 项目管理 | 项目立项、指派设计师/工长、施工进度节点更新 | 核心业务流转中枢 |
| 材料管理 | 材料清单录入、采购记录、库存余量 | 控制装修成本的关键 |
| 合同与收款 | 合同金额、付款节点、实收款记录 | 财务回款跟踪 |
| 员工管理 | 员工信息、岗位角色、负责项目关联 | 人员与权限匹配 |
| 统计报表 | 项目数量、营收总额、材料成本等图表 | 老板视角的经营分析 |
这里尤其要关注的是项目状态流转的设计。装潢项目的状态一般会经过:待量房 → 已量房 → 已报价 → 已签约 → 施工中 → 已完工 → 已验收,这样一个链条。如果系统里只是简单地存一个“项目状态”字段,那它只是一个静态标签;但如果把状态的变更时间、操作人、变更备注都记录成一张项目进度履历表,那这就是项目管理系统和普通信息管理系统的本质区别,也是答辩时一个很好的加分点。
1.3 角色权限设计:为什么管理员、设计师、工长看到的页面不一样
我在实际使用的过程中发现,这套系统的权限模型是按照业务角色来划分的,而不是简单地分“管理员”和“普通用户”两个层级。它主要设计了三种角色:
- 管理员:拥有全部模块的访问权限,可以管理员工账号、查看所有项目和财务数据;
- 设计师:主要操作客户信息、量房记录、设计方案、报价单,不能修改财务收款数据;
- 项目经理(工长):主要维护自己所负责项目的施工进度、材料领用、节点验收。
这种设计逻辑很清晰:权限不是按“能不能登录”来分,而是按“需要对哪些数据负责”来分。在SpringBoot的实现层面上,主要是通过拦截器 + 注解(如自定义@RequireRole)或Spring Security的角色判断来控制接口的访问权限。前端路由也做了对应的控制,用户没权限的菜单直接不渲染。
注意:如果你拿到手的源码是简化版,只有管理员一种角色,也完全可以,不影响主体功能。但是答辩时如果老师问“你的系统怎么保证数据安全”,你就需要说明你是怎么设计角色权限的,哪怕只是“前端隐藏 + 后端接口校验”两层思路,也比你完全没想过这个问题要好得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与源码结构解读:为什么是SpringBoot
2.1 技术栈选型逻辑:SpringBoot + MyBatis-Plus + MySQL的黄金组合
这套系统使用的主流技术栈是:SpringBoot + MyBatis-Plus + MySQL + Vue/Thymeleaf。这个组合在高校课设和中小型企业内部项目中几乎是标配,选它的理由非常实在:
- SpringBoot解决了Spring框架配置繁琐的问题,内置Tomcat,打jar包就能跑,省掉了部署WAR到外部容器的麻烦;
- MyBatis-Plus在MyBatis的基础上提供了
BaseMapper通用CRUD接口,单表操作基本不用写SQL,开发速度肉眼可见地提升; - MySQL作为开源关系型数据库,配合Navicat等图形化工具,建表、导数据都非常方便。
如果前端用的是Vue,那么系统就是一个典型的前后端分离架构:SpringBoot负责提供RESTful API,Vue负责页面渲染和交互。如果前端用的是Thymeleaf模板引擎,那么是服务端渲染方案,整个项目结构更传统,适合部署简单的场景。建议你拿到源码后先看一眼目录结构,确定自己是哪种方案,再决定下一步怎么处理。
2.2 源码目录结构:拿到一份源码先怎么看
很多同学下载了一份源码,解压之后面对几十个文件夹直接懵了。我这里提供一个通用的“源码速读路径”,适用于绝大多数SpringBoot项目:
- 先看根目录的
pom.xml,搞清楚项目用了哪些依赖,SpringBoot版本是多少,有没有引入MyBatis-Plus、Shiro/JWT、Druid连接池等关键组件; - 再看
src/main/resources/application.yml(或application.properties),确认数据库连接配置、端口号、文件上传路径等信息; - 然后看启动类,确认包扫描路径,找到Controller并顺着请求地址去了解接口是怎么组织的;
- 最后看数据库脚本(一般在根目录的
sql/或db/文件夹下),用Navicat执行导入。
这套系统的源码结构通常是这样的:
code复制src/main/java/com/tiansheng
├── controller/ // 控制层:接收请求、返回结果
├── service/ // 业务层:核心业务逻辑
├── mapper/ // 数据访问层:MyBatis-Plus的Mapper接口
├── entity/ // 实体类:对应数据库表
├── config/ // 配置类:拦截器、跨域、静态资源映射
└── utils/ // 工具类:JWT工具、结果封装、日期处理
src/main/resources
├── mapper/ // MyBatis的XML映射文件
├── static/ // 静态资源(CSS/JS/图片)
├── templates/ // Thymeleaf模板页面(如果是前后端一体)
└── application.yml
按照这个路径去读代码,基本上半天时间就能把项目脉络梳理清楚。最忌讳的做法是一头扎进某个Controller里逐行读,那样效率太低,而且容易迷失在细节里。
2.3 核心配置文件:application.yml里那些容易踩坑的配置
我把这套系统的application.yml简化出来,逐项看一下关键配置的含义:
yaml复制server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/tiansheng_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
druid:
initial-size: 5
min-idle: 5
max-active: 20
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
这里有几个容易踩坑的地方:
serverTimezone=Asia/Shanghai不配置的话,高版本MySQL驱动会报时区错误;useSSL=false不配置的话,连接时可能反复警告;map-underscore-to-camel-case必须为true,否则数据库字段project_name无法自动映射到实体类的projectName属性上;- Druid连接池的配置如果缺少了
druid-spring-boot-starter依赖,项目启动时虽然不报错,但配置会完全不生效。
提一句:如果你在搜索引擎上查“springboot版本太高”这类问题,通常指的就是高版本的SpringBoot对JDK版本、Tomcat版本和某些依赖的兼容性要求变严格了。常见处理方式是把SpringBoot版本降到2.7.x左右,同时把JDK版本固定为1.8或11,这套组合最成熟稳定,兼容性问题最少。
3. 数据库设计与核心模块落地实操
3.1 数据库表设计思路:字段怎么定,关系怎么连
装潢公司管理系统的数据库设计是整个项目的基石。表设计得好,后续代码写起来行云流水;表设计得乱,会在开发中途不断返工。这套系统的数据库脚本主要包括以下核心表:
- 客户表:存客户姓名、电话、地址、房屋面积、户型、预算区间、跟进状态、来源渠道;
- 员工表:存员工姓名、职位、电话、所属部门、账号密码;
- 项目表:存项目编号、客户ID、负责人ID、设计风格、合同金额、项目状态、开工日期、竣工日期;
- 材料表:存材料名称、规格、单价、数量、供应商、采购日期、所属项目ID;
- 合同收款表:存合同总金额、已收款金额、未收款金额、计划收款节点、每笔收款的时间和对应节点;
- 进度履历表:存项目ID、节点名称、节点状态、操作人、操作时间、备注。
表之间的关联关系很直观:客户一对多项目,项目一对多材料,项目一对多进度履历,员工一对多负责项目。在SpringBoot的实体类中,用@TableId标记主键,用@TableField映射字段名,外键关联在业务层通过ID查询维护,不在数据库层面做强约束,这也是MyBatis-Plus时代的常见做法——数据库保持简洁,关联逻辑交给service层。
3.2 项目状态流转:这是业务实现的核心难点
项目状态流转是这类管理系统的真正业务核心,直接决定了系统的使用价值。如果你只是给项目加一个“状态”字段,然后每次手动改,那代码确实很简单,但项目就很平庸。更合理的做法是加一条状态流转记录:
java复制// 项目状态变更时的业务逻辑(伪代码)
public void changeProjectStatus(ProjectStatusChangeDTO dto) {
// 1. 更新项目主表的状态字段
Project project = projectMapper.selectById(dto.getProjectId());
project.setStatus(dto.getTargetStatus());
projectMapper.updateById(project);
// 2. 在进度履历表中插入一条记录
ProjectTrace trace = new ProjectTrace();
trace.setProjectId(dto.getProjectId());
trace.setNodeName(ProjectStatusEnum.getDesc(dto.getTargetStatus()));
trace.setOperatorId(LoginUserUtil.getCurrentUserId());
trace.setRemark(dto.getRemark());
projectTraceMapper.insert(trace);
}
这里有个细节值得注意:状态值最好用Integer类型的枚举值(0待量房、1已量房、2已报价、3已签约、4施工中、5已完工、6已验收),而不是直接存中文状态名称。原因有两个:一是枚举值在代码里可以做逻辑判断(比如“只有状态为3的项目才能关联材料采购”),二是数据库存储数字更节省空间、查询更快。展示层需要一个ProjectStatusEnum枚举类去把数字翻译成中文,这个类是答辩时很容易被追问的点,建议提前准备清楚。
3.3 登录认证与权限校验:JWT还是Session
在这套系统的源码里,登录认证一般有两种实现方式:传统的Session方案和JWT方案。现在大多数新项目都会选择JWT,因为它天然适合前后端分离,后端不需要维护Session状态,扩展性更好。
JWT的典型流程是:
- 用户提交账号密码,后端校验通过后生成一个Token返回给前端;
- 前端把Token存到
localStorage或sessionStorage,之后每次请求在请求头里带上Authorization: Bearer <token>; - 后端写一个拦截器,在HandlerInterceptor里校验Token是否有效,并解析出当前用户的ID和角色;
- 对于需要特定角色的接口,再通过自定义注解或代码判断角色是否匹配。
java复制@Component
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
throw new BusinessException("未登录或登录已过期");
}
// 解析token,获取用户信息并放入ThreadLocal
LoginUser user = JwtUtil.parseToken(token.replace("Bearer ", ""));
UserContext.set(user);
return true;
}
}
注意:写拦截器的时候,一定要记得放行登录接口、放行静态资源(比如Vue打包后的页面、CSS/JS文件),否则会出现“前端能打开,接口全部401”的情况。这类问题在实际部署中非常常见,排查方法很简单:把拦截器的
excludePathPatterns配置打开看一眼,确认没有把/api/login和/static/**误拦截。
4. 从源码到可演示系统:本地部署全流程实录
4.1 环境准备:本地需要装什么,版本怎么搭配
我给学生远程调试过很多次这种项目,发现大部分跑不起来的案例,根源不在代码,而在于环境版本不匹配。这里给出一套经过验证的组合,你可以直接抄作业:
| 软件 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 1.8(8u202+) | 最稳,兼容绝大多数SpringBoot 2.x项目 |
| Maven | 3.6.x | 不要用3.9+,某些仓库源会出现依赖解析问题 |
| MySQL | 5.7或8.0 | 8.0更主流,注意驱动要换com.mysql.cj.jdbc.Driver |
| Navicat | 15/16 | 可视化建库导数据,也可以用DBeaver替代 |
| IDEA | 2021.x以上 | 社区版也够用,但专业版对SpringBoot的调试支持更好 |
| Node.js(Vue前端) | 14.x或16.x | 如果前端是Vue项目,需要Node和npm |
安装JDK和Maven之后,请务必在命令行执行java -version和mvn -v确认环境变量生效了。这一步看起来简单,但很多人一路“下一步”装完之后,IDEA里能跑,命令行里却报“找不到命令”,就是因为环境变量没配好。
4.2 部署步骤:从导入源码到页面跑通
下面以“后端SpringBoot + 前端Vue”为例,把完整流程走一遍。
第一步:导入数据库
打开Navicat,新建数据库tiansheng_db,字符集选utf8mb4,排序规则选utf8mb4_general_ci。然后右键数据库,选择“运行SQL文件”,找到源码目录下的tiansheng.sql执行。执行成功后,你应该能在表列表里看到10多张表,并且里面有部分测试数据。
第二步:导入并配置后端
IDEA里选择File -> Open,选中后端源码目录,等Maven自动下载依赖。下载完成后,打开application.yml,把username和password改成你本地MySQL的账号密码。然后运行启动类,等待控制台出现Started Application in x.xxx seconds字样,说明后端已经启动。这时候用浏览器访问http://localhost:8080/api/test之类的接口地址,如果返回JSON数据,说明后端没问题。
第三步:导入并运行前端(如果是Vue项目)
用IDEA或VS Code打开前端源码目录,在终端执行:
bash复制npm install
npm run dev
如果npm install速度慢,可以换成国内镜像源:
bash复制npm config set registry https://registry.npmmirror.com
前端启动后,控制台会输出一个地址,一般是http://localhost:8081或http://localhost:5173。用这个地址打开页面,输入管理员账号密码(源码的文档里一般会写,如果不写,直接查employee表或者sql脚本里插入的初始数据),就能看到系统首页了。
第四步:打通前后端接口
前端页面如果报“请求失败”或者“网络错误”,大概率是跨域问题。Vue工程的vue.config.js里一般已经配置了代理:
javascript复制proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
确保前端请求路径都带/api前缀,并且代理指向的后端端口与application.yml里的server.port一致,接口就能正常访问了。
4.3 答辩要用的“讲解”和“lw”到底该怎么准备
标题里说的“lw”,在毕业设计的语境下指的就是配套的论文/设计文档。这套资料一般包括:开题报告、任务书、毕业论文、答辩PPT等。很多同学有个误区:以为论文是从系统做完后才开始写。其实正确的节奏是一边开发一边写文档——需求分析阶段就把系统功能模块图画出来,数据库设计阶段就把ER图和数据字典整理好,编码阶段把核心接口的设计思路记录下来。这样到了论文排版阶段,你只是把已有的材料组织成论文格式,而不是从零开始憋字数。
论文的常规结构可以参考:绪论(背景+意义)→ 相关技术介绍 → 需求分析 → 系统设计 → 系统实现 → 系统测试 → 总结与展望。系统实现章节不要大段贴代码,而是写“这个模块解决了什么问题,核心逻辑是什么,效果如何”。比如写项目进度管理模块时,重点描述状态流转的设计思路,配合截图说明效果,比贴150行代码有价值得多。
4.4 打包与远程部署:让系统可以被任何人访问
如果评审老师要求你部署到服务器上展示,你需要把前后端分别打包,再部署到一台云服务器上。后端打包非常简单:
bash复制mvn clean package -DskipTests
执行完成后,target目录下会生成一个xxx.jar文件。服务器上只要有JDK 1.8环境,一行命令就能启动:
bash复制java -jar xxx.jar --spring.profiles.active=prod
前端打包执行npm run build,生成的是一个dist目录,里面是纯静态文件,可以使用Nginx托管,同时配置反向代理把/api请求转发到后端Java进程的8080端口。这种“前后端分离 + Nginx反代”的部署结构,是企业里最常见的标准形态,也是答辩时一个不小的加分项。
5. 常见问题与排查技巧实录
5.1 项目启动直接报红:数据库连不上
这是遇到最多的问题,报错信息通常是Cannot connect to MySQL server或Access denied for user 'root'@'localhost'。排查思路按顺序来:
- 确认MySQL服务启动了没有——Windows服务管理器里看
MySQL服务状态是否是“正在运行”; - 确认账号密码对不对——用Navicat试连一下,能连上就说明没问题;
- 确认URL里的数据库名和实际建好的数据库名一致——很多人SQL文件导入到了别的库,或者库名改了但URL没改;
- 确认URL里的
serverTimezone参数加了——高版本驱动没这个参数直接报时区错误。
5.2 端口被占用:8080起不来
报错Port 8080 was already in use,说明有别的程序占用了端口。解决方法有几种:
- 换端口:修改
application.yml里的server.port为8081; - 杀进程:Windows下执行
netstat -ano | findstr 8080查看占用进程PID,再执行taskkill /F /PID <PID>结束进程; - 或者运行
jps命令查看是否有残留的Java进程,很多时候是之前启动没关干净。
5.3 前端能打开但数据出不来:接口状态404/500
如果能打开登录页,但输入账号密码点登录没反应,先打开浏览器F12开发者工具看Console和Network面板:
- 如果Network里登录请求的状态是404,说明代理配置或Controller的请求路径对不上,对照源码里Controller的
@RequestMapping路径修改前端请求地址; - 如果状态是500,说明后端抛异常了,回到IDEA控制台看报错堆栈,最常见的是SQL语句写错或某张表不存在、字段名对不上;
- 如果状态是401/403,大概率是token没带上去或token失效,重新登录一次再试。
5.4 Maven依赖下载失败:一堆本地仓库找不到的jar包
Maven项目首次导入时,IDEA右下角会不断下载依赖。如果你看到红色报错,先检查IDEA的Maven配置——Settings -> Maven,确认Maven home path选到了你本地安装的Maven目录,User settings file指向了settings.xml(通常需要在里面配置阿里云镜像):
xml复制<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
换完镜像记得点一下“刷新”按钮,让IDEA重新解析依赖。这一步能解决90%的Maven依赖下载问题。
5.5 源码文件缺失或者损坏:如何应对
有些同学拿到的源码可能不完整,比如缺少了某个Mapper XML文件,或者asm包版本不对。遇到这种情况,可以先看报错信息卡在哪个类、哪个方法上,然后再到对应的源码文件里检查。如果某个依赖确实导致项目无法启动,可以在pom.xml里排除冲突的依赖版本,或者添加一个明确版本号的依赖覆盖。
关于SpringBoot的jar包反编译问题,如果你的源码是别人打好包后交付的(比如只有一个可运行的jar包),而你确实需要拿到源码来修改功能,可以用高级解压工具把jar包解开,再用反编译工具(如JD-GUI)把class文件转回Java源码。这个操作对学习框架内部原理和恢复丢失源码很有帮助,但要注意它只能恢复业务代码的大体逻辑,注释会丢失,泛型和一些编译期处理过的结构会有偏差,所以只能作为“阅读理解”用途,不建议作为唯一源码来源。
另外还要提醒一点:jar包里还包含了所有依赖库,直接反编译整个jar包会得到一堆第三方class文件,基本没法看。要做的是只反编译你自己项目包名下(比如com.tiansheng.controller、com.tiansheng.service)的class文件,这样出来的代码才是有价值的。
6. 让这个项目真正属于你:二次改造与答辩加分思路
6.1 从“跑起来”到“讲清楚”:理解每一层代码的作用
很多同学拿到带源码的项目,最大的问题不是跑不起来,而是跑起来之后被老师一问就露馅。你要能说清楚的核心点其实就那么几个:一次请求从浏览器到后端再到数据库,中间经历了什么;登录的Token是怎么生成的、怎么校验的;项目状态变更时数据库里哪几张表会发生变化;MyBatis-Plus的selectPage分页是怎么把总数和记录查出来的。
我的建议是:拿到源码后,按一条完整的业务链路去读代码。例如跟单流程:客户登录 → 提交量房申请 → 管理员查看 → 分配设计师 → 设计师更新量房记录 → 系统记录状态流转。把这条链路上涉及的Controller、Service、Mapper、数据库表全部走一遍,你对整个框架的理解就会从一个“会跑的系统”升级到“懂原理的系统”。
6.2 低成本二次开发:给系统加一个新功能
如果你时间充裕,可以在这个基础上做一点二次开发,既锻炼能力,又能在答辩时展示“这是我的独立思考”。这里推荐一个性价比最高的改造方向:给系统加一个通知提醒功能。
思路是这样的:在数据库加一张notice表,字段包括通知内容、接收人ID、关联项目ID、是否已读、创建时间。当某个项目的状态发生变更时,在service层的状态流转方法里自动插入一条通知记录,目标接收人是对应项目的负责人。前端在首页顶部加一个铃铛图标,进入页面时调一个/notice/unread-count接口显示未读数量,点击后展示通知列表,点击某条时调/notice/read接口标记已读。
这个功能改动量不大,但涉及了后端建表、接口开发、业务联动触发、前端页面展示完整链路,既好用又好讲。答辩时老师问你“有没有什么创新点”,你就可以拿这个功能当例子。类似的低成本改造还有:导入导出Excel报表、生成项目完工统计图表、增加操作日志记录等,都是不错的选择。
6.3 部署文档和讲解视频的使用技巧
这套项目附带的部署文档和讲解视频(也就是标题里的“部署文档+讲解”)是很多人容易忽略的宝藏资料。我的使用建议是:不要先看讲解再操作,而是先自己按部署文档跑一遍,卡住了再去看对应的讲解片段。因为你一旦先看了视频的完整演示,就会形成路径依赖,真正自己动手时反而不知道哪里是难点。反过来,自己踩过坑之后再听讲解,你会瞬间理解“原来是这个原因”,记忆也深刻得多。
讲到这一层,我对这类项目的整体评价是:它的技术难度并不高,真正的分水岭在于你对业务的理解深度和对自己代码的熟悉程度。如果把SpringBoot比作一把工具,它解决的是“能不能搭建一个Web应用”的问题;而你对装潢业务流程的理解、对权限和状态流转的设计,才决定了这个应用是否真正可用、可讲、可展示。
最后分享一个我自己调试这类项目时的习惯:先把系统里所有的初始账号和测试数据摸清楚,然后从管理员视角把每个菜单点一遍,记录每个页面能做什么操作、对应后端哪个接口、改动了数据库哪张表。这样完整走一遍之后,整个项目的所有细节都会在你的掌握之中——不仅答辩论不慌,真正上线使用的时候,出了问题你也能快速定位。这套“先通读,再细读,最后动手改”的流程,是我推荐给所有拿到新项目的同学的标准动作。
