ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java毕业设计实战:基于Spring Boot与微信小程序的课堂考勤系统开发全解析

Java毕业设计实战:基于Spring Boot与微信小程序的课堂考勤系统开发全解析 简介一套基于Spring Boot的Java本科毕业设计《小程序上课考勤系统》完整源码包面向正在筹备考勤类毕设或练习小程序后台开发的Java学习者可直接借鉴完整项目结构与功能模块。压缩包共488个文件、4.3MB主要包含51个Java后台源码、128个js脚本、75个wxml页面与74个wxss样式另有SQL数据库脚本和系统操作文档覆盖前端页面、后端接口、数据库初始化与部署说明。系统实现后台管理与小程序端GPS签到、定位打卡、迟到统计等核心功能开发框架采用快速开发模式界面和接口分层清晰适合理解Spring Boot与微信小程序的交互流程。包内附docx操作文档可帮助读者快速配置JDK1.8与MySQL5.7环境并运行项目。该资源已有944人学习是完成本科毕业设计或课程项目的实用参考。1. 从“java开发项目本科毕业设计小程序上课考勤系统源代码”这个标题说起为什么这个题目永远不过时每年一到毕业季Java 方向的学生就会在选题列表里看到“基于小程序的课堂/上课考勤系统”。这个东西听起来不算前沿却常年是本科毕业设计里的常青树。原因很直接它同时踩中了三个刚需——Java 后端在就业市场的基本盘、微信小程序在校园场景里的普及度以及考勤这个每个学校都真实存在的管理痛点。更关键的是它的工作量可控一个人用两个月时间从零到跑通一个能演示、能答辩、能写进简历的完整系统完全可行。如果你正在找毕业设计题目或者已经选了类似题目但还没开工这篇笔记就是按“一个 Java 基础扎实但没做过完整项目的学生”的视角来写的。我把这套系统从需求、数据库、前后端实现到答辩验收的完整路径和你捋一遍中间夹杂我自己带人做项目时踩过的坑。2. 小程序上课考勤系统到底要做成什么样需求拆解与技术选型2.1 先把角色和流程理顺再谈代码很多学生拿到“上课考勤系统”第一反应是做一个签到页面用户点一下按钮就完事。这是典型的没做需求分析就开始写代码写到一半必然翻车。真实的课堂考勤场景里至少要存在三类角色和三条闭环流程。三类角色是学生、教师或教务管理员、系统管理员。学生要能登录小程序、查看自己的课程表、在老师发起签到时完成签到、查看自己的缺勤记录。教师要能管理课程一门课对应哪些学生、发起一次考勤限定时间和范围、实时查看出勤情况、按课程导出缺勤名单。管理员负责基础数据维护——哪个学生属于哪个班、哪个班在哪个教室上什么课以及处理补签申请。三条闭环流程是教师创建课程并导入学生名单 → 学生加入课程 → 上课时教师发起签到学生提交签到 → 系统比对签到时间和地理位置 → 返回签到结果教师查看出勤统计 → 导出记录 → 作为期末平时成绩的依据。这个需求拆解的意义在于它直接决定了你的数据库要建几张表、后端要写多少个接口、小程序端要做多少个页面。如果你在开题报告和论文里能把这三条流程画清楚评审老师第一印象就会好很多因为很多人交上来的系统根本没有“流程闭环”的概念——教师端和学生端各做各的数据都不打通。2.2 Spring Boot MyBatis-Plus MySQL 的选型理由用最稳的组合降低开发风险后端技术栈我推荐直接上 Spring Boot 2.x MyBatis-Plus MySQL 5.7/8.0。原因不是它最先进而是它最适合本科毕业设计这个场景。Spring Boot 2.x 的生态最成熟网上能找到的教程数量是最多的意味着你卡住的时候几乎一定能搜到答案。MyBatis-Plus 的 BaseMapper 提供了开箱即用的单表 CRUD能省掉大量重复的 SQL 编写。MySQL 是你在《数据库原理》课程里就已经学过的东西写进论文里也说得清楚。有学生问是不是要用 Spring Cloud 微服务架构让项目看起来更有“含金量”。我的建议是不要。一个课堂考勤系统并发量撑死也就是一个院系几百人同时签到单体应用绰绰有余。强行上微服务只会带来分布式事务、服务注册发现、网关鉴权等一系列你在论文里根本解释不清的问题答辩时被追问大概率答不上来。同样的道理Redis 缓存可以加但必须想清楚缓存的数据是什么——比如用 Redis 存签到验证码能作为技术亮点写进论文如果只拿它做分布式锁而你根本解释不清锁竞争的场景那就别加。前端小程序端原生微信小程序和 uniapp 都可以选。如果只做微信小程序且以后不想跨端用原生即可API 文档官方最全如果想兼容后续发布到其他平台用 uniapp 更好。毕业设计阶段我更推荐原生微信小程序——少一层框架转换真机调试时少一类“样式对不上”的玄学问题。不过两种方案都会在后面的章节里涉及因为很多同学用的是 uniapp 模板。2.3 一个容易被忽略的环节环境变量的坑要提前趟平做 Java 开发的人对“环境变量配置”这件事习以为常但毕业设计阶段这一环反而坑翻了最多人。最常见的情况是开发机上 JDK 和 Maven 都配好了代码能跑然后到了要写论文“系统运行环境”这一节的时候才发现自己根本说不清 JDK 版本、Maven 版本、MySQL 字符集这些细节。我的建议是开工第一天就把运行环境固化成一份笔记包括但不限于JDK 1.8或 11/17取决于 Spring Boot 版本、Maven 3.6、MySQL 8.0、微信开发者工具版本。固定版本有另一个重要作用避免“昨天还好好的今天突然编译报错”的运气问题。这类问题十有七八是版本不一致导致的你排查了一晚上最后发现是某次升级把依赖搞坏了白白浪费时间。3. 从零搭后端数据库设计与核心接口实现3.1 数据库设计的四张核心表与它们的边界根据前面的需求拆解一个能支撑答辩的考勤系统至少需要四张核心表外加两张辅助表。设计数据库时很多学生会犯一个典型错误把考勤记录直接关联到“用户表”没有区分“课程”和“选课关系”这两个概念。后果是同一个学生选了同一门课老师发起考勤时学生签到记录无法精确落到某一次课的某一次考勤里统计数据全是乱的。我第一次带学生做这个项目时看到他设计的学生考勤表里居然存了 course_name、teacher_name、student_name 三列冗余文本当场就让他重改了——这种表结构在答辩时老师只要问一句“如果老师换了历史考勤记录的教师名字要不要跟着变”他就答不上来。正确的设计是用外键关系关联 ID展示时再联表查询。以下是核心表的结构代码-- 用户表只存登录账号与身份标识 CREATE TABLE sys_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(32) NOT NULL UNIQUE COMMENT 登录账号, password VARCHAR(128) NOT NULL COMMENT bcrypt加密密码, real_name VARCHAR(32) NOT NULL COMMENT 真实姓名, role TINYINT NOT NULL COMMENT 1-学生 2-教师 3-管理员, openid VARCHAR(64) DEFAULT NULL COMMENT 微信openid, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 课程表 CREATE TABLE course ( id BIGINT AUTO_INCREMENT PRIMARY KEY, course_name VARCHAR(64) NOT NULL COMMENT 课程名称, teacher_id BIGINT NOT NULL COMMENT 关联sys_user.id教师, classroom VARCHAR(64) DEFAULT NULL COMMENT 上课教室, start_time TIME NOT NULL COMMENT 上课时间用于自习校验, end_time TIME NOT NULL COMMENT 下课时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程表; -- 选课关系表维系学生与课程的关联以及签到码的存储 CREATE TABLE course_student ( id BIGINT AUTO_INCREMENT PRIMARY KEY, course_id BIGINT NOT NULL, student_id BIGINT NOT NULL, checkin_code VARCHAR(6) DEFAULT NULL COMMENT 本次课程签到码由教师生成, expire_time DATETIME DEFAULT NULL COMMENT 签到码有效期 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT选课关系/签到码表; -- 考勤记录表 CREATE TABLE attendance ( id BIGINT AUTO_INCREMENT PRIMARY KEY, course_id BIGINT NOT NULL COMMENT 课程ID, student_id BIGINT NOT NULL COMMENT 学生ID, teacher_id BIGINT NOT NULL COMMENT 教师ID快照防止课程安排变动, status TINYINT NOT NULL DEFAULT 0 COMMENT 0-未签到 1-正常 2-迟到 3-缺勤 4-请假, checkin_time DATETIME DEFAULT NULL COMMENT 实际签到时间, checkin_code VARCHAR(6) DEFAULT NULL COMMENT 签到时使用的签到码, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT考勤记录表;这套表设计的核心思路是course_student 表不只是简单的选课关系它还承担了“本次签到码”的存储职责。签到码由教师发起签到时生成学生在界面上输入六位数字码后端比对 course_student 表中的 code 与 expire_time校验通过则写入 attendance 表。这样设计的好处是一次上课只用一个码所有选课学生都能用同一个码签到也不需要给学生下发二维码再让学生扫码——扫码方案在真机上经常因为图片压缩或权限问题读不出二维码非常容易翻车。3.2 后端项目初始化与第一个接口最小可运行配置长这样后端我用 Spring Boot 2.7 MyBatis-Plus 3.5 来演示。这里有一个细节很多学生喜欢在 pom.xml 里堆依赖把能想到的 starter 全加进去结果启动时因为依赖冲突报错。我的习惯是只用最小集web、mybatis-plus、mysql-connector、lombok、validation、jwt。除此之外一律不引。dependencies !-- Web 启动器 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis-Plus 启动器 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- MySQL 驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- Lombok 减少样板代码 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies对应的 application.yml 配置我建议把数据库连接池参数写清楚。很多人在本地 MySQL 上起不来项目就是因为在连接串上少了一个参数server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/attendance?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 你自己的密码 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0注意 url 里的 serverTimezoneAsia/Shanghai 和 allowPublicKeyRetrievaltrue 这两个参数。前者不写数据库查询出来的时间会差 8 小时后者不写MySQL 8.0 连接时会直接抛连接失败——这两个问题都属于“网上答案很多但都是零散帖子”的典型你搜一次能解决但每次新建项目都要再配一次。第一个接口建议从“小程序登录换取 token”开始。逻辑是前端 wx.login() 拿到 code后端拿 code 发给微信接口换 openid然后查询 sys_user 表如果存在则生成 JWT 返回不存在则自动注册。这个接口的意义在于打通整个链路的认证环节。RestController RequestMapping(/api/user) RequiredArgsConstructor public class UserController { private final UserService userService; PostMapping(/login) public Result? login(RequestBody LoginDTO dto) { // dto.code 由前端 wx.login() 获得向后端换取业务登录态 String openid userService.code2Openid(dto.getCode()); User user userService.findOrCreateByOpenid(openid, dto.getUserType()); String token JwtUtil.generateToken(user.getId(), user.getRole()); return Result.success(new LoginVO(token, user.getRealName(), user.getRole())); } }wc 登录流程在开发阶段容易踩的坑是真机调试时 wx.login 返回的 code 只能用一次第二次调用必须重新 wx.login。很多学生为了调试方便把 code 写死在代码里结果每次换人测试都要重新生成非常劝退。正确做法是小程序端每次点击登录都先调用 wx.login() 拿新 code再向后端发送。3.3 签到接口与数据一致性按“先验证后写入”的顺序编排签到接口是整个系统的核心也是答辩时老师最可能追问的地方。最常见的追问是“如果一个学生同时打开两个手机用同一个签到码提交两次签到你的系统怎么保证不重复”这个问题考察的就是数据一致性处理。很多学生的第一版代码是先查 attendance 表有没有记录没有就插入。这个逻辑在单机测试时没问题但在两个请求并发到达时两条查询都返回“没有记录”于是两条写入都成功就产生了重复签到。解决方式有两种。第一种是在表结构上做唯一约束把 (course_id, student_id, create_date) 设为唯一索引让数据库从物理层面挡住重复。第二种是为签到请求增加防重令牌即前端在加载签到页面时向后端请求一个一次性 token提交签到必须携带该 token后端消费一次后立即失效。我一般两种同时用数据库唯一索引做兜底防重令牌做业务拦截。以下是加了唯一索引的签到实现Override Transactional(rollbackFor Exception.class) public boolean checkin(CheckinDTO dto) { // 1. 校验签到码与有效期 CourseStudent cs courseStudentMapper.selectOne(new LambdaQueryWrapperCourseStudent() .eq(CourseStudent::getCourseId, dto.getCourseId()) .eq(CourseStudent::getStudentId, dto.getStudentId()) .eq(CourseStudent::getCheckinCode, dto.getCheckinCode())); if (cs null || cs.getExpireTime().before(new Date())) { throw new BizException(签到码无效或已过期); } // 2. 乐观锁更新 course_student防止同一学生并发重复签到 int rows courseStudentMapper.update(null, new LambdaUpdateWrapperCourseStudent() .eq(CourseStudent::getId, cs.getId()) .eq(CourseStudent::getCheckinCode, dto.getCheckinCode())); // 条件里带上签到码相当于 CAS if (rows 0) { throw new BizException(签到失败请勿重复提交); } // 3. 写入考勤记录 Attendance attendance new Attendance(); attendance.setCourseId(dto.getCourseId()); attendance.setStudentId(dto.getStudentId()); // teacherId 通过 course 表联查快照 attendance.setStatus(1); attendance.setCheckinTime(new Date()); attendance.setCheckinCode(dto.getCheckinCode()); attendanceMapper.insert(attendance); return true; }这段代码的核心逻辑在第二步用 LambdaUpdateWrapper 的 eq 条件把原来的签到码作为更新条件即 CASCompare And Swap思路。当两个请求同时到达时数据库层面的 update 会串行执行第一个执行成功第二个因为 where 条件里的 checkin_code 已经被清空影响行数为 0所以能拦截住重复提交。这样即使在多实例部署下也不会出重复签到的问题。4. 小程序端实现与对接让签到动作真正可用4.1 小程序页面结构与 wxml 布局一个可复制的签到页后端接口就绪后前端的核心工作是把“学生输入六位签到码 → 提交到后端 → 展示签到结果”这条链路打通。这个页面看起来简单但实现上有一个细节签到码的输入框和提交按钮必须考虑误触问题——学生上课时手机放在桌面上老师一说签到他拿起手机就要输码如果输入框被自己手碰一下丢了焦点体验就很差。好的做法是用 input 组件的 typenumber 限定数字键盘再配合微信小程序的 focus 属性让页面加载后自动聚焦。签到页面的 wxml 长这样view classpage view classcourse-info text classcourse-name{{courseName}}/text text classcourse-time{{courseTime}}/text /view view classcode-input input typenumber maxlength6 placeholder请输入6位签到码 focus{{autoFocus}} bindinputonCodeInput bindconfirmonSubmit / /view button classsubmit-btn disabled{{!canSubmit}} loading{{submitting}} bindtaponSubmit 立即签到 /button view classresult wx:if{{attendResult}} text{{attendResult 1 ? 签到成功 : 签到失败 errorMsg}}/text /view /view逻辑部分要注意的是disabled 状态不能只看输入长度还要看 submitting 状态否则用户快速双击提交按钮会触发两次请求。如果你用的是 uniapp 开发微信小程序把 input 改成 uni-app 的 input 组件bindinput 改成 inputbindtap 改成 click 就好其余逻辑一致。4.2 请求封装与 Token 处理小程序最容易出问题的环节小程序端对接后端时最常见的两类问题一是 request 请求的 header 没有带 Authorization 头导致后端返回 401二是开发时设置了不校验合法域名却忘了在真机预览时配置 request 合法域名。这两类问题都必须提前处理不然演示当天必然翻车。我的习惯是封装一个 request.js把所有网络请求和 token 处理收敛到一个文件里。这里给出一个精简版// utils/request.js const BASE_URL http://localhost:8080/api; function request(path, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL path, method: method, data: data, header: { Content-Type: application/json, // 读本地存储的 token字符串为空时后端不会识别 Authorization: wx.getStorageSync(token) ? Bearer ${wx.getStorageSync(token)} : }, success(res) { // 业务码 200 表示成功401 跳转登录页 if (res.data.code 200) { resolve(res.data.data); } else if (res.data.code 401) { wx.removeStorageSync(token); wx.redirectTo({ url: /pages/login/index }); reject(res.data); } else { wx.showToast({ title: res.data.message || 请求失败, icon: none }); reject(res.data); } }, fail(err) { wx.showToast({ title: 网络异常请检查后端服务, icon: none }); reject(err); } }); }); } module.exports { request };注意 BASE_URL 在真机调试时若连不上本地后端可以把 localhost 换成电脑的局域网 IP但必须保证手机和电脑在同一 Wi-Fi 下。微信开发者工具里有“不校验合法域名”的开关默认不勾选但这个开关只管开发工具不管真机真机预览时必须在小程序后台的“开发管理 → 开发设置 → 服务器域名”里把后端地址配置进 request 合法域名否则请求会被微信拦截报 “url not in domain list”。4.3 教师端考勤统计页比学赶超的亮点功能很多学生的系统做到能签到就收手了整篇论文没一处可以说“这个功能有难度”的地方。我强烈建议在教师端加一个“按课程查看出勤统计”的页面左侧是课程列表右侧是学生的出勤率柱状图或列表。这个功能的技术难点在数据库端——需要一次性取出一门课的所有学生分组统计出勤情况再用联表查询补上学生姓名。SQL 我一般这样写SELECT u.real_name AS studentName, COUNT(a.id) AS totalCount, SUM(CASE WHEN a.status IN (1, 2) THEN 1 ELSE 0 END) AS normalCount, ROUND(SUM(CASE WHEN a.status IN (1, 2) THEN 1 ELSE 0 END) / COUNT(a.id) * 100, 1) AS attendRate FROM course_student cs JOIN sys_user u ON cs.student_id u.id LEFT JOIN attendance a ON a.course_id cs.course_id AND a.student_id cs.student_id WHERE cs.course_id #{courseId} GROUP BY u.id ORDER BY attendRate ASC LIMIT 20;这个查询里的 LEFT JOIN 是关键必须以 course_student 为主表即选课学生为基准而不是以 attendance 为主表。如果以 attendance 为主表那些从未签过到的学生根本不会出现在统计结果里——这是新手最常见的错误之一。查出来的结果返回小程序端后用 wx-charts 或简单 view 渲染即可甚至用 echarts 的微信小程序版也可以。这个页面一旦做出来论文里的“系统测试”章节就有了实质性的截图材料。5. 避坑指南上课考勤系统开发中常见的 5 个坑5.1 现象数据库里考勤记录的日期全部比实际时间早一天原因application.yml 里没有配置 serverTimezone或者配置成了 UTC。MySQL 8.0 默认连接时区是 UTC导致 Java 代码里的 new Date() 写入数据库后被 MySQL 转换为 UTC 时间存的查出来再转回本地时区时就少了 8 小时。这不是业务代码的问题是连接参数配置的问题。解决在数据库连接串上强制加 serverTimezoneAsia/Shanghai并在 Jackson 配置里指定 time-zone。这里最坑的是本地 Windows 开发时因为系统时区设置的关系可能看不出问题部署到 Linux 服务器后问题才暴露。所以从第一天就把时区参数配好不要赌运气紧随其后。5.2 现象签到接口在开发者工具里正常真机上一直报“请求失败”原因开发工具默认关闭了域名校验所以任何地址都能请求真机预览时必须走小程序后台配置的合法域名。很多人忽略了这一步以为在开发者工具里能跑就等于真机能用。解决在小程序后台把后端域名加到 request 合法域名并且必须使用 HTTPS除非在开发设置里勾选“开发环境不校验请求域名”但真机预览时这个开关无效。如果你没有现成域名最简单的方式是在开发者工具里点击“详情 → 本地设置 → 勾选不校验合法域名”然后在真机调试模式下选择“真机调试”而不是“预览”——“真机调试”仍然会走工具里的不校验域名开关。5.3 现象学生签到时提示“签到码无效”但教师端明明生成了码原因签到码存到数据库时被去掉了前导零——比如教师生成的码是 045782但字符串类型在 MySQL 中不会丢零问题更多地出在教师生成码时用了 Integer 类型转成字符串时前导零被丢弃。或者你生成码时用的是随机数但相同数字被生成过两次先前的码覆盖了新的码。解决签到码字段用 CHAR(6) 并配合代码里 String.format(%06d, random.nextInt(1000000)) 格式化同时要保证每次发起签到时无论是同一课程还是不同课程生成的码都要唯一。用 Redis 的 setnx 存储码与过期时间能同时解决唯一性和过期问题。5.4 现象一个学生上一门课时签到了两次系统两条记录都显示正常原因考勤表没有做唯一性约束后端代码只判断“有没有记录再插入”并发下会穿透。这属于我在 3.3 里提到的数据一致性问题是答辩高频问点。解决给 attendance 表加唯一索引 (course_id, student_id, create_date)同时在 service 层做防重校验。注意唯一索引的 create_date 最好是“课程日期”而不是“签到时间”因为一个学生在同一课程同一天只会签到一次如果用签到时间做唯一约束第一次签到与第二次签到的日期精确到秒不同索引挡不住重复。5.5 现象微信开发者工具打开项目时白屏Console 报“app.json 未找到”原因从网上下载的源码包解压后打开时选错了目录或者项目包是用 uniapp 生成的需要先编译成小程序代码才能用微信开发者工具打开直接用工具打开 uniapp 源码就是白屏。解决先确认项目的目录结构。原生微信小程序项目根目录下有 app.json、app.js、project.config.jsonuniapp 项目源码里只有 src 和 package.json必须用 HBuilderX 或 uni-app CLI 编译到 dist/build/mp-weixin 目录再打开这个目录。每次改动源码后都要重新编译光记住这个流程就能省下半天排查时间。6. 从“能跑”到“能答辩”验收方法与两个提分技巧把系统做出来只是第一步毕业设计最终要过答辩这一关。而答辩时老师不会只看你演示他会直接看代码、问设计。这里有一个很多人吃亏的教训功能都做了但项目结构和论文架构对不上。你在论文里写“系统采用分层架构”代码里却在 Controller 里直接写 JDBC那答辩现场一定被当场质疑。我的建议是Spring Boot 分层设计至少要保持 Controller → Service → Mapper 三层清晰可见哪怕 Service 里逻辑很简单也必须拆出来一层。验收方法我一般按三个维度来。功能维度核心链路测五遍以上包括学生签到、教师发起签到、教师查看统计、管理员导入学生名单边界维度签到码过期、重复签到、未选课学生签到、课程不存在时发起签到这些异常场景必须逐一验证并给出友好提示数据维度统计页面的出勤率数据与原始记录逐条核对确保时间、状态、姓名三列完全对应。建议把这几个维度的测试截图整理成论文的“系统测试”章节素材截图时把时间和操作人信息露出来真实感强很多。最后一个提分技巧是写一个 README 或“部署说明书”。里面写清楚数据库初始化脚本、后端启动参数、小程序 AppID 配置方法、真机调试步骤。这份文档看起来是给老师看的运行手册实际上是给你自己留的后悔药——答辩当天不管谁问你“这个系统怎么部署”你都能立刻背出来。很多学生做完项目就扔半年后要重新演示环境早就忘了怎么配只能熬夜找当初的搭建过程。提前花一小时写好省得后面花三天反刍。希望这篇笔记能帮你少踩几个坑把毕设稳稳落地。本文还有配套的精品资源点击获取
返回列表