ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于SSM与HBuilder的Android高校在线学习APP设计与实现

2026/9/18 3:03:27 拓冰建站 浏览量
基于SSM与HBuilder的Android高校在线学习APP设计与实现 简介基于Android的高校在线学习APP设计与实现毕业设计文档内容针对当前高校在线学习中教学资源共享、自主学习与创新教学模式等需求给出了从可行性分析、需求分析到系统总体设计、详细设计的完整方案。文档为doc格式共1个文件压缩包整体约1.69MB适合计算机相关专业学生、移动开发入门者以及准备毕业设计的同学参考。文中不仅涵盖技术可行性、经济可行性、操作可行性和法律可行性分析还包含UML用例分析、数据库E-R图与功能架构图并对考试功能、教学课件功能、权限系统、班级管理和学生管理等前后台模块进行了实现说明。文档源自广东东软学院本科生毕业设计论文结构完整附有中英文摘要、目录及分章节论述便于读者对照撰写自己的毕设文档。目前已有119人学习下载。读者可依据该文档快速了解高校在线学习App的设计思路、数据库结构及核心业务逻辑也可将其作为毕业设计撰写的结构模板或项目开发的需求蓝图。1. 选题背景与技术定位这套高校在线学习APP到底解决什么问题在2020年线上教学集中爆发之后高校里的在线学习系统依然是很多学校的一块短板。市面上的慕课网、超星等平台功能虽然全面但课程结构、试题库和班级组织形态都是平台通用的真正落到某个学校的教学安排上教师上传课件、组织随堂考试、查看学生成绩仍然要绕一大圈。于是很多毕业生和教研室开始选择自己动手做一套基于Android的高校在线学习APP把学生信息、班级、课件、视频、考试、师生互动塞进一个完整闭环里。这套方案的核心思路是用SSM搭建RESTful后端用MySQL存储业务数据前端采用HBuilder的H5混合方案打包成Android安装包既避开了纯原生开发的工程量又能调用手机本地的文件下载、相册、通知等能力。对于看这篇内容的人来说无论你是要做毕业设计还是给学校信息化中心做预研这套架构都有可参考的落地路径而且踩坑经验基本可以复用。2. SSM后端与数据库设计从E-R图到可运行的接口2.1 为什么是SSM而不是Spring Boot原项目文档中明确写了后端采用SSM框架即Spring SpringMVC Mybatis。如果今天重新选型Spring Boot显然更省事但SSM的价值在于把Spring的IOC容器、SpringMVC的请求分发、Mybatis的SQL映射拆开看能更清楚一次请求经过哪些层。这对刚接手在线学习类系统的开发者也更友好因为项目里的权限拦截、事务控制、SQL调优都能在显式配置中找到位置。这套系统需要处理三类角色管理员、教师、学生每类角色有不同的接口访问范围SSM的拦截器机制加上Spring的切面配置能很自然地实现资源隔离。2.2 核心表结构学生、班级、考试、课件根据系统设计逻辑数据库有管理员信息表、考试详情表、学生管理表、班级管理表再加上课件资料表和视频管理表。下面把学生表、班级表、考试表和三张关联表的核心字段整理成DDL这套结构在MySQL 5.7和8.0下都能直接跑。CREATE DATABASE IF NOT EXISTS campus_learning DEFAULT CHARSET utf8mb4; USE campus_learning; CREATE TABLE t_student ( id INT NOT NULL AUTO_INCREMENT COMMENT 学生序号主键, student_no VARCHAR(20) NOT NULL COMMENT 学号, student_name VARCHAR(20) NOT NULL COMMENT 姓名, student_age INT DEFAULT 0 COMMENT 年龄, student_id_card VARCHAR(18) DEFAULT NULL COMMENT 身份证号, student_tel VARCHAR(11) DEFAULT NULL COMMENT 手机号, student_sex VARCHAR(5) DEFAULT 男 COMMENT 性别, student_address VARCHAR(100) DEFAULT NULL COMMENT 地址, student_state VARCHAR(10) DEFAULT 在读 COMMENT 状态在读/休学/毕业, password VARCHAR(64) NOT NULL DEFAULT 123456 COMMENT 登录密码, PRIMARY KEY (id), UNIQUE KEY uk_student_no (student_no) ) ENGINEInnoDB COMMENT学生管理表; CREATE TABLE t_class ( id INT NOT NULL AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL COMMENT 班级名称, teacher_name VARCHAR(20) DEFAULT NULL COMMENT 班主任用户名, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, remarks VARCHAR(255) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_class_name (class_name) ) ENGINEInnoDB COMMENT班级管理表; CREATE TABLE t_exam ( id INT NOT NULL AUTO_INCREMENT, exam_name VARCHAR(50) NOT NULL COMMENT 试卷名, exam_content TEXT COMMENT 题目JSON按题序存题目、选项、答案, course_id INT DEFAULT NULL COMMENT 所属课件id, total_score INT DEFAULT 100, primary key (id) ) ENGINEInnoDB COMMENT考试详情表;这里把考试题目的内容用TEXT字段以JSON格式存储是为了降低多表联查压力。如果题目是固定题量这个设计很直接但后续如果要统计每道题的得分率需要拆成exam_question和option两张表。文档里的E-R图其实就是围绕学生、班级、试卷、管理员四类实体做的实际查询中最常见的组合是「班级-学生」和「试卷-课程」这两条链路用外键或逻辑外键都能跑推荐逻辑外键避免数据迁移时被外键约束卡住。2.3 MyBatis映射与多表查询学生端查询课程列表时需要同时拿到课件名称、教师名字和视频地址。这个场景下Mybatis的关联查询值得注意。先定义一个VO类public class CourseVO { private Integer courseId; private String courseName; private String teacherName; private String videoUrl; private String pdfUrl; private Integer studentCount; // 省略getter/setter }对应的Mapper XML片段如下select idselectCourseWithTeacher resultTypecom.campus.vo.CourseVO SELECT c.id AS courseId, c.course_name AS courseName, t.teacher_name AS teacherName, c.video_url AS videoUrl, c.pdf_url AS pdfUrl, COUNT(s.id) AS studentCount FROM t_course c LEFT JOIN t_teacher t ON c.teacher_id t.id LEFT JOIN t_class cl ON c.class_id cl.id LEFT JOIN t_student s ON s.class_id cl.id WHERE c.status 1 GROUP BY c.id, t.teacher_name, c.course_name, c.video_url, c.pdf_url /select逻辑说明这个查询的核心是把课程、教师、班级、学生四张表串起来。使用LEFT JOIN是为了防止教师或班级被删除后课程查不出来COUNT配合GROUP BY统计每个课程覆盖的学生数用于前端展示课程热度。如果数据量超过十万建议把统计动作改成冗余字段在班级调整时用定时任务更新course_student_count否则这个SQL会在学习成绩列表页产生较高的IO开销。这里要特别提醒Mybatis的resultType映射依赖列别名字段名必须与实体属性严格对应。实际开发时我习惯在XML里写完整的别名避免开启驼峰映射后出现意外错误。在配置mybatis-config.xml时也可以统一配置mapUnderscoreToCamelCasetrue这样数据库的course_name能自动映射到courseName但上面SQL里显式别名的方式可读性更高。2.4 Controller层与接口格式后端接口统一返回JSON结构前端H5才能无差别接管。定义一个基础响应类RequestMapping(/student) RestController public class StudentController { Autowired private StudentService studentService; PostMapping(/login) public Result login(RequestBody LoginVo loginVo) { Student student studentService.login(loginVo.getUsername(), loginVo.getPassword()); if (student null) { return Result.error(账号或密码错误); } return Result.ok().put(token, JwtUtil.createToken(student.getId(), student)); } GetMapping(/courses) public Result listCourses(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer limit) { PageResultCourseVO result studentService.pageCourses(page, limit); return Result.ok().put(data, result); } }这里使用JWT作为登录态方案而没有沿用传统的session。原因是H5混合打包后前端页面运行在WebView里如果使用原生session云端打包和本地调试会出现Cookie存取不一致的问题。而JWT只需要在请求头里带一个Authorization字段后端通过拦截器统一解析跨端复用非常稳定。权限说明见后文第4章。3. Android端HBuilder混合开发课件、视频与在线考试3.1 HBuilder H5混合开发的打包链路原文档中提到的HBuilder是DCloud出品的IDE它支持写HTML、CSS、JavaScript通过内置的5 runtime调用Android底层能力最后云打包生成APK。整个链路是本地页面资源通过HBuilderX编译生成www目录下的静态文件云打包时将这些文件打进APK。运行时Android WebView加载这些本地文件同时通过plus.bridge与原生层通信。关键点在于H5页面不能直接访问局域网内的接口需要在manifest.json里配置网络权限和域名白名单。{ permissions: { Android: { network: true, internet: true } }, plus: { distribute: { android: { permissions: [ uses-permission android:name\android.permission.INTERNET\/, uses-permission android:name\android.permission.ACCESS_NETWORK_STATE\/ ] } } } }参数说明network和internet权限决定App能否访问网络ACCESS_NETWORK_STATE用于判断网络类型这两种权限是基础。如果涉及文件下载还需要加WRITE_EXTERNAL_STORAGE。注意Android 6.0以上动态权限但HBuilder云打包默认会在启动时申请存储权限不需要自己写运行时请求代码。3.2 学生端功能界面与交互首页需要展示轮播图、通知公告和我的课程入口。H5页面使用Vue或原生JS都行这里以原生JS为例写一个动态渲染function loadCourses() { var token plus.storage.getItem(token); fetch(BASE_URL /student/courses, { method: GET, headers: { Content-Type: application/json, Authorization: Bearer token } }) .then(function(resp) { return resp.json(); }) .then(function(data) { if (data.code 0) { var list data.data.list; var container document.getElementById(courseList); var html ; for (var i 0; i list.length; i) { var item list[i]; html div classcourse-card onclickopenCourse( item.courseId ) img src item.coverUrl / div classcourse-title item.courseName /div div classteacher item.teacherName /div /div; } container.innerHTML html; } }) .catch(function(err) { plus.nativeUI.toast(网络异常); }); }逻辑说明plus.storage.getItem是H5提供的本地存储API可以把JWT字符串持久化在App的私有存储空间。每次请求都从存储中取出token放到请求头。openCourse跳转详情页时通过plus.webview.open打开另一个HTML将courseId作为参数拼接在URL里。这种页面间的参数传递在混合开发中很常见比存储全局变量更可靠因为WebView重新加载后全局变量会丢失。3.3 考试模块答题与计时逻辑在线考试是系统的核心模块。学生进入考试页面后需要从后端拉取试题这里采用一次拉取全部试题在本地循环渲染的方式。答题过程中将每道题的答案暂存到对象里提交时再统一发送。var examData []; var answerMap {}; function startExam(examId) { fetch(BASE_URL /exam/ examId, { headers: authHeader() }) .then(resp resp.json()) .then(res { examData res.data.questions; // [{id, type, title, options, correct}] answerMap {}; renderQuestions(examData); var remainSeconds res.data.duration * 60; timer setInterval(() { remainSeconds--; if (remainSeconds 0) { submitExam(); } document.getElementById(timer).innerText formatTime(remainSeconds); }, 1000); }); } function submitExam() { clearInterval(timer); var payload { examId: examId, answers: answerMap, usedTime: totalSeconds - remainSeconds }; fetch(BASE_URL /exam/submit, { method: POST, headers: Object.assign({ Content-Type: application/json }, authHeader()), body: JSON.stringify(payload) }).then(resp resp.json()).then(res { plus.nativeUI.alert(考试完成得分 res.data.score); plus.webview.currentWebview().close(); }); }这里的核心是前端计时的准确性。setInterval在App退到后台时会被挂起所以真正的考试计时必须在后端记录开始时间和结束时间。前端倒计时只是用户体验而后端在submit接口里校验提交时间是否超过规定时限。如果超过成绩仍然有效但会标记为超时提交这个业务逻辑在需求分析阶段容易被忽略。3.4 与后端的数据对接及常见兼容问题混合开发中更容易出问题的往往是接口地址的配对。pc端后端Tomcat监听8080端口Android模拟器访问本机要用10.0.2.2真机调试要用电脑在局域网内的IP地址。我一般把API地址抽成一个config.jsvar BASE_URL http://192.168.1.100:8080/campus;在HBuilder真机运行前先确认手机和电脑在同一WiFi下再ping一下IP通不通。另外后端要允许跨域否则WebView里请求会被拦。最省事的方案是在SpringMVC里加个CORS过滤器public class CorsFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { HttpServletResponse response (HttpServletResponse) res; response.setHeader(Access-Control-Allow-Origin, *); response.setHeader(Access-Control-Allow-Methods, GET, POST, PUT, DELETE, OPTIONS); response.setHeader(Access-Control-Allow-Headers, Content-Type, Authorization); chain.doFilter(req, res); } }4. 三端权限体系与业务流程管理员、教师、学生的权限边界4.1 UML用例到接口权限的映射系统有三类端侧后台管理端PC版或H5版、教师端、学生Android端。根据UML用例管理员负责教师信息、学生信息、班级和登录密码维护教师负责学生信息登记、课件管理、视频管理、试题管理学生只能查看课程资料、在线考试、留言。下面的表格将这三种角色的功能点映射到具体的接口资源和操作方法是后续做权限表设计的基础。角色用例功能后端资源路径允许操作管理员学生信息管理/admin/studentGET, POST, PUT, DELETE管理员班级管理/admin/classGET, POST, PUT, DELETE管理员修改密码/admin/passwordPUT教师课件管理/teacher/courseGET, POST, PUT, DELETE教师试题管理/teacher/examGET, POST, PUT, DELETE教师学生信息登记/teacher/studentGET, POST学生课件在线学习/student/courseGET学生在线考试/student/examGET, POST学生师生交流/student/messageGET, POST4.2 后端拦截器实现角色认证SSM中通过最纯净的HandlerInterceptor实现接口鉴权比在Controller里手动判断要清晰。定义三个角色标识admin、teacher、student前端在登录后拿到JWT里面包含角色和用户ID。后端拦截器先验证JWT签名再从claims中读取角色做匹配。public class AuthInterceptor extends HandlerInterceptorAdapter { private static final SetString STUDENT_PATHS Collections.unmodifiableSet(new HashSet(Arrays.asList(/student/))); private static final SetString TEACHER_PATHS Collections.unmodifiableSet(new HashSet(Arrays.asList(/teacher/))); private static final SetString ADMIN_PATHS Collections.unmodifiableSet(new HashSet(Arrays.asList(/admin/))); Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equals(request.getMethod())) { return true; } String uri request.getRequestURI(); String role getRoleFromToken(request.getHeader(Authorization)); if (uri.startsWith(/admin/) !admin.equals(role)) { response.setStatus(403); return false; } if (uri.startsWith(/teacher/) !teacher.equals(role) !admin.equals(role)) { response.setStatus(403); return false; } if (uri.startsWith(/student/) !student.equals(role) !teacher.equals(role) !admin.equals(role)) { response.setStatus(401); return false; } return true; } }注意这里有一个隐含规则管理员虽然是后台角色但业务上可能也需要查看学生端的学习数据所以将管理员放行为teacher和student接口的访问。教师可以查看学生列表但不能访问/admin下的资源。这种设计在文档的业务流程中没有单独说明但实际做权限的时候最好给管理端接口和业务端接口各留一条通道避免后续扩展时出现管理员无法模拟学生视角的问题。4.3 教师端课件与试题管理的状态流转教师端管理课件时上传视频和PDF是两套流程。PDF走文件上传视频走流媒体转存。由于原系统没有引入云存储文件都存储在Tomcat服务器目录下。教师上传课件的操作包括填写课程名、选择文件、指定班级。上传成功后记录在t_course表status字段为0表示未发布学生端看不到教师点击发布后状态变为1学生端立刻可见。在线考试的状态更复杂一些试题在编辑期间是草稿教师添加完题目后可以设置考试时长和总分点击发布后生成一次考试批次。学生提交后系统自动判分并回写成绩。这个状态流在代码里用一个整型字段管理public static final int EXAM_DRAFT 0; // 编辑中 public static final int EXAM_PUBLISHED 1; // 已发布 public static final int EXAM_FINISHED 2; // 已截止如果某个学生考试中途退出系统应当允许重新进入但要保留已经作答的题目。这里可以利用WebView缓存或者在后端用答题中间表存储历史答案。通常选择后者因为手机杀进程后WebView缓存会丢。5. 系统测试、打包验证与混合开发避坑5.1 测试用例覆盖在线学习系统的测试重点不在常规CRUD而在并发提交考试、文件下载路径、权限绕过三个方面。下面给出我在验收这套系统时使用的测试用例表可以直接参考修改。编号模块测试场景预期结果关键点TC01登录输入错误密码5次第5次后锁定10分钟防止爆破TC02权限学生端访问/admin/student返回403拦截器生效TC03考试考试倒计时归零时提交自动提交成绩有效时间策略TC04考试同一账号在另一设备再次登录前一个token失效会话管理TC05课件下载100MB视频时锁屏恢复后继续下载断点续传TC06混合端Android 9以下系统打开文件管理器能选中pdfFileProvider权限TC02这种权限用例最容易被忽视很多开发者只测了UI层面的隐藏按钮没有直接请求受保护接口。App抓包工具可以直接绕过前端按钮打开浏览器发起请求所以后端接口权限必须用拦截器强制校验。5.2 HBuilder云打包与Android Studio联调HBuilder云打包不需要本机装完整的Android SDK但联调仍然建议安装一个Android Studio作用是查看崩溃日志和抓取WebView控制台。常见做法是把App包通过adb安装到手机adb install -r campus_online.apk # 查看日志过滤WebView报错 adb logcat -s WebView chromium在HBuilderX的「运行到手机或模拟器」模式下可以直接浏览器的开发者工具风格调试H5页面。云打包的APK需要配置App图标、名称、包名包名默认是com.html5plus.campus如果要上架应用市场需要改成公司域名反写。签名时如果使用测试证书部分安卓设备会提示风险这里只要保证功能演示可正常运行即可。5.3 三个容易踩的坑CORS、时区、FileProvider后端CORS过滤器如果缺失混合App在部分Android版本上请求会直接失败。具体表现是电脑浏览器访问接口正常手机App请求无返回。排查时先看请求是否发送再看响应头有没有Access-Control-Allow-Origin没有就按第3章的过滤器方案补上。考试时间显示出的时区问题不容易发现。MySQL默认的CURRENT_TIMESTAMP是服务器时区如果服务器部署在UTC学生端显示的开始时间就会差8个小时。解决方式是在JDBC连接串上强制设置时区jdbc:mysql://localhost:3306/campus_learning?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8最后是Android 7.0以上的FileProvider问题。混合App中调用系统安装器或文件选择器时如果使用file://协议会直接崩溃HBuilder已经内置了FileProvider但自己在原生插件中调用时会遇到FileUriExposedException。解决办法是在AndroidManifest.xml中声明provider路径指向external_files这个配置在HBuilder的manifest.json里写FileProvider: { authorities: ${applicationId}.fileprovider, exported: false, grantUriPermissions: true }做完这些验证这套基于SSM和HBuilder混合架构的学习APP基本能稳定运行在学生手机上了。本文还有配套的精品资源点击获取