ARTICLE DETAIL

资讯详情

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

SpringBoot+SSM作业管理系统:从数据库设计到评分统计实战

SpringBoot+SSM作业管理系统:从数据库设计到评分统计实战 1. 为什么是作业管理系统这个项目要解决的真实痛点做这个项目的起因很直接。我接过不少学校内部系统的小项目其中被问到最多的需求就是作业管理——老师布置作业、学生提交作业、老师批改给分。这个场景看起来简单但实际处理起来问题非常多。最常见的现状是老师在微信群里发作业通知学生用邮件交作业、用QQ私聊补交、甚至直接拍张照片发群里。到了期末统计成绩的时候老师需要手动把几十份邮件附件下载下来打开一个个看再在Excel里手工登记分数。如果中间有一份文件命名不规范、一个学生漏交了、一次补交没有登记成绩统计就会出错。更麻烦的是如果同一个老师带多个班布置的是同一份作业那么哪个班交了几份、谁没交、平均分是多少这类问题靠人工统计几乎不可能在短时间内算清楚。所以这个springbootssm结构下的课堂作业布置评分管理系统要解决的本质上是三条线作业布置线老师从创建课程、布置作业、设置截止时间到发布通知整个流程数字化。作业提交线学生在线查看待办作业、按时提交文本或附件、查看批改反馈。评分统计线老师在线批改、打分、写评语系统自动汇总成绩并生成统计报表。这个系统不是我凭空设想的而是我实际开发并交付过的一类项目。它特别适合两类人来参考一是正在准备毕业设计或课程设计的学生这个业务足够完整、技术栈足够典型、演示效果好二是小团队或学校内部打算自建工具的场景功能覆盖度高但不过度设计。简单总结它的核心价值把老师从收作业-整理-批改-登记的重复劳动中解放出来把作业数据变成可以被系统追踪和分析的结构化数据。这就是我第一篇要讲的项目的业务边界。2. 技术选型分析SpringBoot与SSM的组合为什么是首选2.1 SSM与SpringBoot的真实关系很多人一开始就没搞懂先解决一个非常常见的认知误区。很多初学者会把SSM和SpringBoot当作两种对立的东西觉得用了SpringBoot就不算SSM项目。这个理解是错的。SSM指的是Spring SpringMVC MyBatis这个经典组合。它定义了项目的三层架构思路Spring负责对象管理和事务控制SpringMVC负责请求路由和参数绑定MyBatis负责数据库映射。而SpringBoot本质上不是一个新的Web框架它是一个基于Spring生态的快速开发脚手架帮我们自动配置了大量的底层组件让我们不用再写繁琐的XML配置和依赖管理。换句话说这个项目的真实技术栈应该是用SpringBoot做基础框架和自动配置在它内部整合SpringMVC处理Web层请求整合MyBatis做数据持久化。所以springboot_ssm867作业管理系统这个命名是完全合理的——它是以SpringBoot为壳、以SSM为核的典型工程。我在设计这个项目时的选型逻辑是这样的对比项纯SSMSpringSpringMVCMyBatisSpringBoot整合SSM配置复杂度需要写大量XML配置数据源、事务、扫描、视图解析器自动配置少量yml搞定依赖管理手动管理版本兼容性starter统一管理版本部署方式通常打WAR包丢Tomcat打JAR包直接java -jar运行适合场景老项目维护新项目、毕设、中小系统显然对于作业管理系统这种中等规模的Web应用SpringBoot整合SSM既能保留MyBatis灵活的SQL控制能力又能享受SpringBoot的开箱即用体验。后期如果要做Vue前后端分离改造SpringBoot也能轻松暴露REST接口扩展空间很大。2.2 项目目录结构从入口到Mapper的分层设计我创建项目时用的分组路径是com.example.homework整个包结构如下com.example.homework ├── HomeworkApplication.java // SpringBoot启动类 ├── controller/ // 控制层接收请求、返回页面或JSON │ ├── LoginController.java │ ├── TeacherController.java │ └── StudentController.java ├── service/ // 业务层事务逻辑、业务规则 │ ├── HomeworkService.java │ └── impl/ │ └── HomeworkServiceImpl.java ├── mapper/ // MyBatis的Mapper接口 │ ├── UserMapper.java │ ├── HomeworkMapper.java │ └── SubmitMapper.java ├── entity/ // 实体类与数据库表对应 │ ├── User.java │ ├── Course.java │ ├── Homework.java │ └── HomeworkSubmit.java ├── config/ // 配置类拦截器、上传配置等 │ └── WebMvcConfig.java ├── interceptor/ // 登录拦截器 │ └── LoginInterceptor.java └── utils/ // 工具类 ├── Result.java └── FileUtils.javaresources目录下还有几个关键文件resources/ ├── application.yml // 端口、数据源、MyBatis、文件上传配置 ├── mapper/ // MyBatis的XML映射文件 │ ├── UserMapper.xml │ ├── HomeworkMapper.xml │ └── SubmitMapper.xml └── templates/ // 页面模板如果使用Thymeleaf ├── teacher/ └── student/这套分层的核心思想就是各层各司其职Controller不写业务逻辑Service不直接拼SQLMapper只管数据访问实体类不掺杂多余字段。这样后续加功能、修Bug的时候你能快速定位要改哪一层而不是在一个类里翻几百行代码。2.3 为什么不选择前后端分离架构还有一个读者肯定会问的问题现在SpringBoot项目很多都是做前后端分离用Vue写前端后端只出JSON接口为什么这个项目不采用这种架构我的回答是要看项目的实际规模和交付对象。作业管理系统如果给一个学校内部使用部署环境很简单维护人员很可能只有一个网管。用Thymeleaf模板引擎做服务端渲染后端一次性返回页面和数据部署时只打一个JAR包不需要额外部署Nginx、不需要配Node环境、不需要解决跨域问题。对于课程设计答辩或者小团队自用这种单体模板引擎的方案是最稳定的。如果你打算后续前后端分离也可以参考这样的改造路径Controller层返回JSON而不是返回视图静态页面用Vue重写通过axios调用接口。因为业务逻辑和Mapper层完全不变改造的成本主要在Web层。这也是我选择在架构上只做SSM整合、不强行上前后端隔离的原因——让技术选型服务于业务而不是为了炫技增加复杂度。3. 数据库设计与作业状态流转3.1 核心表结构用户、课程、作业、提交记录数据库设计的质量直接决定这个系统好不好扩展。我把核心表拆成五张用户表、课程表、作业表、提交记录表以及用户-课程关联表。用户表user字段设计如下字段类型说明idint 主键自增用户IDusernamevarchar(50) 唯一登录账号如学号或工号passwordvarchar(255)登录密码建议MD5或BCrypt加密存储real_namevarchar(50)真实姓名方便老师批改时识别学生roletinyint角色1管理员 2教师 3学生这里有一个容易被忽略的设计点role字段用tinyint而不是字符串。用数字表示角色的好处是查询效率高、修改灵活坏处是不够直观。所以我一般在实体类中加一个roleName的派生字段在查询时用SQL的CASE WHEN把数字转换成教师学生这样的中文这样页面展示方便权限判断时仍然比较数字两者兼顾。课程表course字段id、course_name课程名、teacher_id任课教师ID、create_time。教师和学生之间的多对多关系通过单独一张user_course关联表来维护。学生选了哪门课老师教哪门课都记录在关联表中。作业表homework这是核心业务表字段包括字段类型说明idint 主键自增作业IDcourse_idint所属课程关联课程表titlevarchar(100)作业标题contenttext作业要求详细描述attachment_pathvarchar(255)教师上传的附件路径可空deadlinedatetime截止时间create_timedatetime发布时间提交记录表homework_submit这个表关联了哪个学生提交了哪份作业、得了多少分。字段id、homework_id、student_id、submit_content学生提交的文本、file_path学生提交的附件、submit_time、score得分、comment教师评语、status状态。这张表包含了评分功能的核心数据。3.2 作业状态机从布置到评分的完整流转这个系统里最值得抠细节的是状态设计。我定义了一个status字段来标记作业在教师和学生的双重视角下的状态取值如下0未提交学生对某作业的初始状态1已提交待批改学生提交后教师未评分2已评分教师完成批改学生可以看到分数3已逾期截止时间已过但学生未提交自动变为已逾期状态流转图用文字描述不画图是这样的教师在homework表创建作业并设置deadline → 系统自动为选了这门课的每个学生生成一条未提交的submit记录status0→ 学生在截止时间前提交 → status变为1 → 教师评分 → status变为2。如果到截止时间还没有提交则通过一个定时任务或者查询时动态判断把status置为3。这里有个关键选择为什么在布置作业时就预先创建submit记录而不是等学生提交时才创建因为这样查询谁没交作业变得异常简单只需要SELECT * FROM homework_submit WHERE status 0 OR status 3一条SQL就能统计出未交名单。如果等学生提交时才插入记录那么没交作业的人在数据库里根本不存在你只能通过比较选了这门课的人和提交了的人来查缺失逻辑复杂且数据库压力大。3.3 MyBatis XML中几个实用的动态SQL写法既然用了MyBatisXML映射文件里的动态SQL就是基本功。我挑几个有代表性的写法分享。作业列表的联表分页查询教师端按课程查作业select idselectHomeworkPage resultTypecom.example.homework.entity.Homework SELECT h.*, c.course_name AS courseName, u.real_name AS teacherName FROM homework h LEFT JOIN course c ON h.course_id c.id LEFT JOIN user u ON c.teacher_id u.id where if testcourseId ! null and courseId ! AND h.course_id #{courseId} /if if testkeyword ! null and keyword ! AND h.title LIKE CONCAT(%, #{keyword}, %) /if /where ORDER BY h.create_time DESC /select统计未提交学生名单评分页展示谁还没交select idselectUnsubmittedStudents resultTypecom.example.homework.entity.SubmitVO SELECT u.real_name AS studentName, u.username AS studentNo, s.status, s.deadline FROM homework_submit s JOIN user u ON s.student_id u.id WHERE s.homework_id #{homeworkId} AND (s.status 0 OR s.status 3) ORDER BY u.username /select很多初学者写动态SQL时会忘记AND的位置处理。上面的写法用where标签包裹MyBatis会自动干掉第一个多余的AND这是最稳妥的方式。另外要注意MySQL的CONCAT(%, #{keyword}, %)比直接写%${keyword}%更安全因为后者存在SQL注入风险——这一点我确实见过有同学踩坑直接用${}拼接导致整个系统被注入攻击教训非常深刻。4. 核心功能模块的代码实现思路4.1 登录认证拦截器 角色权限控制登录模块是整个系统的闸门。我采用的方式是传统Session方案没有引入Spring Security原因很简单这个系统只有三种角色权限模型非常清晰用自带的拦截器处理足够引入Spring Security反而会让学习成本增加、配置文件变复杂。登录Controller的核心逻辑PostMapping(/login) public Result login(String username, String password, HttpSession session) { User user userService.login(username, password); if (user null) { return Result.error(用户名或密码错误); } session.setAttribute(loginUser, user); return Result.success(user); }拦截器负责两件事检查是否登录、路由是否匹配当前角色。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { User user (User) request.getSession().getAttribute(loginUser); if (user null) { response.sendRedirect(/login); return false; } // 按角色控制访问路径 String uri request.getRequestURI(); if (uri.startsWith(/teacher) user.getRole() ! 2) { response.sendRedirect(/login); return false; } if (uri.startsWith(/student) user.getRole() ! 3) { response.sendRedirect(/login); return false; } return true; } }注册到WebConfigConfiguration public class WebMvcConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /register, /css/**, /js/**); } }有几个坑我在这里提醒一下。第一静态资源路径一定要做排除excludePathPatterns否则登录页面的CSS和JS全部被拦截页面会裸奔。第二角色判断不能只靠前端隐藏按钮必须在拦截器里做二次校验防止学生直接访问教师端的URL拿到所有作业数据。第三Session超时后用户会突然跳到登录页为了提升体验可以在前端用全局AJAX拦截302响应并提示登录已过期。4.2 教师端布置作业与附件上传教师布置作业时除了表单字段标题、要求、课程、截止时间最麻烦的是附件上传。SpringBoot处理单文件上传非常简单但有几个配置容易漏spring: servlet: multipart: max-file-size: 20MB max-request-size: 50MB如果漏掉这两个配置默认只有1MB上传稍大一点的作业附件就会直接报MaxUploadSizeExceededException。文件存储路径建议不要写在业务代码里把它做成配置项upload: path: /data/homework-upload/Controller中的上传逻辑PostMapping(/teacher/homework/publish) public Result publish(Homework homework, RequestParam(value file, required false) MultipartFile file) { if (file ! null !file.isEmpty()) { String originalName file.getOriginalFilename(); // 重新生成文件名避免中文乱码、重名覆盖 String newName System.currentTimeMillis() _ UUID.randomUUID().toString().substring(0, 8) _ originalName; File dest new File(uploadPath newName); file.transferTo(dest); homework.setAttachmentPath(newName); } homeworkService.publishHomework(homework); return Result.success(); }这里有一个非常容易踩坑的细节文件重命名时一定不要直接用原文件名。中文文件名在传输和存储时经常出现编码问题而且两个学生可能上传了同名文件直接覆盖就麻烦了。我习惯用时间戳UUID片段原文件名的格式既保证唯一性也保留了扩展名和部分可读性。4.3 学生端查看待办、提交作业、查看成绩学生登录后的首页就是一个 待办作业列表查询逻辑是从user_course查出学生选的课程 → 从homework查出这些课程下的作业 → 关联homework_submit显示当前状态。我建议直接用一条联表查询搞定不要做多次查询再在Java代码里组装N1问题在数据量小的时候不明显但查询次数多了页面延迟会很严重。学生提交作业时Controller要处理两件事保存提交内容文本和文件→ 更新submit记录的状态和提交时间。核心SQLupdate idsubmitHomework UPDATE homework_submit SET submit_content #{submitContent}, file_path #{filePath}, submit_time NOW(), status 1 WHERE homework_id #{homeworkId} AND student_id #{studentId} /update值得注意的是状态从0变为1之后还要在前端逻辑上禁止重复提交。虽然数据库里可以强行UPDATE覆盖但用户不小心点两次提交内容会被第二次覆盖。我的做法是提交前先查一次status如果已经不是0未提交状态直接提示作业已提交如需修改请联系教师。这个判断看起来多余实际上能避免大量乌龙数据。4.4 教师端评分与成绩统计评分页是教师最常用的页面我把它设计成按作业查看所有学生的提交记录每条记录如下信息学生姓名、学号、提交时间、提交内容、附件、当前状态以及评分输入框。评分时更新submit记录的score和comment同时status从1变为2update idscoreHomework UPDATE homework_submit SET score #{score}, comment #{comment}, status 2 WHERE id #{id} /update成绩统计方面我做了两个维度单次作业的统计应交人数、已交人数、未交人数、平均分、最高分、最低分、及格率以及一个课程总览把一门课所有作业的平均分求和取平均或者按权重计算总评。统计SQL用GROUP BY和聚合函数就能完成不需要额外引入报表组件。比如SELECT COUNT(*) AS submitCount, AVG(score) AS avgScore, MAX(score) AS maxScore, MIN(score) AS minScore FROM homework_submit WHERE homework_id #{homeworkId} AND status 2我在实际经验中有一个感受评分页面里最有价值的功能是只看未交的过滤按钮。老师点一下就能看到哪些学生还没交然后一键复制名单发到班级群催促这比逐个人头核对省力得多。这种小功能虽然技术含量不高但它是老师真正会高频使用的功能设计这类系统时要多和实际用户聊他们会告诉你真正的痛点在哪里。5. 从本地开发到部署上线那些容易被忽视的细节5.1 初始化项目与版本选择的坑这个项目我推荐用IDEA直接初始化File New Project Spring Initializr。但版本选择上有个常见的坑我身边已经不止一个人踩过SpringBoot版本选得太新导致和本地JDK版本不匹配或者和MyBatis Starter不兼容。以我的经验做一个SSM整合项目SpringBoot 2.7.x 是当前最稳妥的版本。原因有几点它基于JDK 8兼容性最好官方维护期覆盖时间长网上教程和解决方案最多。不要盲目追新去选SpringBoot 3.x因为SpringBoot 3要求JDK 17及以上而且部分相对旧版的MyBatis和第三方库可能没有完成适配排查问题会非常痛苦。pom.xml中核心依赖如下dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId version1.2.20/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-thymeleaf/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies关于Lombok再多说一句。它确实能减少大量的getter/setter代码但如果你的团队里有新手一定要让他们搞明白Data实际上生成了什么否则一旦某个字段需要自定义逻辑比如密码加密存储被Lombok覆盖的默认getter会造成隐蔽的Bug。建议实体类适度使用Lombok业务类不要用。5.2 application.yml中的关键配置server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/homework_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword type: com.alibaba.druid.pool.DruidDataSource thymeleaf: cache: false mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.homework.entity configuration: map-underscore-to-camel-case: true这里有两个非常关键的点。第一数据库连接URL必须带上characterEncodingutf8和serverTimezoneAsia/Shanghai。不带编码参数插入中文会变成乱码不带时区参数NOW()和Java时间可能出现8小时的时差尤其在服务器是UTC时区的情况下作业截止时间会莫名其妙早8个小时或晚8个小时这个错误在测试阶段非常隐蔽。第二map-underscore-to-camel-case: true一定要开启。数据库字段如create_time开启后MyBatis会自动映射到实体类的createTime不用手动写大量的resultMap。如果不开你会发现查询结果里所有带下划线的字段都是null排查这个问题浪费时间。5.3 打包部署流程IDEA中双击即可运行SpringBoot最爽的地方就是部署不依赖外部Tomcat。打包只要三步在IDEA右侧Maven面板中执行clean清理之前的编译产物。执行package生成JAR包路径在target/homework-0.0.1-SNAPSHOT.jar。把JAR包上传到服务器执行java -jar homework-0.0.1-SNAPSHOT.jar。如果服务器内存紧张可以加点JVM参数控制内存java -Xms128m -Xmx256m -jar homework-0.0.1-SNAPSHOT.jar这组参数把JVM初始堆内存设为128MB最大堆内存设为256MB对作业管理系统这个规模的应用完全够用。如果什么都不设置JVM默认会占有服务器的大量物理内存小内存VPS上很容易触发OOM Killer。如果你需要对已经打好的JAR进行反编译排查问题解决思路在这里也顺带提一下JAR包本质就是一个ZIP压缩包反编译的关键是找到里面的.class文件然后用反编译工具比如IDEA自带的Java Decompiler或者CFR还原出Java源码。具体来说先用jar tf homework.jar列出包结构或者直接改扩展名为zip解压再把target目录下的class文件拖入IDEA它会自动反编译并展示源码。这个技能在排查为什么生产环境和本地环境表现不一样的时候非常有用。5.4 我实际开发中踩过的坑和对应的解决办法这里把几个让我印象最深的坑集中列出来都是真实项目里会出现的问题。坑1MyBatis查询结果为null但不报错这种情况最常见的原因有三个一是实体类字段名和数据库列名对不上且没开启驼峰映射二是XML中select的返回类型写错三是type-aliases-package配置了但实体类不在对应包下。排查思路是先在Navicat等工具里执行SQL确认是否有数据然后在Mapper接口方法上打上断点检查参数和返回值把范围一步步缩小。坑2文件上传成功但找不到文件我遇到过两次。一次是配置了绝对路径但没建目录File.transferTo()会报FileNotFoundException另一次是开发时用的相对路径部署后工作目录变了导致文件写到了完全不同的位置。解决办法是统一用绝对路径且在应用启动时自动创建目录Component public class UploadPathInitializer implements ApplicationRunner { Value(${upload.path}) private String uploadPath; Override public void run(ApplicationArguments args) { File dir new File(uploadPath); if (!dir.exists()) { dir.mkdirs(); } } }坑3数据库中的截止时间比用户设置的晚8小时前面提到过这是时区问题。记住一个原则MySQL的DATETIME类型不带时区信息但JDBC连接会带上时区。如果你在application.yml中加了serverTimezoneAsia/Shanghai同时数据库连接也指向上海时区那么时间就会正常显示。如果还出错检查服务器操作系统时区timedatectl或date命令确保是CST中国标准时间。坑4Thymeleaf模板中的日期格式化在页面上显示截止时间时直接输出LocalDateTime会是一长串带T的ISO格式非常难看。我的处理方式是在Controller传数据时就把日期格式化为字符串DateTimeFormatter formatter DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm); homework.setDeadlineStr(homework.getDeadline().format(formatter));或者在模板里用#temporals工具类span th:text${#temporals.format(homework.deadline, yyyy-MM-dd HH:mm)}/span两种方式都可以我推荐第一种因为把格式化放在后端有利于单元测试。坑5学生列表翻页时出现的空指针当时我的PageHelper分页插件和MyBatis的版本没有对齐导致分页查询时page.getList()返回了null而不是空列表。排查后发现是PageHelper版本太老在MySQL 8的驱动下部分参数解析出错。解决办法升级到与SpringBoot 2.7匹配的pagehelper-spring-boot-starter1.4.7版本并且在application.yml中开启helper-dialect: mysql配置。6. 成绩统计从单次作业到期末总评6.1 单次作业的统计报表实现每次作业批改完成后教师端应该能看到一个完整的统计面板我实现的数据是这样的统计项计算方法应交人数选了该课程并属于该作业状态记录的学生数已交人数status 1 或 status 2 的记录数未交人数status 0 或 status 3 的记录数平均分已评分记录status2的AVG(score)及格率score 60 的人数 / 已评分总人数分数分布按90-100、80-89、70-79、60-69、60以下分组统计分数分布这个功能老师非常喜欢它直接呈现了作业难度的分布情况。实现时我用了一条带GROUP BY的分段SQLSELECT CASE WHEN score 90 THEN 90-100 WHEN score 80 THEN 80-89 WHEN score 70 THEN 70-79 WHEN score 60 THEN 60-69 ELSE 60以下 END AS scoreRange, COUNT(*) AS cnt FROM homework_submit WHERE homework_id #{homeworkId} AND status 2 GROUP BY scoreRange ORDER BY scoreRange DESC6.2 期末总评的权重计算到了期末老师通常需要把平时作业成绩按一定权重加权汇总。我在系统里加了一个简单的配置每门课程可以设置作业的权重比如9次作业各占10%最后一次占20%然后按如下规则计算总评统计每个学生在这门课下所有作业的得分。如果某次作业未交status3该次成绩记0分不跳过。按权重计算加权平均分。生成course_grade表记录最终总评。这一步的SQL写法核心是先按学生分组再把每份作业的分数乘上对应的权重然后求和。由于作业权重存储在homework表中需要JOIN两张表再聚合。这种实现虽然不算复杂但非常实用——老师可以一键导出整个班的总评成绩。不过要注意的是总评成绩计算出结果后应要求老师锁定结果避免因为权重修改导致过往成绩反复变动。最终的导出功能我也实现了用POI生成Excel文件支持整班导出和按作业导出。7. 关于扩展性的一些个人建议最后聊一聊这个系统后续可能的扩展方向以及我个人的体会。从作业管理这个核心出发最常见的扩展方向有三个。第一是增加在线答题功能让客观题自动判分教师只需要批改主观题和代码类作业这会极大提升批改效率。第二是引入消息通知机制作业发布、截止时间提醒、成绩公布都通过站内信或邮件自动通知学生减少教师维护群消息的成本。第三是数据可视化把成绩分布、作业提交率、学生进步曲线用图表展示出来让教师对班级学习状况的认知更直观。我自己的实际操作体会是任何一个管理类系统最忌讳的就是一开始就想做一个全功能平台。作业布置、提交、批改、统计这四个核心闭环能稳定跑起来系统就已经具备实际使用价值了。剩下的功能都应该以教师和学生真的会愿意用为前提做增量。我见过很多同类项目加了在线讨论、加了大班直播、加了排行榜最后因为交互复杂老师宁可用回微信群。所以做完核心功能后更应该去问真实用户你最想从系统里省下什么时间然后针对性优化那一个点。如果你打算拿这个项目做毕业设计我的建议是先精读一遍本文的数据库设计和状态流转部分把这两块吃透答辩的时候讲清楚为什么这样设计比背几段代码效果要好得多。如果你打算把它落地成实际工具优先处理文件存储路径的规划和权限校验这两个环节在生产环境中最容易出问题。
返回列表