1. 项目定位与架构选型:为什么是SSM + 微信小程序
每年到毕业设计季,我都能收到大量类似的问题:实习管理类系统到底怎么选型?能不能用Spring Boot?前端用Vue还是小程序?说实话,技术选型没有绝对的对错,只有合不合适的区别。这个项目选择SSM框架 + 微信小程序的组合,并不是随大流,而是出于几个非常实际的考量。
先说SSM框架,也就是Spring + SpringMVC + MyBatis。这个组合在当下的企业级开发里确实不算最前沿,Spring Boot已经成为新项目的主流选择,但对于学生项目、课程设计以及那些需要快速搭建、稳定运行的业务系统来说,SSM依然是一个非常扎实的底子。为什么?因为SSM的边界非常清晰:Spring负责对象管理和事务控制,SpringMVC负责HTTP请求的路由和参数绑定,MyBatis负责数据库操作。这种各司其职的结构,对于理解Java Web开发的核心流程非常有帮助,而且绝大多数高校的课程内容仍然围绕SSM展开,遇到问题查资料、问老师都方便得多。
再说微信小程序端。这里要展开讲一下,因为很多人在选型时纠结的是:为什么不做App?为什么不做H5?我的看法是,实习记录这个业务场景,核心使用场景是学生在外实习期间快速记录工作内容,比如在地铁上、在公司工位上、在回学校的路上,掏出手机随手记录一段文字拍一张照片。小程序天然契合这个场景:无需安装、微信内即点即用、不占用手机存储空间,最重要的是学生群体对微信的依赖度极高,从微信聊天切到小程序记录实习内容,路径极短。
从通信机制上看,小程序端和SSM后端的交互模式是典型的前后端分离。小程序通过wx.request发起HTTP请求,后端以RESTful风格提供JSON接口,两者通过HTTPS协议通信。这种架构的一个明显好处是,将来业务扩张到需要App端或PC管理后台时,后端接口可以几乎不改动地复用,只需要新增前端入口就行。
这个项目的完整技术栈如下表所示:
| 层次 | 选型 | 说明 |
|---|---|---|
| 客户端 | 微信小程序原生开发 | 不需要额外引入uni-app等跨端框架,原生开发稳定性最高,调试最直接 |
| 服务端 | Spring + SpringMVC + MyBatis | 经典的SSM组合,Maven管理依赖,打War包部署到Tomcat |
| 数据库 | MySQL 5.7+ | InnoDB引擎,utf8mb4字符集,保证中文和表情符号存储无压力 |
| 工具 | IDEA + 微信开发者工具 + Navicat | 后端开发、小程序调试、数据库可视化操作各司其职 |
这个选型方案还有一个隐藏的好处:学习资料极其丰富。不管是SSM整合过程、微信小程序登录流程,还是MySQL表设计,网上随便一搜都有大量的博客、视频和开源项目可以参考。对于时间紧、任务重的毕业设计来说,这一点非常关键。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心需求拆解与数据库设计:先把业务想清楚再动手
很多人拿到这类题目就直接开始写代码,结果写到一半发现业务逻辑捋不清、表结构设计不合理,只能推倒重来。我个人的习惯是,先花半天到一天时间把需求彻底拆透,把每一张表、每一个状态都设计好,再开始动键盘。这个项目虽然叫"实习记录小程序系统",但它的业务逻辑远比表面上看起来的要复杂。
2.1 用户角色与权限边界
这个系统里有三类核心角色,每类角色对系统的需求和权限边界完全不同:
- 学生:注册登录后,可以维护个人基本信息,填写实习单位信息,提交每日/每周的实习记录,查看指导教师的审核意见,修改被驳回的记录后重新提交。
- 指导教师:查看自己所带学生的名单,审核学生提交的实习记录,给出"通过"或"驳回"的意见,对实习情况进行简单的统计和跟踪。
- 系统管理员:管理全部用户账号,维护专业、班级等基础数据,查看整体实习进展数据。
需要注意的是,学生和指导教师的身份可以统一存放在一张用户表中,通过role字段区分,但是学生的学号、班级等扩展信息,以及教师的工号、职称等扩展信息,则需要单独的表来存储。这样可以避免一张表里堆太多字段,也方便将来扩展其他角色。
2.2 数据库表结构设计
这个项目的核心表我建议至少设计以下几张:
用户表(sys_user)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键,自增 |
| username | varchar(50) | 登录账号(学号/工号) |
| password | varchar(100) | 密码,BCrypt加密存储 |
| role | int | 1-学生,2-教师,3-管理员 |
| create_time | datetime | 创建时间 |
学生信息表(tb_student)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| user_id | bigint | 关联sys_user表 |
| student_no | varchar(30) | 学号 |
| name | varchar(30) | 姓名 |
| major | varchar(50) | 专业 |
| class_name | varchar(50) | 班级 |
| teacher_id | bigint | 指导教师ID,关联教师表 |
| phone | varchar(20) | 联系电话 |
教师信息表(tb_teacher)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| user_id | bigint | 关联sys_user表 |
| teacher_no | varchar(30) | 工号 |
| name | varchar(30) | 姓名 |
| title | varchar(30) | 职称 |
实习单位表(tb_company)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| student_id | bigint | 关联学生表 |
| company_name | varchar(100) | 单位名称 |
| company_address | varchar(200) | 单位地址 |
| position | varchar(50) | 实习岗位 |
| start_date | date | 实习开始日期 |
| end_date | date | 实习结束日期 |
实习记录表(tb_practice_record)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| student_id | bigint | 关联学生表 |
| record_date | date | 记录日期 |
| work_content | text | 工作内容 |
| work_summary | text | 心得总结 |
| image_url | varchar(200) | 图片附件URL |
| status | int | 0-草稿,1-已提交,2-已通过,3-已驳回 |
| teacher_comment | varchar(500) | 教师评语 |
| audit_time | datetime | 审核时间 |
| create_time | datetime | 创建时间 |
| update_time | datetime | 更新时间 |
这里有几个设计上的细节值得说一下:
第一,status状态字段是整个实习记录模块的灵魂。我见过不少项目用字符串存状态,枚举值写得到处都是,维护起来非常痛苦。用int类型配合常量类或枚举类,前端传数字、后端存数字、展示时再映射中文文案,这是最稳妥的方式。更重要的是,状态流转必须清晰:草稿可以编辑,已提交不能改,只能等审核,驳回后可以修改并重新提交,已通过则锁定。这实际上就是一个简化的状态机,业务逻辑围绕状态做分支判断,代码结构会非常清晰。
第二,为什么要把学生和教师拆成单独的表,而不是统一塞进用户表?原因很简单:用户表管的是"登录账号"这件事,学生表和教师表管的是"业务资料"这件事。随着系统迭代,学生可能需要填更多信息(比如紧急联系人)、教师可能需要填更多信息(比如研究方向),如果都在一张表里,字段会越来越多,关联查询也会越来越复杂。分开之后,核心用户表保持精简,扩展表各自发展,这是一种非常务实的数据库设计思维。
第三,实习记录和图片附件的存储方式。图片不建议直接存到数据库里的blob字段,而是上传到服务器指定目录或云存储后,把URL存到image_url字段里。这样数据库的压力小,图片的访问也灵活。
2.3 关键业务逻辑
这个系统最核心的业务链路是:学生填写实习记录 -> 提交给指导教师 -> 教师审核 -> 反馈结果。围绕这条链路,后端的接口设计要覆盖以下几个方面:
- 学生端:新增记录(草稿)、修改记录、提交记录、查看自己的记录列表、按状态筛选
- 教师端:查看名下学生的记录列表、审核记录(通过/驳回)、填写评语
- 公共端:登录、获取用户信息、修改密码
权限控制上也需要注意:学生只能操作自己的数据,教师只能审核自己名下的学生数据。如果接口没有做权限校验,让一个学生通过修改请求参数查到了别人的记录,那就是严重的数据越权漏洞。这块我建议在Service层做一层额外的数据归属校验,而不只是依赖前端的按钮显隐控制。
3. SSM后端开发要点:配置、接口与安全控制的细节
后端开发是整个系统的心脏,也是项目评审时最容易被追问技术细节的部分。SSM框架的整合过程网上教程非常多,我不打算再重复一遍完整配置,而是把实际开发中真正容易踩坑、也最能体现项目质量的关键节点单独拎出来讲。
3.1 SSM整合中最容易翻车的三个地方
第一,Spring和SpringMVC的容器边界一定要分清楚。 Spring的配置文件管Service、Dao、数据源、事务,SpringMVC的配置文件只管Controller、视图解析器、静态资源放行和JSON消息转换器。我见过不少项目把applicationContext.xml和spring-mvc.xml的内容混在一起写,结果Controller里注入Service的时候报空指针,排查半天才发现是扫描包范围重叠或遗漏。标准做法是:
xml复制<!-- Spring配置文件:只扫描service和dao -->
<context:component-scan base-package="com.example.service, com.example.dao" />
<!-- SpringMVC配置文件:只扫描controller -->
<context:component-scan base-package="com.example.controller" />
第二,MyBatis的Mapper接口和XML文件的对应关系。 namespace必须指向接口的全限定名,id必须和接口方法名一致,parameterType和resultType在复杂对象上尽量不要图省事省略。如果出现"Invalid bound statement (not found)"的报错,十有八九是XML文件没有编译到target目录,或者mybatis.mapper-locations配置的路径不对。用Maven管理项目时,记得在pom.xml的build节里加上resources配置,把src/main/java下的XML文件一并打包进classes目录。
第三,事务管理的配置。 在Spring配置文件中启用<tx:annotation-driven>,然后在Service层的公开方法上标注@Transactional。这里有个很多人忽略的坑:事务只对RuntimeException回滚,如果方法里抛的是受检异常(比如Exception),默认是不回滚的。如果项目里有自定义的业务异常,要么让它继承RuntimeException,要么在@Transactional的rollbackFor属性里指定异常类型。
java复制@Transactional(rollbackFor = Exception.class)
public void submitRecord(Long recordId) {
// 更新记录状态
practiceRecordMapper.updateStatus(recordId, 1);
// 记录一条日志,如果这里异常,前面更新状态的操作也要回滚
auditLogMapper.insertAuditLog("提交实习记录", recordId);
}
3.2 统一响应结构与接口设计
后端接口返回给小程序的数据格式一定要统一,否则前端处理起来会非常痛苦。我通常设计一个Result类,包含code、message、data三个字段:
java复制public class Result<T> {
private int code; // 200成功,401未登录,500异常
private String message; // 提示信息
private T data; // 业务数据
}
这样做的好处是,前端在小程序里封装wx.request时,只需要统一判断code值就能决定是弹提示还是走成功逻辑,不需要每个接口分别处理错误格式。
接口路径的设计也遵循一定的语义。比如:
POST /api/user/login登录GET /api/user/info获取当前用户信息POST /api/record/save保存草稿POST /api/record/submit提交记录GET /api/record/list?page=1&size=10&status=1分页查询记录POST /api/record/audit教师审核
分页是所有管理类界面都绕不开的功能。如果不想自己手写LIMIT和COUNT,可以直接引入PageHelper分页插件,在Service层查询前调用PageHelper.startPage(pageNum, pageSize),查询返回的List会自动包装成PageInfo,里面直接带了总条数、总页数等字段,非常省事。
3.3 登录鉴权和密码安全的实现
小程序端的登录流程和Web端有很大区别,这里要重点展开讲。
微信小程序的登录不是传统的账号密码登录,而是基于微信身份的静默登录。完整流程是这样的:
- 小程序端调用
wx.login(),获取一个临时凭证code - 小程序把
code通过wx.request发送到后端 - 后端拿
code去请求微信接口https://api.weixin.qq.com/sns/jscode2session,换取openid(用户唯一标识)和session_key(会话密钥) - 后端用
openid查数据库,如果查到用户则直接登录成功,查不到则引导用户补充学号、姓名等信息完成注册 - 登录成功后,后端生成一个自定义的
token返回给小程序,小程序把它存到storage里,后续每次请求携带这个token
这里有几个必须注意的坑:
第一个坑是code的一次性。code只能用一次,用过了就失效,所以后端拿到code必须立即调用微信接口去换openid,不能缓存也不能重复使用。如果并发量大的情况下同一个客户端连发两个请求,要确保两个请求各自拿code去换,而不是共用一个。
第二个坑是session_key永远不要下发到前端。session_key是微信用来加密数据的密钥,只能留在后端服务端。一旦泄露,别人就可以伪造微信支付等场景的敏感数据。有些开发者图省事把session_key一起返回给小程序,这是非常危险的做法。
第三个坑是登录态的有效期管理。自己生成的token建议用UUID或者jwt来生成,同时在数据库的user_token表里维护一份,记录过期时间。小程序每次请求时,后端通过拦截器校验token的有效性,并刷新过期时间。接口级别的登录拦截建议用SpringMVC的HandlerInterceptor来实现,只拦截需要登录的接口路径,/api/user/login这类接口直接放行。
密码安全这块也不容忽视。如果系统还保留账号密码登录的入口(比如管理员从PC后台登录),密码绝对不能明文存储。现在Java生态里最推荐的是BCryptPasswordEncoder进行哈希加密,每次登录时用matches方法校验。比MD5安全得多,而且不需要自己处理盐值的问题。
java复制// 密码加密
String encodedPwd = new BCryptPasswordEncoder().encode("123456");
// 密码校验
boolean isValid = new BCryptPasswordEncoder().matches(rawPassword, encodedPwd);
3.4 文件上传问题的处理
小程序端可以通过wx.chooseMedia选择图片,然后通过wx.uploadFile上传到后端。后端的处理逻辑是:接收MultipartFile,检查文件大小和类型,存储到服务器的指定目录(比如/data/upload/),返回可访问的URL。
这里要注意两点:
一是静态资源映射配置。SpringMVC默认不处理静态资源路径,需要在配置类里重写addResourceHandlers方法,把本地磁盘的图片目录映射到URL路径上。比如:
java复制@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/upload/**")
.addResourceHandler("file:/data/upload/");
}
二是在部署到Linux服务器时,上传目录的权限问题。Tomcat的运行用户(通常是tomcat)必须对上传目录有读写权限,否则上传过程中会报FileNotFoundException或Permission denied。我记得第一次部署这类项目时,光排查这个权限问题就花了一个多小时,最后chmod 755和chown一把梭解决。
3.5 数据权限的精细化控制
前面提到过,学生只能看自己的记录,教师只能审核自己名下的学生。这个逻辑怎么在代码层面落地?
我的做法是在DAO层的SQL层面做约束,而不是查询全量数据后在业务层过滤。比如教师查看学生记录列表的SQL:
sql复制SELECT r.* FROM tb_practice_record r
INNER JOIN tb_student s ON r.student_id = s.id
WHERE s.teacher_id = #{teacherId}
AND r.status = #{status}
ORDER BY r.record_date DESC
LIMIT #{offset}, #{pageSize}
这样数据库层就把数据范围限定好了,即使业务层代码写错了,也不会出现越权查询。同理,学生查看自己的记录列表,SQL里必须加WHERE student_id = #{currentUserId}。
4. 小程序端实现:登录流程、记录填写与真机调试的坑
小程序端是这个项目对用户最直接的呈现层,代码写得好不好,直接决定了评委或者用户的使用体验。这一节挑几个核心模块来讲,特别是那些文档里不会写、但实践中一定会遇到的坑。
4.1 小程序端项目结构与请求封装
小程序原生的项目结构很清晰:pages目录存放各页面,utils目录存放公共工具,app.js是全局逻辑,app.json是全局配置。我的建议是,按照业务模块来组织pages目录:
text复制pages/
login/ // 登录页
index/ // 首页/仪表盘
record/ // 实习记录相关
list/ // 记录列表
edit/ // 新增/编辑记录
detail/ // 记录详情
company/ // 实习单位信息
mine/ // 我的(个人中心)
请求封装这一层非常重要,统一写在utils/request.js里,把wx.request包一层:
javascript复制function request(url, method, data) {
return new Promise((resolve, reject) => {
wx.request({
url: getApp().globalData.baseUrl + url,
method: method || 'GET',
data: data || {},
header: {
'Content-Type': 'application/json',
'Authorization': wx.getStorageSync('token')
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data);
} else {
reject(res);
}
},
fail: (err) => reject(err)
});
});
}
这样每个页面调用接口时,只需要request('/api/record/list', 'GET', params).then(...),不用重复写wx.request的样板代码。如果遇到401(未登录/登录过期),可以在统一的拦截逻辑里跳转到登录页。
4.2 登录流程在小程序端的实现
上面的章节讲了登录的后端设计,这里从小程序前端的视角再补充一遍实际代码流程:
javascript复制// 页面启动时检查是否有token
onLoad: function () {
let token = wx.getStorageSync('token');
if (!token) {
this.login();
} else {
// token过期后,后端的拦截器会拦截请求,此时需要重新静默登录
this.checkLoginValid();
}
}
login: function () {
wx.login({
success: (res) => {
let code = res.code;
request('/api/user/login', 'POST', { code: code })
.then((result) => {
if (result.code === 200) {
wx.setStorageSync('token', result.data.token);
wx.setStorageSync('userInfo', result.data.userInfo);
} else if (result.code === 300) {
// 未注册,跳转到完善资料页
wx.redirectTo({ url: '/pages/login/bind' });
}
});
}
});
}
这里有个小细节:wx.login()返回的code有效期很短,登录请求发出后应该尽快提交给后端,中间不要穿插其他复杂的异步操作,否则可能出现code失效的报错。
4.3 实习记录编辑页面的设计思路
实习记录填写是学生使用频率最高的功能,这个页面的设计至关重要。我在设计时把它拆成了三个关键区域:
一是基本信息区域,包含记录日期(默认当天)、实习单位(自动带出)、实习岗位。这些信息在提交时可以直接从后端实时获取,不需要学生每次手动填。
二是正文内容区域,包含工作内容描述和心得总结。这里要用textarea组件,设置maxlength限制字数,并展示已输入字数/总字数。在手机上输入长文本的体验本身就一般,所以给一个实时字数提示会友好很多。
三是附件区域,支持拍照或从相册选择图片上传。这里的核心逻辑是:调用wx.chooseMedia选择图片,然后通过wx.uploadFile上传到服务器,拿到返回的URL后再和表单一起提交。注意,上传和提交是两个独立的步骤,不要选择完图片就立刻提交整个表单,而是先上传图片拿到URL,再和文本内容一起组装,最后统一提交保存。
保存逻辑也要分成两个按钮:存草稿和提交审核。存草稿意味着status=0,后续还可以编辑;提交审核意味着status=1,之后学生自己就不能改了。这两个按钮的交互反馈要做得明确,提交审核前弹一个确认框提示"提交后不能修改,确认提交?",防止学生误操作。
4.4 真机调试中遇到的典型坑
小程序开发有一个特点:开发工具里一切正常,一上真机问题层出不穷。下面这些坑是我实际经历过的,写出来帮你提前规避。
域名和HTTPS问题:本地开发时可以在开发者工具的"详情 -> 本地设置"中勾选"不校验合法域名",但真机预览时必须把小程序的request合法域名配置到微信公众平台后台,并且这些域名必须支持HTTPS。如果后端还在本地或者内网,真机根本访问不到,只能通过内网穿透工具或者把后端部署到有公网地址的服务器上。
顶部导航栏高度适配:iPhone的刘海屏和非刘海屏,以及Android不同机型的系统状态栏高度都不一样。小程序里可以通过wx.getSystemInfoSync()获取statusBarHeight,然后计算自定义导航栏的占位高度。如果是标准导航栏,直接用navigationBarTitleText配置标题就行,少操很多心。
上传图片的并发限制:如果学生一次选了9张图片,前端代码不要同时发起9个wx.uploadFile请求,微信开发工具的并发限制可能导致部分请求失败。更稳妥的姿势是用一个队列,控制同时上传2~3个,上传完成后再发起下一个,全部完成后更新UI。
下拉刷新和页面滚动:记录列表页建议加上enablePullDownRefresh: true启用下拉刷新,但要注意在onPullDownRefresh回调里结束后一定要调用wx.stopPullDownRefresh(),否则加载动画会一直转个不停。
5. 项目部署与运维的实用经验
一个系统如果不经历部署上线的考验,就算不上真正完成。很多同学在本地开发环境跑得好好的,一部署到服务器就各种报错。这一节把部署过程中最关键的几个环节讲清楚,帮你在答辩演示时不出Bug。
5.1 服务器环境搭建
部署这个SSM项目,一台2核4G的云服务器就足够了,操作系统推荐CentOS 7.9或Ubuntu 20.04。需要安装的环境包括:
- JDK 1.8(SSM项目标配,不要用太高版本)
- MySQL 5.7
- Tomcat 8.5以上
- Nginx(可选,用于反向代理和HTTPS证书配置)
环境的安装顺序建议是:MySQL -> JDK -> Tomcat -> 项目部署 -> Nginx。每安装完一个组件,都先验证一下当前组件是否正常运行,避免多个组件同时出问题时无法定位。
5.2 项目打包与部署
项目在IDEA里通过Maven的package命令打成War包,然后上传到服务器的Tomcat的webapps目录,启动Tomcat后会自动解压部署。这里有两个细节值得注意:
一是数据库连接信息的修改。本地开发时数据库连接地址通常是localhost:3306,到了服务器要改成服务器内网IP或公网IP,包括用户名密码都要对应调整。建议在jdbc.properties里配置,不要写死在代码里。同时,在导入SQL脚本时要注意MySQL版本之间可能存在的字符集差异,统一用utf8mb4最保险。
二是Tomcat的启动内存设置。如果项目本身不算大,默认的JVM参数一般够用。但有些同学会在服务器上同时跑MySQL和Tomcat,内存吃紧时可以在Tomcat的bin/catalina.sh里调整JAVA_OPTS参数,比如:
bash复制JAVA_OPTS="-Xms256m -Xmx512m"
让Tomcat限制在512M以内,给MySQL留出空间。
5.3 微信小程序must的HTTPS部署
微信公众平台对线上环境要求所有请求走HTTPS,这是硬性要求,没有商量空间。如果只是毕业设计演示,可以在阿里云或腾讯云免费申请一年的SSL证书,然后配置到Nginx上。Nginx配置的大致思路是:
nginx复制server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
WebSocket和代理配置不需要,毕业设计到这里已经够了。配好后,记住在小程序后台的"开发管理 -> 开发设置 -> 服务器域名"里,把request合法域名改成https://yourdomain.com,这样真机就能正常访问了。
5.4 答辩演示前一定要做的检查
项目部署好之后,不要急着关机,答辩前务必做一轮完整的冒烟测试。我建议重点检查下面几个场景:
- 新学生注册后能正常完善资料,指导教师的账号能看到该学生
- 学生提交实习记录后,状态变为"待审核",教师端能看到待办提醒
- 教师驳回记录的反馈信息,学生在列表里能看到红色的驳回原因
- 切换账号登录时,学生不能看到其它学生的数据
- 网络断开或服务器重启后,小程序端有合理的错误提示,而不是白屏
这些场景基本覆盖了项目核心功能的完整闭环,任何一个环节出问题,都能提前发现并修补,避免在答辩现场翻车。
6. 从源码中学习:这套系统的可扩展方向
拿到了完整的SSM + 微信小程序源码,很多同学第一反应是想赶紧跑起来看效果,但是其实源码的价值远远不止"跑起来"这一点。学会看源码、改源码、扩源码,才能让这个项目真正变成你自己的成果,在答辩时也能对答如流。
6.1 如何高质量地阅读这套源码
拿到项目后,我建议按照"数据库 -> 后端 -> 前端 -> 联调"的顺序来读,而不是一上来就全部打开。
第一步,先看数据库脚本。打开SQL文件,看每张表的字段注释,脑子里还原出整个业务模型的轮廓。在这个过程中,你可能会发现一些表之间有外键关联,也可能发现状态字段的取值范围,这些都是在代码阅读中需要留意的关键信息。
第二步,看后端的包结构。通常SSM项目的包结构是controller、service、mapper、entity、config等。先看controller层的每个接口方法,了解系统的全部功能入口;再看service层的实现类,理解每个核心业务流程的代码逻辑;最后看mapper层的XML文件,搞清楚每个SQL怎么写、为什么要这么写。
第三步,看小程序的页面结构。打开pages目录下的页面,对照后端接口,理解前端是如何调用后端拿到数据并渲染到界面上的。这个环节能帮你在答辩时,快速回答"前端如何显示一个列表""提交按钮触发什么请求"这类问题。
第四步,动手改动一个小功能。比如把首页的标题改掉,或者在记录列表增加一个"只看本周记录"的筛选条件。从小改动开始,逐步加深对代码的理解,直到能独立增加一个完整的功能模块。
6.2 几个有价值的扩展方向
这个实习记录系统的数据结构已经足够健壮,扩展业务功能非常方便。下面几个方向,按难度从低到高排列,你可以根据自己的时间和能力选择:
一是增加实习报告模块。学生实习期结束时,系统根据日常的实习记录自动生成实习报告初稿,学生在此基础上编辑后提交。这个功能需要增加一张report表,以及在学生端增加报告编辑页面。逻辑上不难,但看起来工作量很充实。
二是增加消息通知功能。利用微信小程序的订阅消息能力,在学生提交记录后给教师推送一条审核提醒,在教师审核通过后给学生推送一条审核结果通知。开发者需要在微信公众平台申请模板消息,然后后端调用微信的订阅消息接口。这个功能非常实用,也是评审老师比较看重的交互亮点。
三是增加数据分析图表。在教师端用ECharts或者原生Canvas画一个柱状图,展示学生近一个月提交记录的数量趋势。还可以统计整个班级的实习完成率,教师一眼就能看出哪些学生不积极。后端加一个统计接口,前端加一个图表页面,成果很直观。
6.3 从毕业设计到项目经验:如何讲清楚你的项目
最后聊一个很多同学忽略的问题:项目做完了,不代表你能在答辩时讲清楚。评审老师通常会问这四类问题:
- 为什么选择这个课题、这个技术方案?
- 系统的核心业务流程是怎样的?
- 在开发过程中遇到的最大困难是什么,怎么解决的?
- 系统的不足之处以及未来改进方向是什么?
这些问题没有标准答案,但你的回答一定要体现真实的思考过程。比如问到技术选型,不要只说"SSM是主流框架",而是说"选择SSM是因为我熟悉Spring的依赖注入和事务控制机制,MyBatis灵活的SQL映射适合业务多变的查询统计,并且社区资料丰富,遇到问题能高效解决"。问到最大困难,可以讲登录态管理的设计过程,从最初简单的token校验到后来增加过期时间、拦截器统一鉴权、学生数据权限隔离的完整优化过程。
把源码吃透,把每一步设计的原因想明白,答辩时自然会底气十足。这套系统虽然业务不算复杂,但麻雀虽小五脏俱全,从用户体系、权限控制、核心业务流转到文件上传、消息通知,覆盖了一个完整业务系统的全部关键环节。认真把它弄懂,对你的成长会非常有帮助。
