news 2026/9/26 0:10:40

基于Android和Spring Boot的自闭症康复训练全流程管理系统设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Android和Spring Boot的自闭症康复训练全流程管理系统设计

1. 选题拆解:一个毕业生题目背后的“三层需求”

每年的毕业设计选题季,“基于 Android 的 XX 管理系统”这类题目都会以不同面貌出现。今年轮到你手里的,是“基于 Android 的自闭症康复训练 APP + Spring Boot 框架 + 全流程管理系统”——标题很长,关键词很多,乍看起来就是个常规的增删改查项目。但真做起来你会发现,这个题目的难点不在 CRUD,而在“全流程”三个字。自闭症康复训练不是用户打开 APP 做几个题那么简单,它牵扯到儿童建档、能力评估、训练计划制定、日常执行、数据记录、效果反馈、计划调整,是一条完整业务链路。把这条链路用软件系统支撑起来,才是这个毕设的真正价值。

1.1 业务层:康复训练“全流程”到底指什么

我先把这个项目的核心业务拆给你看。自闭症康复训练比较主流的流程大概是这样的:

  • 儿童建档:记录孩子的基本信息、诊断情况、初始能力水平;
  • 能力评估:康复师对孩子在认知、语言、社交、精细动作等维度做评估,形成评估报告;
  • 制定训练计划:根据评估结果,为每个孩子生成阶段性训练计划,计划里有具体的训练任务;
  • 执行训练任务:孩子在家里或机构,通过 APP 完成配对、分类、点选、语音跟读等任务,或者在线下完成训练后由家长/康复师记录结果;
  • 数据留痕:每次训练的时间、完成度、正确率、情绪表现都要记录下来;
  • 评估反馈:周期结束后再次评估,对比前后数据,判断训练是否有效,再调整下一步计划。

这个闭环听起来不复杂,但落到系统上就有很多文章可做。“康复训练服务平台”解决的核心问题,就是把过去靠纸质表格和口头沟通完成的流程,变成数据驱动、多方协同的信息流。你把这个闭环讲清楚,毕设的业务层面就立住了。

这也是为什么我建议你做系统之前先画业务流程图,别急着写代码。哪怕只是在一张白纸上把“谁在什么时间对谁做了什么操作、产生了什么数据”画出来,后面建表、写接口、设计界面都会顺很多。很多同学做这个题目做到一半发现要改表结构、改接口,根因就是没在第一步理清业务闭环。

1.2 用户层:谁在用这个系统,他们分别在乎什么

这个 APP 表面上是给“使用者”用的,但“使用者”其实分三类角色,而且三类角色的诉求差异非常大:

  • 康复师(治疗师):他们要给孩子做评估、制定计划、查看训练结果,最关心的是“数据准不准、计划好不好下发、能不能快速看到孩子的进步曲线”;
  • 家长:在家里带孩子做训练,最关心的是“任务怎么开始、孩子完成得怎么样、今天练了什么、下次该练什么”,而且家长不一定懂专业术语,界面必须足够简单;
  • 系统管理员:负责账号管理、基础数据维护、数据统计,关心的是系统稳不稳定、角色权限分得清不清楚。

很多同学做管理系统只有一个“用户”概念,所有功能堆在一起,这样答辩时很容易被问倒。我的建议是:从一开始就按三种角色设计权限模型和界面,哪怕实际只做了两种角色的完整流程,也要把角色边界想清楚。这不仅是业务需要,也是毕业设计评分表里“系统设计合理性”这一项的得分点。

角色权限在 Spring Boot 端用一张角色字段加接口拦截就能解决,不必上太重的权限框架。Android 端则根据登录后返回的角色字段,动态决定底部导航栏显示哪些页面。这样实现成本低,演示效果却非常直观。

1.3 技术层:标题里的技术栈如何分工

再来看题目里这串技术名词:Java、Android、Spring Boot、APP、框架。其实分工很清晰:

  • Android 端(Java 编写):面向家长和孩子,承担训练任务呈现、交互操作、数据采集展示,是“前台”;
  • Spring Boot 端(Java 编写):提供 RESTful API,负责任务管理、数据存储、权限校验、报表统计,是“后台”;
  • MySQL 数据库:存放用户、儿童档案、训练计划、训练记录、评估报告等数据;
  • 两者通过 HTTP + JSON 通信。

这个组合是 Java 技术栈毕设里最稳的组合之一。原因在于:Android 原生开发 + Spring Boot 都是 Java 生态,你只需要掌握一门语言,就能同时驾驭客户端和服务端,学习曲线比“Android + Node.js”或“iOS + Spring Boot”平滑得多。对于毕业设计来说,技术栈的一致性直接决定你开发效率的上限。

接下来的章节,我按照实际开发顺序,分别讲服务端设计、Android 端实现、全流程管理闭环和联调踩坑,最后说答辩准备。每一部分都会给出可以直接抄作业的表结构、接口定义和实现思路。

2. 技术选型与架构决策:为什么这个组合能撑起整个毕设

选技术栈这件事,很多同学是“题目里写了什么就用什么”,但如果你能说出“为什么这么选”,毕业设计答辩时的底气会完全不一样。这个项目我着重考虑了几个点。

2.1 客户端为什么用 Android 原生,而不是跨平台方案

现在 uni-app、Flutter、React Native 确实很火,但毕业设计我依然推荐 Android 原生,理由很务实:

第一,跑通一个原生项目的环境成本最低。Android Studio 装好、SDK 配好就能跑,网上资料一搜一大把,遇到问题排查路径也清晰。跨平台框架虽然一套代码多端复用,但环境配置、依赖版本、原生插件这些问题对新手来说反而更容易卡住。

第二,自闭症康复训练的场景非常依赖触屏交互和系统能力。比如配对训练需要拖动卡片、点击反馈要有震动和音效、语音跟读需要调用系统 TTS(TextToSpeech)、精细动作训练可能要检测触摸轨迹,这些用原生控件和系统 API 实现最直接。跨平台框架遇到这种高频交互场景,往往还要写原生插件,反而绕远路。

第三,这个 APP 的核心使用者是家长和孩子,Android 设备在家庭中的普及率也足够高,选题本身就是 Android 方向,用原生实现最契合需求。

你不用担心“原生开发没亮点”,只要把训练交互做出质感——比如卡片匹配成功时的放大动画、连续完成时的星星奖励、情绪状态选择的大表情按钮——这些体验上的细节就是答辩时的加分项。做原生反而更容易做出细节。

2.2 Spring Boot 在项目中扮演什么角色

Spring Boot 在这个项目里承担的是典型的服务端职责,但有几个点值得展开。

首先是接口服务,这是最基础的。Android 端所有数据的读写都通过 RESTful API 完成,我把接口按业务模块划分:认证模块、儿童档案模块、训练计划模块、训练记录模块、评估报告模块。每个模块一组接口,路径清晰,联调时前端后端都省心。

其次是权限控制。Spring Boot 拦截器加 JWT(JSON Web Token)就可以实现角色校验,不用引入 Spring Security 全家桶。你只需要在登录成功后把用户 ID 和角色写进 Token,后续请求带着 Token 访问,拦截器里解析出来放行或拒绝即可。这个方案代码量少、逻辑直观、答辩也好解释。

第三是数据聚合与报表。评估报告、训练统计这类功能,需要把多张表的数据按孩子维度聚合。Spring Boot 的 Service 层做业务计算,把结果封装成 DTO 返回给 Android 端,比在客户端做聚合严谨得多。报表数据尽量服务端算好,客户端只负责展示,这也是前后端职责分离的体现。

表驱动设计也是一个被很多毕设忽略的点。什么算“表驱动”?就是数据库表结构是你的系统骨架,先定义好表,再围绕表写代码。后面我会给出完整的核心表设计,这是整个项目最关键的部分,比代码更值得你花时间。

2.3 整体架构与一次训练的完整数据链路

先宏观构建一下系统的整体结构,方便你后面理解模块划分。

系统分为两层。服务端是 Spring Boot 单体应用,连接 MySQL,提供 RESTful API,内置 JWT 权限拦截器;Android 端是原生应用,网络层用 Retrofit 封装,页面按角色动态展示。两端之间只通过接口通信,不共享任何代码和状态。

架构上不需要微服务,不要分布式,单机部署就够毕业设计用了。把这套结构降级理解:一个 Tomcat 端口、一个数据库实例、一个 Android APK,全链路可以在一台电脑上跑通,这对演示和答辩反而是好事。

一次典型的训练执行,数据是怎么流动的?我以“家长带孩子完成一个水果配对任务”为例:

  1. 家长打开 APP,登录后进入今日训练列表;
  2. Android 端调用GET /api/trainings/today?childId=xx,服务端从数据库查出今天该执行的训练任务;
  3. 服务端返回任务内容、训练类型、所需素材、目标数量;
  4. Android 端渲染训练界面,孩子开始操作;
  5. 训练结束后,家长点击完成,Android 端调用POST /api/training/record上报本次训练记录,包括完成状态、正确率、用时、孩子的情绪状态;
  6. 服务端写入训练记录表,同时更新该计划的累计完成数;
  7. 家长在“成长档案”里可以看到最新一次训练记录和本周训练汇总,数据来自GET /api/children/{id}/progress。

这个链路把“执行—记录—反馈”串起来了,而这个闭环的每个节点都需要对应的表和接口支撑。下面进入服务端核心设计。

3. 服务端设计:表结构、接口约定与关键实现

服务端是整项目的底座。很多同学代码写到一半发现“这个数据没地方存”“那个字段查不出来”,绝大多数问题出在表结构设计阶段。我先把核心表给你,这些表已经过实际项目的检验,直接拿来用不会有大坑。

3.1 核心数据模型:六张表撑起全流程管理

先明确一点:不需要设计几十张表,六张核心表加两张辅助表足够覆盖这个题目。

用户表(sys_user)

字段类型说明
idbigint主键,自增
usernamevarchar(50)登录账号
passwordvarchar(100)BCrypt 加密后的密码
rolevarchar(20)角色:ADMIN / THERAPIST / PARENT
real_namevarchar(50)姓名
phonevarchar(20)联系电话
create_timedatetime创建时间

儿童档案表(child_profile)

字段类型说明
idbigint主键
namevarchar(50)儿童姓名
birth_datedate出生日期
gendertinyint性别,1男 2女
diagnosis_infovarchar(500)诊断情况概述
parent_idbigint关联家长用户 ID
avatarvarchar(200)头像地址
create_timedatetime建档时间

一个家长可以绑定多个孩子,一个孩子也可以同时有多个康复师跟进,所以儿童表和用户表是多对多关联的。这里我加了一张中间表child_therapist_rel,字段就三个:id、child_id、therapist_id,不做冗余设计。

训练计划表(training_plan)

字段类型说明
idbigint主键
child_idbigint关联儿童
plan_namevarchar(100)计划名称,例如“8月认知提升计划”
start_datedate开始日期
end_datedate结束日期
statustinyint0未开始 1进行中 2已完成 3已暂停
creator_idbigint创建人(康复师)
create_timedatetime创建时间

训练任务表(training_task)

字段类型说明
idbigint主键
plan_idbigint所属计划
task_typevarchar(20)任务类型:MATCH(配对)/ CLASSIFY(分类)/ SPEECH(语音)/ FINE_MOTOR(精细动作)
titlevarchar(100)任务标题
content_jsontext任务内容,JSON格式,存放素材和答案
difficultytinyint难度,1-3级
target_durationint目标时长,单位分钟
rewardvarchar(100)完成后奖励描述,如“奖励一颗星星”
order_noint同计划内排序
frequencyvarchar(20)执行频率:DAILY / WEEKLY / CUSTOM

content_json 是任务内容的核心字段,不同任务类型存不同结构。比如配对任务存素材图片 URL 列表和正确答案;语音任务存示范音频和评分提示。用 JSON 字段而不是建一堆任务子表,能大幅降低系统复杂度,也方便 Android 端解析。

训练记录表(training_record)

字段类型说明
idbigint主键
task_idbigint关联任务
child_idbigint关联儿童
operator_idbigint操作人(家长或康复师)
completion_statustinyint1完成 2未完成 3超时
accuracydecimal(5,2)正确率百分比
durationint实际用时,单位秒
emotion_leveltinyint训练情绪:1抗拒 2一般 3配合 4积极
remarkvarchar(500)备注
record_timedatetime记录时间

评估记录表(assessment_record)

字段类型说明
idbigint主键
child_idbigint关联儿童
assessor_idbigint评估人(康复师)
assessment_typevarchar(20)评估类型:INITIAL(初始)/ PERIODIC(阶段)
dimension_scorestext各维度得分 JSON,如语言、认知、社交、精细动作
summaryvarchar(500)综合评估结论
suggestionvarchar(500)下一阶段训练建议
create_timedatetime评估时间

这套表结构最核心的设计思想是:计划是模板,记录是事实,评估是反馈。计划表达“应该练什么”,记录表达“实际练了什么、效果如何”,评估表达“数据说明了什么”。三者通过 child_id 关联起来,构成完整的业务闭环。

3.2 接口约定:前后端怎么对齐

接口设计遵循一个原则:按资源划分,按角色控制。我把主要接口列出来,你可以直接作为接口文档的骨架。

POST /api/auth/login 登录,返回 Token 和用户信息 GET /api/children 家长/康复师获取自己关联的儿童列表 POST /api/children 新建儿童档案(家长/康复师) GET /api/children/{id} 获取儿童详情 GET /api/children/{id}/progress 获取儿童训练进度汇总 POST /api/plans 新建训练计划(康复师) GET /api/plans/{childId} 获取某儿童所有训练计划 GET /api/plans/{planId}/tasks 获取计划内所有任务 GET /api/trainings/today 获取今日训练任务列表 POST /api/training/record 提交训练记录 GET /api/assessments/{childId} 获取儿童评估报告列表 POST /api/assessments 提交评估报告(康复师)

这组接口的路径设计遵循了 RESTful 风格,资源名用复数名词,操作通过 HTTP 方法区分。你不需要追求严格的 REST 规范,但路径一定要直观,让答辩老师一眼看出是哪个模块的功能。

统一返回结构也很重要。我建议定义一个 Result 类,所有接口返回格式一致:

public class Result<T> { private Integer code; // 200 成功,500 失败,401 未登录 private String message; private T data; public static <T> Result<T> ok(T data) { Result<T> result = new Result<>(); result.code = 200; result.message = "操作成功"; result.data = data; return result; } public static <T> Result<T> error(Integer code, String message) { Result<T> result = new Result<>(); result.code = code; result.message = message; return result; } }

Android 端接这种格式非常省事:统一判断 code 是否为 200,再解析 data 即可。如果每个接口返回结构都不一样,客户端解析逻辑会变得很啰嗦,这是很多前后端联调痛苦的根本原因。

3.3 关键代码:训练记录上报接口实现

我挑两个最有代表性的接口展开,训练记录上报和今日任务查询,这两个是业务闭环的核心,也最容易在答辩时被追问。

先看训练记录上报。这是一个写接口,核心逻辑是校验任务和儿童是否存在、写入记录、更新计划进度。注意我说“核心逻辑是校验”,不是“核心逻辑是插入”。很多同学写接口只做 insert,完全不校验,被答辩老师一追问就答不上来“万一重复提交怎么办”“找不到任务怎么办”。

@RestController @RequestMapping("/api/training") public class TrainingRecordController { @Resource private TrainingRecordService recordService; @PostMapping("/record") public Result<?> submitRecord(@RequestBody TrainingRecordDTO dto) { // 1. 校验任务和儿童是否存在 TrainingTask task = recordService.getTaskById(dto.getTaskId()); if (task == null) { return Result.error(500, "训练任务不存在"); } ChildProfile child = recordService.getChildById(dto.getChildId()); if (child == null) { return Result.error(500, "儿童档案不存在"); } // 2. 基本参数校验 if (dto.getCompletionStatus() == null || dto.getAccuracy() == null || dto.getDuration() == null) { return Result.error(500, "关键参数缺失"); } // 3. 写入训练记录 TrainingRecord record = new TrainingRecord(); BeanUtils.copyProperties(dto, record); record.setOperatorId(CurrentUserHolder.getUserId()); record.setRecordTime(new Date()); recordService.save(record); return Result.ok(null); } }

这里有三个细节值得注意。第一,CurrentUserHolder是我写的一个 ThreadLocal 工具类,在 JWT 拦截器里把当前登录用户 ID 放进去,Controller 里直接取,避免在每个接口都手动传 userId,既方便又安全。第二,BeanUtils.copyProperties可以少写一堆 setter,但这个工具类同名属性才会拷贝,DTO 字段名一定要和实体类对齐,否则拷不过去。第三,服务端时间以服务器时间为准,客户端不上传 record_time,避免两端时钟不一致,这一点很实用。

再看今日任务查询。这是一个读接口,逻辑上要组合“计划状态 + 任务列表 + 完成情况”。我的实现思路是:先查当前处于“进行中”状态的计划,然后查计划下的所有任务,再查出今天的训练记录,最后在内存中组装,标记每个任务是否已完成。

public List<TrainingTaskVO> getTodayTasks(Long childId, Date today) { // 1. 查询进行中的计划 LambdaQueryWrapper<TrainingPlan> planQuery = new LambdaQueryWrapper<>(); planQuery.eq(TrainingPlan::getChildId, childId) .eq(TrainingPlan::getStatus, 1) .le(TrainingPlan::getStartDate, today) .ge(TrainingPlan::getEndDate, today); List<TrainingPlan> plans = planMapper.selectList(planQuery); if (plans.isEmpty()) { return Collections.emptyList(); } // 2. 获取所有计划下的任务 List<Long> planIds = plans.stream().map(TrainingPlan::getId).collect(Collectors.toList()); LambdaQueryWrapper<TrainingTask> taskQuery = new LambdaQueryWrapper<>(); taskQuery.in(TrainingTask::getPlanId, planIds) .orderByAsc(TrainingTask::getOrderNo); List<TrainingTask> allTasks = taskMapper.selectList(taskQuery); // 3. 查询今天的已完成记录 LambdaQueryWrapper<TrainingRecord> recordQuery = new LambdaQueryWrapper<>(); recordQuery.eq(TrainingRecord::getChildId, childId) .eq(TrainingRecord::getRecordTime, today); List<TrainingRecord> todayRecords = recordMapper.selectList(recordQuery); Set<Long> doneTaskIds = todayRecords.stream() .map(TrainingRecord::getTaskId) .collect(Collectors.toSet()); // 4. 组装VO,标记完成状态 List<TrainingTaskVO> result = new ArrayList<>(); for (TrainingTask task : allTasks) { TrainingTaskVO vo = new TrainingTaskVO(); BeanUtils.copyProperties(task, vo); vo.setCompleted(doneTaskIds.contains(task.getId())); result.add(vo); } return result; }

注意这里我把“查询计划”“查询任务”“查询记录”分三步做,没有写一条大 SQL 去 join,原因是:表之间的关联复杂度不高,分开查询逻辑清晰,代码可读性好;并且数据量很小,性能完全够用。毕业设计阶段,代码可读性远比微优化重要。这也是我经常和学生强调的一点:不要为了秀技术写出谁都看不懂的代码。

3.4 数据库初始化与演示数据

一个很实用的小建议:在项目里放一个data.sql或启动初始化类,预置一组演示数据。包括一个管理员账号、一个康复师账号、一个家长账号、两个孩子的档案、一个进行中的计划、若干训练任务和几条训练记录。

这看起来是个细节,但对毕设演示至关重要。答辩现场不可能现场注册账号、现场做训练、现场生成报表,预置数据能让你 30 秒内进入演示状态,避免现场等待。同时也让代码审查时看到系统不是空架子,有真实数据支撑。

4. Android 端落地:康复训练功能的交互实现与细节处理

服务端是整个系统的底座,但用户真正接触的是 Android 端。康复训练 APP 和普通管理系统最大的区别在于:它不只是信息展示工具,还要承载孩子实际的操作行为。所以这一端的工作量和难点都比想象中大。

4.1 客户端分层与网络层封装

Android 端我采用了标准的 MVC + Repository 思路,没有用比较重的 MVVM 框架,因为毕业设计代码的可读性比架构先进性更重要。分层结构如下:

  • model:数据实体类,和服务端 DTO 字段一一对应;
  • network:Retrofit 接口定义和网络工具类;
  • repository:数据仓库,负责调接口并处理结果;
  • activity/fragment:页面层,负责绑定数据和渲染界面;
  • adapter:列表适配器。

网络层用 Retrofit + OkHttp,这是目前 Android 最主流的方案。先说一个容易踩的坑:Android 9(API 28)之后默认禁止明文 HTTP 请求。如果你服务端没有配置 HTTPS 证书,直接访问http://192.168.x.x:8080会报错“Cleartext HTTP traffic not permitted”。解决方式是在AndroidManifest.xml的 application 节点加一行:

<application android:usesCleartextTraffic="true" ...>

这样本地联调时才能正常访问测试环境接口。正式商用当然要用 HTTPS,但毕设阶段加这一行就够。

Retrofit 接口定义示例:

public interface ApiService { @POST("api/auth/login") Call<Result<LoginVO>> login(@Body LoginDTO dto); @GET("api/children") Call<Result<List<ChildVO>>> getChildren(); @GET("api/plans/{childId}") Call<Result<List<PlanVO>>> getPlans(@Path("childId") long childId); @GET("api/trainings/today") Call<Result<List<TrainingTaskVO>>> getTodayTasks(@Query("childId") long childId); @POST("api/training/record") Call<Result<Void>> submitRecord(@Body TrainingRecordDTO dto); }

登录功能建议做成全局会话管理。登录成功后,把 Token 和用户信息保存到 SharedPreferences,网络层通过拦截器自动附加 Token 到请求头。这样后续所有接口都不用手动传 Token,体验和真实项目一致。

public class AuthInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); String token = SessionManager.getToken(); if (token != null) { Request request = original.newBuilder() .header("Authorization", "Bearer " + token) .build(); return chain.proceed(request); } return chain.proceed(original); } }

提示:在实际开发中我发现一个很好用的调试姿势——OkHttp 的日志拦截器在 debug 环境打印所有请求和响应体,联调时打开,遇到接口报错一眼就能定位是参数传错还是返回格式不对。上线前记得关掉,这个建议对任何 Android 项目都适用。

4.2 训练任务的交互设计:配对、分类、语音

训练任务是整个 APP 的核心功能。我按类型分别实现了几个训练组件,这里挑最有代表性的三个讲。

配对训练:界面是一个 GridView,卡片分成上下两行,上排是目标图片,下排是候选图片。孩子点击下排卡片,如果和目标匹配,则播放成功音效并弹出星星动画,卡片变成灰色;如果不匹配,卡片抖动一下提示错误。

核心逻辑其实不复杂:一个列表存所有卡片,点击时对比两张卡片的 itemId 即可。我建议把目标卡片和候选卡片混合在同一列表中,避免维护两个列表的状态。匹配成功后用ValueAnimator做缩放动画,视觉反馈很直观,孩子也容易理解“做对了”和“做错了”。

分类训练:界面上显示多个类别按钮,比如“动物”和“水果”,屏幕中央出现一张图片,孩子需要把图片拖到正确的类别区域。这里用 RecyclerView + ItemTouchHelper 实现简单拖拽,拖到区域后判断类别是否正确。

分类训练的难点是拖拽的命中判断。我的方案是:在拖拽过程中实时获取目标区域的中心坐标,当拖拽位置距离目标中心小于阈值时,判定为“放入该区域”。这个阈值根据屏幕宽度动态计算,避免大屏和小屏体验差异过大。

语音跟读训练:调用 Android 系统的 TextToSpeech 播放示范读音,孩子跟读后(如果家长在场辅助),家长选择“发音清楚”“发音模糊”“不配合”三个选项,并记录得分。

这个功能用系统 API 就能完成,但注意释放资源。TextToSpeech 实例在页面销毁时必须调用shutdown(),否则会有内存泄漏提醒。此外,语音训练的结果判定不要做成自动识别(语音识别准确率对儿童发音不友好,容易误判),而是做成家长/康复师主观选项,既简单又符合机构里“教师辅助评估”的真实流程。

这个设计思路很重要:毕设功能的第一优先级不是“看起来厉害”,而是“符合业务真实逻辑且技术可行”。自动语音识别在真实康复场景中既不可靠又增加复杂度,简化的主观选项反而更贴合实际。

4.3 训练记录上报与离线容错

Android 端另外一个关键点是训练记录上报。孩子在训练过程中可能多次暂停、中断,所以上报时机和上报内容需要仔细设计。我的方案是:

  • 页面记录训练开始时间戳;
  • 孩子完成任务后展示结果页,显示用时、正确率、获得奖励;
  • 家长确认后点击“完成训练”,此时客户端才组装记录并上报服务端;
  • 如果在训练过程中退出 APP,再次进入时提示“存在未完成的训练,是否继续”,从本地缓存的进度恢复。

这里有个体验细节:训练中途退出时,要把当前训练状态保存到本地数据库或 SharedPreferences。我选择用 Room 数据库保存训练中间状态,因为任务可能在服务端下发多个,本地持久化需要结构化存储。如果只是临时状态,用 SharedPreferences 也可以,但存储体验会差一些。

上报失败的情况也必须处理。网络不稳定时,把记录存入本地待上传队列,等网络恢复后自动补传。这个“待上传队列”是加分项,答辩时可以当做“系统容错设计”来讲。实现上可以用一个表存待上传记录,再在 Application 里监听网络状态变化,恢复网络时遍历队列上传。

5. 全流程管理闭环:评估、报告与多方协同

把 Android 端的单个训练功能做完,系统已经能用了,但“全流程管理”的深度还不够。这一章讲的是如何把训练、评估、报告串成真正有价值的闭环。

5.1 训练计划如何生成:康复师怎么介入

训练计划不是家长自己随便定的,而是康复师基于评估结果生成的。在系统里,康复师的操作路径是这样设计的:

  1. 登录 APP 后进入“儿童管理”,选择需要安排计划的孩子;
  2. 查看该孩子最近一次评估报告,了解能力短板;
  3. 点击“新建计划”,选择计划周期(通常 2 到 4 周),填写计划名称和目标;
  4. 在计划中添加任务:选择任务类型、标题、难度、频率、每日目标时长;
  5. 保存并发布计划,孩子今天起就能在“今日训练”里看到任务。

为了让这个流程能落地,服务端需要两个关键接口:查看评估报告和新建计划。尤其是新建计划接口,要支持一次提交整个计划的全部任务列表,而不是一个个任务地创建。我在设计这个接口时用了嵌套 DTO:

public class PlanCreateDTO { private Long childId; private String planName; private LocalDate startDate; private LocalDate endDate; private List<TaskItemDTO> tasks; public static class TaskItemDTO { private String taskType; private String title; private String contentJson; private Integer difficulty; private Integer targetDuration; private String frequency; } }

Service 层逻辑是:先插入计划主记录,再循环插入任务列表,用一个事务包住两个操作。

@Transactional(rollbackFor = Exception.class) public void createPlanWithTasks(PlanCreateDTO dto) { TrainingPlan plan = new TrainingPlan(); plan.setChildId(dto.getChildId()); plan.setPlanName(dto.getPlanName()); plan.setStartDate(dto.getStartDate()); plan.setEndDate(dto.getEndDate()); plan.setStatus(0); plan.setCreatorId(CurrentUserHolder.getUserId()); planMapper.insert(plan); for (int i = 0; i < dto.getTasks().size(); i++) { PlanCreateDTO.TaskItemDTO item = dto.getTasks().get(i); TrainingTask task = new TrainingTask(); task.setPlanId(plan.getId()); task.setTaskType(item.getTaskType()); task.setTitle(item.getTitle()); task.setContentJson(item.getContentJson()); task.setDifficulty(item.getDifficulty()); task.setTargetDuration(item.getTargetDuration()); task.setFrequency(item.getFrequency()); task.setOrderNo(i + 1); taskMapper.insert(task); } }

@Transactional一定要加。如果不加事务,任务列表插入到一半失败,就会出现“有计划没任务”的脏数据。我在联调阶段就吃过这个亏,前 4 个任务插入成功、第 5 个失败,页面显示计划存在但任务列表是空的,排查了大半天才发现是事务没加。你不踩上这一个坑,可能都不知道事务注解在真实开发里有多重要。

5.2 家长端:家庭训练与打卡模式

家长端是这个系统里使用频率最高的入口,设计上必须足够简单。我的推荐方案:家长登录后只看到四个核心页面——首页、今日训练、训练记录、我的。

首页展示当前绑定儿童的今日训练完成进度,用进度环展示今天完成了几个任务、还需完成几个,直接告诉家长“今天还有两个任务没完成”。今日训练页面按顺序列出任务卡片,点进去就是任务执行界面。训练记录页面按时间倒序展示历史训练结果。我的页面展示当前账号信息和绑定儿童列表,支持切换孩子。

有一个功能建议必做:训练提醒。每天设定一个提醒时间,到点推送通知“该带孩子训练啦”。用 Android 的 AlarmManager + 通知栏实现,代码量不大但能极大提升系统完整度,也是答辩演示时很有画面感的一个功能。

家长端的数据统计要克制。展示本周训练天数、累计训练分钟数、任务完成率、情绪状态分布即可,不要堆砌一堆看不懂的图表。统计图我推荐用 MPAndroidChart 库,曲线图和柱状图足够用,而且库很成熟,接入成本低。但要注意这个库的体积较大,不影响使用,但演示时首屏加载稍慢,别在图表页面停留太久。

5.3 数据报表怎么设计才算有用

评估报告是系统里最有专业含量的部分。康复师做完一次评估后,系统要生成一份结构化的报告,包含:

  • 基本信息:儿童姓名、年龄、评估类型、评估时间、评估人;
  • 各维度得分:语言、认知、社交、精细动作、生活自理各维度百分制分数;
  • 综合结论:一段评估总结文字;
  • 训练建议:结合得分给出的下一阶段建议;
  • 历史对比:和上一次评估相比,每个维度分数变化情况。

这个报告在 Android 端的展示页是一个长列表,顶部是基本信息和综合结论,中部是各维度得分条形图,底部是建议和历史对比。条形图用 MPAndroidChart 的 HorizontalBarChart 实现,一个数据集五根柱子,非常简单。

历史对比的数据要服务端算好再返回。服务端查询该儿童最近两次评估记录,按维度计算差值,组装成对比数据结构:

public class AssessmentReportVO { private Long childId; private String childName; private String assessmentType; private List<DimensionScoreVO> dimensions; private String summary; private String suggestion; private Map<String, Integer> scoreChange; // 维度名 -> 变化值 }

这里的关键思维是:客户端越“笨”,系统越稳。所有计算、聚合、对比逻辑都在服务端完成,客户端只负责把拿到的数据渲染出来。这样不用管 Android 端数据不一致的问题,也好测试。

5.4 评估与训练的整体协同逻辑

把评估和训练串起来看,系统闭环就完整了:初始评估建档 → 根据评估结果制定训练计划 → 家长带孩子按计划执行训练 → 系统记录每次训练数据 → 周期结束后再次评估 → 根据前后两次评估对比判断训练是否有效 → 调整下一阶段训练计划。

这个闭环在答辩时一定要流畅地讲出来。我的建议是准备一个标准演示脚本,按顺序走一遍:康复师登录 → 查看儿童评估报告 → 创建新计划 → 退出登录 → 家长登录 → 查看今日任务 → 完成一个训练并上报记录 → 查看本周训练统计 → 回到康复师端查看训练记录汇总。整个演示最好控制在 10 分钟以内,突出业务完整性和交互流畅度。

答辩老师最喜欢问的问题之一就是“如果训练效果不好,系统能做什么?”回答思路是:系统不直接判断训练是否有效,而是通过记录的数据和再次评估的结果,把变化趋势呈现给康复师;康复师根据趋势人工调整计划。系统的角色是“数据辅助决策”,不是“自动决策”。这个定位既符合真实业务逻辑,又避开了“系统是否有医学价值”这种难以回答的质疑。

6. 联调阶段的坑、性能优化与答辩准备

系统功能做完不是终点,真正让人头疼的是联调阶段。这一章把我在这个项目中遇到过的问题和解决办法集中整理出来,你大概率也会碰到其中几个。

6.1 前后端联调最常见的四个坑

第一,时间格式不一致。服务端返回的日期时间默认是 ISO 格式(例如2025-06-28T15:30:00),Android 端如果用SimpleDateFormat直接解析会报错。解决办法是统一约定:服务端在application.yml配置全局时间格式。

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8

Android 端用Gson或Fastjson解析时也统一按这个格式处理。时间问题不解决,你在训练记录列表页、评估报告页会看到一串排不整齐的时间字符串,非常影响观感。

第二,图片上传的路径问题。儿童头像、训练素材图片都需要上传,我在 Spring Boot 里做了静态资源映射,把本地上传目录和外部访问路径对应起来:

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceHandler("file:" + uploadDir + "/"); } }

但这里有一个联调常见的坑:上传时返回的图片地址如果是相对路径/upload/xxx.jpg,Android 端加载时必须拼接服务端 IP 和端口,变成http://192.168.1.100:8080/upload/xxx.jpg。如果拼接逻辑写错,图片就加载不出来。我的做法是上传成功后服务端直接返回完整的可访问地址,客户端拿到什么就用什么,不自行拼接,省去很多麻烦。

第三,Android 端模拟器访问宿主机。如果你用 Android Studio 模拟器调试,访问本机服务端时不能用localhost或127.0.0.1,因为模拟器里这指向模拟器自身。要用10.0.2.2才能访问宿主机。这个坑几乎所有初次做前后端联调的人都会遇到,我在这个项目里也花了不少时间排查,最后才反应过来是地址写错了。

第四,请求参数序列化。如果你在 Android 端用 Fastjson 序列化对象,命名风格不一致会导致字段解析不出来。比如服务端字段是childId,Android 端写成了child_id,解析结果就是 null。建议两端都用驼峰命名,并配合 Lombok 的@Data注解,字段名保持完全一致。

提示:联调开始前,先花 10 分钟确认双端的字段命名规范、时间格式、返回结构三项约定,后面能省下几十倍的排查时间。

6.2 训练环节的流畅度优化

训练界面是孩子直接操作的地方,流畅度直接决定使用体验。我在项目中做了一次性能优化,核心是减少卡顿:

图片加载用 Glide 做缓存。配对训练中的素材图片如果每次都从网络拉取,加载时间不可控。Glide 会自动做内存缓存和磁盘缓存,第一次加载后,后续进入训练页面几乎瞬间呈现。

Glide.with(context) .load(imageUrl) .placeholder(R.drawable.ic_loading) .error(R.drawable.ic_error) .into(imageView);

动画用硬件加速。配对成功时的缩放动画、卡片翻转动画,可能在低端设备上出现掉帧。在 Activity 的onCreate里开启硬件加速(默认就是开启的),但不要在自定义 View 的draw里频繁创建对象,导致 GC 频繁触发。我的做法是动画的 ValueAnimator 只创建一次,通过反复 start 复用。

数据列表用 RecyclerView。训练记录列表页和计划列表页都用 RecyclerView,配合setHasFixedSize(true)提升性能。如果列表数据量不大(几十条以内),不需要分页加载;如果评估报告按时间累积多了,可以加一个简单的滑动加载更多。

本地缓存减少接口请求。家长端的首页进度环在进入时调用一次接口,训练记录页面在数据不变时不清空缓存。我用Lifecycle的onResume才刷新数据,避免页面切换时重复拉取接口导致白屏和等待。

6.3 答辩的核心梳理与常见提问

最后说答辩。毕业设计做得再好,讲不清楚也拿不到高分。我建议你按这条线准备答辩材料:

第一,讲清楚“这是一个什么问题”。用 30 秒说明背景:自闭症儿童康复训练周期长、过程复杂,需要多角色协作,现有管理方式效率低,所以做一个平台把评估、计划、执行、反馈串起来。

第二,讲清楚“系统怎么解决问题的”。展示系统架构(服务端 + 客户端 + 数据库三层),展示核心业务流程(评估 → 计划 → 训练 → 记录 → 评估),展示核心页面(计划管理、训练执行、记录上报、评估报告)。

第三,讲清楚“你自己做的最有价值的部分”。每个人都应该有自己最熟悉的亮点。如果你的亮点在 Android 端,就重点讲训练交互设计和离线容错;如果在服务端,就重点讲接口设计和事务一致性。重点不在于功能多,而在于你能把某一个部分讲透。

第四,准备好被追问的技术点。我根据自己的答辩经验,把高频问题整理如下:

Q1:你的系统如何保证同一计划不会被重复创建?答:创建计划接口在 Service 层校验该儿童当前是否存在进行中的计划,如果存在则提示先结束旧计划。同时用一个唯一索引 + 业务校验双重保障。

Q2:训练记录数据量很大怎么处理?答:数据量增大后可以按月分区,或只在服务端做统计聚合,客户端按页加载历史记录。统计报表查询时按 child_id + 时间范围走索引。

Q3:为什么用 JWT 而不用 Session?答:JWT 适合前后端分离场景,服务端不需要维护会话状态,Android 端只需保存 Token 并在请求头携带,轻量且易扩展。缺点是 Token 无法主动失效,但毕设场景下完全够用。

Q4:Android 端如何保证离线也能使用部分功能?答:我把今日任务列表和训练中间状态缓存在本地数据库,网络不可用时可以查看已缓存的任务,训练完成后的记录先保存到本地待上传队列,网络恢复后自动补传。

这些问答你自己要先过一遍,哪怕只是嘴上说说,也比现场临场发挥强很多。

6.4 还有哪些可扩展的方向

如果一个学期的时间比较充裕,或者你想把项目做得更好看,有几个方向可以在现有基础上扩展,不必伤筋动骨:

  • 视频训练指导:在任务中嵌入示范视频,孩子先看示范再练习,需要服务端支持视频上传和流媒体播放;
  • 数据大屏:管理员端增加一个 Web 或 Android 大屏页面,展示所有儿童训练完成率、情绪状态分布、康复师工作量等统计指标;
  • 消息推送:用极光推送或 Firebase Cloud Messaging,把任务提醒、计划调整通知主动推送到家长手机;
  • 语音识别辅助评估:引入成熟语音识别 SDK,对儿童跟读进行初步打分,再结合教师人工确认,形成半自动评估;
  • 家庭报告邮件:定期把孩子的训练报告生成为 PDF 发送给家长,Spring Boot 端用 iText 或 POI 生成 PDF 即可。

这些方向任意选一个做出来,“系统亮点”就非常突出了。但记住一个原则:扩展功能是锦上添花,先把主流程跑通、跑稳定,再谈扩展。很多同学一开始就想做视频、做推送,结果主流程都没做完,最后被迫删功能,非常可惜。

写在最后的一点体会

这个项目从头到尾做下来,我最大的感受是:毕业设计选题看起来是“技术题”,实际上更多是“业务题”。技术栈都是现成的,框架、库、网上教程一大把,真正拉开差距的是你对业务的理解深度和设计能力。自闭症康复训练全流程管理系统这个题目是有温度的方向——它不是做一个普通的后台管理,而是试图用技术去支持一个特殊群体的日常康复过程。把评估、计划、训练、记录、反馈这条链路想清楚并落地,学到的东西会比单纯做一百个增删改查接口多得多。

如果你正在做这个题目,或者选了类似的“XX平台 + XX管理系统”方向,我希望这篇内容能帮你少走弯路。先理业务,再建表,再写接口,最后做界面,这个顺序不要乱。反而仗着一身技术冲动从界面开始写,大概率会在后期返工。祝你的项目顺利通过验收,也祝你能在答辩台上把这个系统讲得漂漂亮亮。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 0:10:01

小说CMS轻简部署全指南:从环境配置到批量导入与伪静态

简介&#xff1a;这是一套面向个人站长与小说网站运营者的轻简小说内容管理系统&#xff08;CMS&#xff09;&#xff0c;以快速搭建、低技术门槛为设计目标&#xff0c;帮助非技术背景用户完成小说入库、分类、章节管理、用户注册登录及展示等日常运营需求。压缩包共884个文件…

作者头像 李华
网站建设 2026/9/26 0:07:42

Atlas 300V 24G昇腾推理卡上跑通YOLOv5:从环境配置到性能调优

上周一个朋友突然发消息问我&#xff1a;atlas 300v 24g 是不是运算加速卡&#xff0c;能不能跑 YOLO。我说能跑&#xff0c;但它不是用来跑训练的&#xff0c;也不是插上就能用的“显卡”。他接着问&#xff1a;那为什么我照着 GPU 的教程装完 PyTorch&#xff0c;驱动也能认&…

作者头像 李华
网站建设 2026/9/26 0:07:25

无人茶室系统实战:Java Spring Boot预约与设备联动设计

把无人茶室这套系统从零到一落地&#xff0c;前后大概用了三周。项目本身不复杂&#xff0c;但“无人”两个字把所有压力都压在了后台——预约排期、订单计费、门锁联动、异常告警&#xff0c;哪一个环节断了&#xff0c;客人都会被关在门外。技术底座选了 Java&#xff0c;原因…

作者头像 李华
网站建设 2026/9/26 0:06:29

HTTP协议与www子域名的分层原理及工程避坑指南

1. 别再把“http://”和“www.”当成一回事了&#xff1a;一个被所有人忽略的底层认知断层你有没有点开过这样的链接&#xff1a;http://www.example.com&#xff0c;然后下意识觉得“哦&#xff0c;这是个网站”&#xff1b;接着又看到https://example.com&#xff0c;心想“这…

作者头像 李华
网站建设 2026/9/26 0:03:43

超低能耗建筑K值要求能否满足?浙东铝业建筑型材解析

核心摘要浙东铝业的超低能耗系统门窗产品&#xff0c;资料显示保温性能可达 K≤1.4W/(㎡K)&#xff0c;能够对应上海地区超低能耗住宅对门窗保温性能的应用需求。判断建筑是否满足超低能耗要求&#xff0c;不能只看铝型材本身&#xff0c;还需要结合玻璃、隔热条、密封系统、开…

作者头像 李华