ARTICLE DETAIL

资讯详情

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

JavaWeb实现阿尔茨海默症早期康复系统:从设计到部署全攻略

JavaWeb实现阿尔茨海默症早期康复系统:从设计到部署全攻略 简介面向专科、本科毕业生的一份JavaWeb方向原创毕业论文文档以阿尔茨海默症早期康复系统设计为主线依次覆盖研究背景、国内外进展、疾病科普、早期康复现状与挑战以及系统需求分析、架构设计、功能模块划分和测试评估可帮助读者建立完整的毕业设计写作框架。压缩包内为单个DOCX文档大小约33KB从摘要、目录到第五章系统测试与评估均已整理成文适合直接阅读、参考或二次修改。该资源已有93人学习尤其适合计算机科学与技术及相关专业学生用于理解基于JavaWeb的医疗康复系统设计与论文撰写。文档还讨论了用户管理、康复计划定制、康复训练、数据分析等模块并涉及系统安全性、稳定性与用户体验优化对需要快速搭建论文结构、补充算法与设计细节的毕业生具有较强参考价值。1. 阿尔茨海默症早期康复系统为什么我推荐用 JavaWeb 落地家属要记录老人每天的认知训练结果医生想看到一个月内的分数变化曲线社区机构希望有一台电脑就能给几十位老人做筛查——这就是我说“阿尔茨海默症早期康复系统用 JavaWeb 做最顺手”的场景。它不需要分布式架构不依赖微信小程序审核核心就是一台服务器加一个浏览器照护者登录后带着老人做几组记忆和注意力练习系统记录每一次得分按时间生成趋势。JavaWeb 生态里 Tomcat MySQL Servlet/JSP 这套组合二十年没有断过代学习资料和踩坑案例多到搜不完哪怕你只学过一半黑马的 JavaWeb 笔记也能把项目从设计文档落到能演示的完整系统。本文适合正在做课程设计、毕业设计或者小规模医疗信息化的从业者我会把从表结构、IDEA 配置到避坑的全部细节写透。2. 系统设计先行功能模块、技术选型与 MySQL 表结构2.1 康复系统的功能边界筛查、训练、跟踪三件事阿尔茨海默症的早期康复不是一个“治愈”动作而是延缓认知衰退的训练计划。系统如果只做一个答题页面那和网上随便找的测评工具没有区别真正有价值的是把三件事串成一个闭环。第一件事是筛查。老人第一次使用系统时照护者需要在指导下完成一轮基础评估比如时间定向、词语回忆、数字广度测试得到一个基线分数。这个分数决定后续训练强度所以它不能只存在前端必须落库。第二件事是训练。根据基线分数生成一组训练任务比如按顺序点击数字、从几张图片里找出目标物品、复述一段话。每次训练时长控制在 15 到 30 分钟太长了老人会疲劳太短了数据没有统计意义。第三件事是跟踪。训练结束后系统要记录得分、用时、错误次数并且在一个页面里展示“第 1 次 32 分 → 第 20 次 58 分”这样的趋势。医生或者照护者能看懂这个曲线才敢判断当前方案是否有效。系统角色必须分开。患者本人基本不直接操作电脑实际操作者是照护者或社区护士所以客户端设计上要做成大字号、少跳转、每个操作都有二次确认。后台管理端才是医生和机构管理员用的负责维护题目、查看患者列表、导出评估报告。我第一次做这类系统时把患者和照护者混在一张表里结果权限判断处处别扭后来拆成两种角色才顺畅。功能边界在需求阶段就要锁死否则后期加功能会拖垮整体节奏。2.2 技术选型Servlet/JSP 还是 SpringBoot数据表怎么拆标题写的是 JavaWeb不是 Spring Boot这就意味着你有一个自由的选型空间。我见过两类做法一类是课程设计常用方案——Servlet JSP Tomcat MySQL代码写在三层结构里不引入额外框架另一类是把 SpringBoot 当作 JavaWeb 的现代实现。我的建议是如果项目挂在“JavaWeb 设计”这个名称下并且你的重点在展示完整链路那么传统 Servlet/JSP 足够支撑全部功能而且答辩时你可以把每个 Request 的流转过程讲得清清楚楚。这并不代表要丢掉 Maven。相反我强烈建议用 Maven 来管理依赖哪怕是一个 10 张表的小项目。原因只有一个配置可控。你不会希望自己手动把十几个 jar 包拖进 WEB-INF/lib然后换一台电脑就少一个包。用 Maven 声明依赖IDEA 会自动拉取项目交给别人也能一键导入。以下是这个项目最精简的依赖清单dependencies !-- Servlet 核心 APITomcat 9 对应 javax.servlet-api 4.0 -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- JSP 支持视图层需要 -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency !-- MySQL 驱动版本要和数据库对应MySQL 8 用 8.0.33 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- JSTL 标签库JSP 里做循环和判断 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency /dependencies注意两个地方Servlet 和 JSP 的 scope 是provided因为 Tomcat 本身自带这两个库打包时不需要塞进去MySQL 驱动的 scope 不要写成provided否则部署到独立 Tomcat 时会出现找不到驱动。这个细节我在第 4 章会展开讲。数据表怎么拆是第二个关键问题。有人喜欢把训练记录和评估结果放一张表省事但康复系统里二者频率不同——评估一天最多一两次训练一天可能三五次混在一起会让查询条件变得很难写。我会拆成四张核心表用户表、题目表、训练记录表、评估结果表。题目表里存的是题目内容、类型和难度训练记录表存一次训练的得分和耗时评估结果表只存基线评估和阶段性复评。这个拆分方法是照着黑马 JavaWeb 笔记里的经典三层案例演化出来的不同的是把业务表单独拉出来避免数据冗余。2.3 核心表结构用户、训练记录、评估结果的设计细节我不会把整个数据库脚本全部贴出来因为那会占掉大半篇幅这里给出四张最核心的建表语句并解释每一张表为什么这样设计。先看用户表CREATE TABLE sys_user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登录账号, password VARCHAR(128) NOT NULL COMMENT 加盐后的密码摘要, salt VARCHAR(16) NOT NULL COMMENT 盐值随机生成, real_name VARCHAR(50) DEFAULT NULL COMMENT 真实姓名, role TINYINT NOT NULL DEFAULT 2 COMMENT 角色 1管理员 2照护者 3医生, patient_id INT DEFAULT NULL COMMENT 关联患者ID照护者角色时有效, phone VARCHAR(20) DEFAULT NULL, status TINYINT NOT NULL DEFAULT 1 COMMENT 1启用 0禁用, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表;这里有一个容易忽略的点密码字段长度不要设成 32 或 64。如果你用 SHA-256 加盐摘要长度是固定的 64 位十六进制但加上盐的存储、未来算法升级128 的长度更安全。另外patient_id这个字段看起来和用户表耦合了实际是“一个照护者绑定一个患者”的业务约束比单独建中间表更直观。如果你做一个机构版患者和照护者是多对多那再考虑中间表。看训练记录表CREATE TABLE training_record ( id INT NOT NULL AUTO_INCREMENT, patient_id INT NOT NULL COMMENT 患者ID, task_type TINYINT NOT NULL COMMENT 任务类型 1数字广度 2词语回忆 3注意力训练, task_id INT NOT NULL COMMENT 题目组ID, score INT NOT NULL COMMENT 本次得分 0-100, duration_seconds INT NOT NULL COMMENT 训练用时单位秒, error_count INT DEFAULT 0 COMMENT 错误次数, detail_json JSON DEFAULT NULL COMMENT 题目级明细用于复盘, train_date DATE NOT NULL COMMENT 训练日期按天统计, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_patient_date (patient_id, train_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT训练记录表;detail_json是这个表最有价值的设计。很多 JavaWeb 项目完整案例 MySQL 版本里训练明细都是再拆一张子表但康复训练每次题目数量不固定子表会越查越重。用 JSON 存题目级明细既能回溯“第 3 题答错了哪个词”又不必为它单独建表。MySQL 5.7 以上支持 JSON 类型查询时可以用JSON_EXTRACT但日常只做存储和展示不需要在 SQL 里解析。评估结果表的结构和训练记录类似但它额外记录了评估类型首次基线/月度复评和医生结论CREATE TABLE assessment_result ( id INT NOT NULL AUTO_INCREMENT, patient_id INT NOT NULL, assessment_type TINYINT NOT NULL COMMENT 1首次基线 2周复评 3月复评, total_score INT NOT NULL COMMENT 综合评分, memory_score INT DEFAULT NULL COMMENT 记忆维度分, attention_score INT DEFAULT NULL COMMENT 注意力维度分, doctor_advice VARCHAR(500) DEFAULT NULL COMMENT 医生建议, assessor_id INT NOT NULL COMMENT 评估人ID取sys_user.id, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_patient_time (patient_id, create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT康复评估结果表;两张表的区别在于语义training_record是过程数据assessment_result是结论数据。医生看结论照护者看过程两者不能混。设计表结构时我习惯把所有时间字段都加到表里create_time和update_time一开始就写上哪怕当前用不到也不要在上线后再补字段。3. 用 IDEA 跑通一个 JavaWeb 康复系统核心代码与配置3.1 IDEA 运行 JavaWeb 项目的配置Maven 仓库、Tomcat 与 JDK 版本到手一个 JavaWeb 项目第一步不是看代码而是先把运行环境跑通。IDEA 里最多的问题集中在 Maven 仓库源和 Tomcat 配置。Maven 默认源在国外初次导入依赖时可能卡在下载环节我一般会改settings.xml里的镜像源为阿里云镜像但要注意黑色和红色报错最能暴露仓库源问题jar 下载到一半再切换源lastUpdated文件要删掉否则重试也不生效。JDK 版本和 Tomcat 版本必须匹配。Tomcat 9 对应 JDK 8 或 11Tomcat 10 对应 JDK 8 以上但包名从javax.*改成了jakarta.*。因为这个项目用的是javax.servlet千万不能选 Tomcat 10否则 servlet 类全部编译过。我在 IDEA 里配置 Tomcat 的步骤通常是Run 菜单 → Edit Configurations → 点加号选 Tomcat Server → Local → 在 Application server 里选本地 Tomcat 目录。这里的存量问题在第 4 章会讲。项目的目录结构也决定了 IDEA 能否正确打包标准做法是 Maven 的 webapp 结构。如果你的项目是从别处拷来的导入后右键项目 → Add Framework Support → 勾选 Web 和 Maven。看到src/main/webapp/WEB-INF/web.xml存在IDEA 才会把它识别成 Web 项目否则无法添加 Artifact。我曾经接手过一个项目所有页面都在就是没有web.xmlIDEA 怎么都不认新建一个空的web.xml才恢复正常。运行前的最后一步是配置 Maven 的编译版本properties maven.compiler.source11/maven.compiler.source maven.compiler.target11/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties这个配置会在打包时把字节码锁定在 Java 11如果你本机装的是 JDK 17不写这个属性编译可能直接失败或者报“无效的源发行版”。黑马 JavaWeb 笔记数据里的项目大多沿用 JDK 8但现代版本 JDK 建议统一到 11兼容性和特性都够用。3.2 认知训练模块的实现答题交互与康复评估逻辑认知训练最常见的题型是“数字广度”——屏幕上显示一串数字消失后让老人按顺序回忆点选。这个题型的核心逻辑是题目由服务端生成前端只负责展示和收集作答最后提交到后端计分。下面是一个训练任务下发接口的简化实现WebServlet(/api/training/task) public class TrainingTaskServlet extends HttpServlet { Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应类型前端 fetch 才能正确解析 response.setContentType(application/json;charsetUTF-8); request.setCharacterEncoding(UTF-8); HttpSession session request.getSession(false); if (session null || session.getAttribute(userId) null) { response.setStatus(401); response.getWriter().write({\message\:\未登录\}); return; } // 从请求参数获取本次训练类型默认数字广度 int taskType request.getParameter(type) null ? 1 : Integer.parseInt(request.getParameter(type)); // 调用 Service 层获取题目组这里用静态示例数据模拟 TrainingTaskService service new TrainingTaskService(); TrainingTask task service.getTaskByType(taskType); // 关键只返回题目的展示字段正确答案绝不下发 JSONObject result new JSONObject(); result.put(taskId, task.getId()); result.put(type, task.getType()); result.put(digits, task.getDisplayDigits()); // 例如 [5, 3, 8, 2] result.put(timeout, task.getTimeoutSeconds()); response.getWriter().write(result.toJSONString()); } }这段代码里有三个细节值得说。第一response.setContentType(application/json;charsetUTF-8)必须写否则前端拿到的可能是text/html解析为 JSON 时直接抛错。第二从 Session 判断登录状态而不是信任前端参数训练题目接口一旦暴露未登录用户就能刷题数据全乱。第三正确答案不下发是这条设计的关键。注意digits字段只放展示用的数字串如果这是一道“找目标物品”题前端拿到的只有图片路径和备选项正确位置只存在后端计分逻辑里。这是防止老人没有作答就拿到答案的底线设计。计算分数的逻辑放在后端public class TrainingScoreCalculator { /** * 计算数字广度题得分 * param correctCount 正确回忆的数字个数 * param totalCount 本次题目总数字个数 * param duration 用时秒数 * return 0-100 的整数得分 */ public int calculateDigitSpanScore(int correctCount, int totalCount, int duration) { if (totalCount 0) { return 0; } // 基础正确率占 70 分用时因素占 30 分 double accuracyScore (double) correctCount / totalCount * 70.0; double timeScore 0; if (duration 0 duration 60) { timeScore 30.0; } else if (duration 120) { // 超时递减最低给 5 分 timeScore 30.0 - (duration - 60) * 0.4; timeScore Math.max(timeScore, 5.0); } else { timeScore 5.0; } return (int) Math.round(accuracyScore timeScore); } }这个计算器的参数是我根据康复训练常识定的正确率权重是 70%因为康复训练目标不是比速度而是比准确用时权重 30%防止老人完全拖延时间。你接手项目时可以根据医生建议调整权重但调整后要保证训练分数的分布不至于长期贴着 100 分或者 0 分Score 校准这个步骤太容易被人忘记我在第 5 章会专门说。3.3 训练进度接口把评估结果写回 MySQL 的完整链路前端完成一次训练后需要把结果提交到后端。这个接口是所有数据落库的核心也是验证三层架构是否合理的试金石。下面是一个完整的 Service 方法它负责把一条训练记录写入 MySQLpublic class TrainingRecordService { private JdbcTemplate jdbcTemplate; public TrainingRecordService() { // 获取数据库连接的工具类内部使用 Druid 连接池 this.jdbcTemplate JdbcTemplateFactory.getJdbcTemplate(); } /** * 保存训练记录 */ public boolean saveRecord(TrainingRecord record) throws SQLException { String sql INSERT INTO training_record (patient_id, task_type, task_id, score, duration_seconds, error_count, detail_json, train_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?); int rows jdbcTemplate.update(sql, record.getPatientId(), record.getTaskType(), record.getTaskId(), record.getScore(), record.getDurationSeconds(), record.getErrorCount(), record.getDetailJson(), record.getTrainDate()); return rows 0; } }你可能会问JdbcTemplate 不是 Spring 的东西吗纯 JavaWeb 怎么用答案是它可以被单独引入。Maven 里加spring-jdbc依赖不把整个 Spring 框架引进来也能用JdbcTemplate简化 JDBC 操作。这样做的好处是代码干净坏处是你得手动初始化连接源。实际项目里很多人用 Druid 连接池 JdbcTemplate 组合下面是对应的初始化代码public class JdbcTemplateFactory { private static JdbcTemplate jdbcTemplate; static { try { // 读取数据库配置不写死在代码里 Properties props new Properties(); props.load(JdbcTemplateFactory.class.getClassLoader() .getResourceAsStream(db.properties)); DruidDataSource dataSource new DruidDataSource(); dataSource.setUrl(props.getProperty(jdbc.url)); dataSource.setUsername(props.getProperty(jdbc.username)); dataSource.setPassword(props.getProperty(jdbc.password)); dataSource.setInitialSize(5); dataSource.setMaxActive(20); jdbcTemplate new JdbcTemplate(dataSource); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } }注意db.properties里的 URL 必须带三个参数这是 JavaWeb 项目连接 MySQL 最容易翻车的点jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/rehab_db?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.passwordyour_passworduseUnicodetruecharacterEncodingUTF-8解决中文乱码serverTimezoneAsia/Shanghai解决 MySQL 8 时区报错。少了最后一个参数启动时大概率会看到The server time zone value Öйú±ê׼ʱ¼ä is unrecognized这种乱码异常第一次遇到的人会误以为是编码问题实际只是缺了时区声明。前端提交数据后整个链路是JSP/前端页面 → TrainingSubmitServlet 解析参数 → 校验数据合法 → 调用 saveRecord 写入 training_record。接口链路必须保持短平直不要在 Servlet 里写业务逻辑否则后期维护你会想骂人。4. 项目踩坑与排查从 404 到数据错乱的 5 条经验4.1 现象IDEA 里启动 Tomcat 后页面 404IDEA 启动 Tomcat 后浏览器打开http://localhost:8080/显示 404这是 JavaWeb 新手最常遇到的第一个拦路虎。原因通常是两层第一Tomcat 的 Deployment 里 Artifact 没有添加或者添加的 Application context 路径和访问路径不一致第二项目里没有默认欢迎页。解决方法是打开 Run → Edit Configurations → Deployment 标签页点加号选 Artifact然后看 Application context 的取值。如果你配置成/rehab那访问地址就得是http://localhost:8080/rehab/而不是根路径。再把webapp/index.jsp或者其他欢迎页加进去Tomcat 才会在访问目录时自动跳转。我见过有人把 Application context 写成/out/artifacts/xxx这是把 IDEA 的编译输出路径当部署路径了删掉重选 Artifact 即可。4.2 现象MySQL 中文乱码康复记录全是问号训练记录表里保存的是患者姓名、训练备注结果页面显示的是一排问号。原因有两个数据库表字符集不是utf8mb4或者 JDBC 连接字符串缺少characterEncodingUTF-8。前者要看建表语句的DEFAULT CHARSET如果你导入的 SQL 脚本是latin1那怎么改连接串都没用。解决方法是先确认表字符集用SHOW CREATE TABLE training_record;查看如果不是 utf8mb4执行ALTER TABLE training_record CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;。然后在db.properties的 URL 后追加useUnicodetruecharacterEncodingUTF-8重启 Tomcat。这里有个血泪经验改完数据库字符集之后还要检查web.xml里有没有设置请求编码过滤器否则前端传上来的中文在 Servlet 里就乱掉了落库再整改是双重翻车。最好在项目里写一个 CharacterEncodingFilter强制 request 和 response 都用 UTF-8。4.3 现象训练进度重复写入评估次数翻倍前端训练完成后点击“保存结果”按钮网络慢用户连续点了三次后台插入了三条一模一样的数据。这在康复数据里后果很严重——曲线图会凭空出现一个跳变点。原因就是没有任何防重复机制。解决方法是双保险。后端在接口入口判幂等前端提交时带一个requestId后端把它存在 Redis 或数据库表里重复的 requestId 直接返回成功但不执行插入如果项目没引入 Redis至少要用 Post/Redirect/Get 模式让 Servlet 在第 31 行完成插入后response.sendRedirect(success.jsp)页面刷新也不会重新提交表单。我一般会再加上一个约束同一患者的同一天、同一任务类型只允许一个训练分数用数据库唯一索引把(patient_id, task_type, train_date)设成唯一键重复插入直接抛异常。这个设计不用写代码就挡住了 90% 的问题。4.4 现象JSP 页面拿不到登录状态用户明明登录成功了跳转到首页后显示“未登录”或者每次刷新都要重新登录。这个问题在 JSP 项目里非常典型原因是 Session 域和浏览器会话之间断了。最常见的原因你在登录 Servlet 里写了session.setAttribute(userId, id)但sendRedirect之后目标页面在另外一个应用上下文里或者 Tomcat 的 Session 序列化配置有误。排查步骤我按顺序走先看登录成功后跳转地址是不是同一个 context path比如/rehab/login.jsp跳到/login.jsp就错了再到浏览器开发者工具的 Application 面板看 JSESSIONID 是否存在。如果 JSESSIONID 在但页面仍拿不到检查 JSP 页面顶部是不是写了% page sessionfalse %这个属性会把 JSP 的 session 隐藏。另外Spring MVC 项目加入 Spring Security 后如果配错了拦截器也会覆盖 session 值但纯 Servlet 项目里 95% 是路径或者sessionfalse的问题。4.5 现象项目打包后找不到数据库驱动在 IDEA 里运行一切正常但用 Maven 打成 war 包部署到独立的 Tomcat 后报ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因前面在 pom.xml 里已经埋了伏笔mysql-connector-java 的 scope 被写成providedMaven 打 war 包时不会把它放进WEB-INF/lib。Tomcat 自己的lib目录里又没有这个驱动自然加载不到。解决方法是把 pom.xml 里 MySQL 驱动的 scope 删掉或者改成默认的compile。这是一个只靠 IDE 运行永远发现不了的问题因为 IDEA 的 Artifact 打包机制默认会把 provided 依赖也加进去但独立 Tomcat 不会。所以提交代码前我习惯手动解压一次 war 包查看WEB-INF/lib是否有 mysql 驱动这比部署到服务器再晚发现好得多。另外如果你用的是 Tomcat 8.5还要注意驱动 jar 包不能放在WEB-INF/classes下必须放WEB-INF/lib。5. 数据准确性与安全性康复系统上线前的硬要求5.1 评估结果校验防止异常值污染训练曲线康复训练数据最怕的不是缺数据而是脏数据。一次训练的得分是 50下一次是 100医生会认为方案有效但如果下下次又是 10整条曲线就失去参考意义。我在设计阶段吃过亏当时完全没有后端校验测试时手动调接口提交了一个score999结果趋势图直接拉满上限所有正常数据都被压缩到图表底部。从那以后我在服务端专门加了一个校验工具类所有训练和评估数据在落库前统一过一遍public class RehabDataValidator { private static final int MAX_SCORE 100; private static final int MIN_SCORE 0; private static final int MAX_DURATION 3600; private static final int MIN_DURATION 5; /** * 校验训练记录合法性 * return 合法返回 null否则返回错误信息 */ public static String validateTrainingRecord(TrainingRecord record) { if (record.getScore() MIN_SCORE || record.getScore() MAX_SCORE) { return 得分必须在 0-100 之间; } if (record.getDurationSeconds() MIN_DURATION || record.getDurationSeconds() MAX_DURATION) { return 训练时长必须在 5-3600 秒之间; } if (record.getPatientId() null || record.getPatientId() 0) { return 患者 ID 不合法; } if (record.getTaskId() null) { return 题目组不能为空; } return null; } }除了后端参数校验还要做第二种校验逻辑一致性校验。比如训练时长 5 秒但题目数量有 20 道这个数据一定有问题因为老人不可能 5 秒完成 20 题。我会额外比较taskId对应的题目数量与duration的最小可接受值如果duration / taskCount 1.5就判定为异常并拒绝入库。康复训练里的“快速作答”不一定是有效训练更可能是误触宁可拒绝也不写入。前端显示趋势曲线时我还会在 SQL 查询里加一个过滤条件比如只取score BETWEEN 0 AND 100双保险。这样即使某条异常数据溜进了数据库也不会在图表上产生肉眼可见的毛刺。5.2 敏感数据保护患者信息与测试结果的安全基线康复系统涉及的是医疗健康数据和普通管理系统不一样。患者姓名、训练分数、患病状态都属于敏感个人信息一旦泄露对老人家庭的伤害远超普通账号被盗。JavaWeb 项目在这方面的安全基线不需要做到银行级别但以下几条是必须的。密码存储绝不能用明文也不能只做一次 MD5。JavaWeb 项目最简单可靠的做法是“加盐 SHA-256”public class PasswordUtil { public static String generateSalt() { return UUID.randomUUID().toString().replace(-, ).substring(0, 16); } public static String hashPassword(String rawPassword, String salt) { String salted salt rawPassword; return DigestUtils.sha256Hex(salted); } }登录时从数据库查出盐值再用相同算法计算摘要比对两个摘要是否一致。绝对不要在代码里把盐写死否则等于没加盐。黑马 JavaWeb 笔记数据里有一个典型例子就是密码字段只有 32 位且不加盐这样的项目只能拿来练手不能直接套到康复场景。数据库权限分离也是很多人忽视的一环。开发时用 root 连接没问题但项目部署后至少建一个专用账号只授予该数据库的增删改查权限不给grant option。如果系统有 Web 管理后台登录认证和业务接口要分开后台管理页面的 URL 不要暴露在普通用户可见的导航里。日志输出也要管住。我在一个项目里看到过这样的日志saveRecord success patientId12 score88 detail[{word:苹果...}]这在康复场景里已经越界了。日志里可以记录patientId作为关联主键但患者姓名、具体题目作答明细绝不能打到日志里。清零日志的权限要单独规定康复训练数据属于持续跟踪数据不能随意清理或覆盖。5.3 备份与恢复MySQL 定时备份的最小方案前面说的是防止脏数据本节说的是防止数据毁灭。医疗系统跑几个月后训练记录、评估曲线都是宝贵资产服务器磁盘坏了就全没了。我见过不止一个小团队的项目数据库裸奔没有任务计划也没有备份脚本。我用的是最简单的 mysqldump crontab 方案不需要装任何额外组件。#!/bin/bash # 每天凌晨 2 点备份康复系统数据库保留最近 30 天 BACKUP_DIR/data/backup/rehab BACKUP_FILE$BACKUP_DIR/rehab_$(date %Y%m%d_%H%M).sql mkdir -p $BACKUP_DIR /usr/bin/mysqldump -u backup_user -ppassword --single-transaction \ --default-character-setutf8mb4 rehab_db $BACKUP_FILE # 删除 30 天前的备份文件 find $BACKUP_DIR -name rehab_*.sql -mtime 30 -exec rm {} \;crontab 加入这一行0 2 * * * /bin/bash /data/backup/rehab/backup.sh /data/backup/rehab/backup.log 21--single-transaction参数对 InnoDB 表非常关键它能在不锁表的情况下做一致性备份康复训练系统白天有人在用数据表如果备份时把表锁住用户这边会突然卡顿。--default-character-setutf8mb4是为了让备份文件里的中文字符不变成转义串。恢复数据时执行mysql -u root -p rehab_db rehab_xxx.sql即可。注意恢复前先确认目标库是空的不然会主键冲突。备份文件建议至少保留 30 天并且定期抽查一次备份能否正常恢复每月要做一个超过 30 天的恢复演练。6. 从课程设计到可交付系统一个 JavaWeb 康复项目的进阶思路6.1 训练方案的动态配置把题组从代码里抽到数据库在 2.1 节里我已经提到系统核心是“筛查、训练、跟踪”但训练方案如果写在 Java 代码里那么每次增加一种题型都要改源码、重新编译、重新部署。实际康复机构的需求远不止固定几套题——医生可能希望为不同认知水平的患者分配不同题目组。我的一般做法是新建一张task_config表把题组数据以 JSON 方式存进去后端按task_version加载。task_config表结构很轻id、task_type、task_name、content_json、level、enable。content_json里放的是题目数组例如数字广度题的数字串和每题的展示秒数。加载题的 Service 不再从静态代码取数据而是查数据库然后把 JSON 解析成TrainingTask对象。这样做的好处是医生可以在后台直接调整难度梯度不用重启 Tomcat。缺点也有JSON 无法用 SQL 强约束出问题要人工排查数据所以我在content_json里追加了一个schema_version字段解析时按版本号兼容。这个方案能让你从“演示项目”走到“可维护项目”同时也是面试时能说上十分钟的亮点。6.2 用监听器实现在线人数与训练时长的统计康复系统上线后运营方想知道“今天有多少人在使用系统”。这个数据用数据库每次都拉训练记录也能算但不够实时。JavaWeb 的 Servlet 监听器是标准且轻量的方案。HttpSessionListener负责在线人数增减ServletContextListener负责初始化统计容器。WebListener public class OnlineCounterListener implements HttpSessionListener { private static int onlineCount 0; Override public void sessionCreated(HttpSessionEvent se) { // 新会话建立在线人数加一 onlineCount; // 存入应用上下文JSP 里可以直接读取 se.getSession().getServletContext().setAttribute(onlineCount, onlineCount); } Override public void sessionDestroyed(HttpSessionEvent se) { onlineCount--; if (onlineCount 0) { onlineCount 0; } se.getSession().getServletContext().setAttribute(onlineCount, onlineCount); } }这里有一个坑Session 过期和用户主动退出是两回事如果要统计准确退出登录时要手动调session.invalidate()触发sessionDestroyed。监听器只能看到 JVM 内部的会话如果未来做了多节点部署这个数字会被每个节点单独统计那时候就要换 Redis 方案了。所以我在项目交付文档里会写清楚监听器方案只适用于单机部署多节点时不要依赖它。在统计训练时长时我会结合业务数据而不用监听器。因为训练时长核心在用户训练动作的开始和结束时间监听器只知道会话存活时间并不知道老人是不是对着页面发呆。正确算法是在训练开始时把时间戳存入 Session提交训练结果时取差值这里就不再给代码了。6.3 打包部署把 JavaWeb 项目发布到云服务器的标准步骤很多人在本地 IDEA 里跑得欢到部署环节就慌。部署其实是最不该慌的环节因为步骤是死的。第一步在 IDEA 右侧 Maven 面板点package生成target/rehab.war。第二步把 war 包上传到服务器 Tomcat 的webapps目录。第三步启动 Tomcat它会自动解压 war 包。访问路径就是http://服务器IP:8080/rehab/注意这里的/rehab默认是 war 包的文件名如果要用自定义路径把 war 包改名为ROOT.war后访问根路径即可。这一步有一个生产环境必须配的文件context.xml。Tomcat 默认对 war 包不会热部署数据库连接池配置如果db.properties里的数据库地址写成localhost服务器上连不到本地 MySQL。建议在项目根目录放一份README-deploy.md部署前把db.properties里的连接地址改成服务器内网 IP并确认云服务器安全组已放行 3306 和 8080 端口。这不是技术门槛纯属流程管理但恰恰是项目交付时最容易扯皮的部分。最后一个进阶技巧把 Tomcat 的端口从 8080 改成 80部署后访问 URL 可以不带端口号会显得正式很多。改法是在conf/server.xml的Connector port8080改成 80同时确保服务器没有其他进程占用。如果是开发机器上跑80 端口需要管理员权限生产环境用 root 启动即可。做完这些后重启 Tomcat用curl -I http://127.0.0.1:80/rehab/自测返回 200 说明成功。我习惯在收官时做一次从零恢复演练把本地数据库删掉用备份脚本恢复把服务器上的整个 Tomcat 目录删掉重新解压 JDK 和 Tomcat、放 war、导入数据库。这套流程走通之后这个系统才算真正交给了别人而不是“在我电脑上能跑”。过程很枯燥但能暴露环境依赖、配置文件遗漏、端口占用等一堆隐藏问题。希望这一次的分享能让你在构建属于自己的阿尔茨海默症早期康复系统时少走几条弯路。本文还有配套的精品资源点击获取
返回列表