☰
SSM中医养生系统毕业设计:可运行+可交付+带论文框架
2026/10/18 10:06:36 网站建设 项目流程

简介:本资源是一套完整的基于SSM框架的中医养生系统毕业设计实现方案,面向计算机专业本科生及Java初学者,解决课程设计、毕设选题与Web全栈开发实践需求。系统采用B/S架构,后端以Java(JDK1.8+)结合Spring+SpringMVC+MyBatis三层框架开发,前端使用JSP配合Bootstrap、Layui、Element UI等主流CSS/JS库实现响应式界面,数据库基于MySQL,配套完整SQL脚本与Navicat/SQLyog兼容结构。压缩包共1509个文件,含147个Java核心业务类、144个JSP页面、390个JS交互脚本、178个PNG/UI图标及160个CSS样式文件,整体41.98MB,结构清晰、模块分明,涵盖用户登录管理、中医知识展示、养生方案推荐等典型功能模块。目前已有143人学习下载,提供可直接导入IDEA或Eclipse运行的Maven工程、数据库建表与初始化脚本、详细技术说明及论文参考框架,助读者快速掌握SSM整合开发全流程与医疗健康类系统设计逻辑。

1. 一个毕业设计能跑通的中医养生系统:不是Demo,是带完整用户流、数据库脚本、论文框架的可交付工程

你有没有遇到过这种场景:导师说“做个SSM项目”,你翻遍GitHub,下载十几个“ssm-xxx”仓库,解压进去一看——只有Controller空壳、Mapper接口没实现、SQL文件里建表语句缺字段、连登录页面都404?这个中医养生系统不是那样。它是一份从IDEA里真能跑起来的毕业设计工程:启动Tomcat后输入http://localhost:8080/login.jsp,输入默认账号admin/123456,立刻进入后台管理页;点击“养生知识管理”,能增删改查带富文本编辑器的图文内容;用户端首页自动轮播节气养生贴士,点击“体质自测”跳转到带逻辑判断的问卷页,提交后生成PDF版体质报告(含建议条目)。它不炫技,但每个模块都闭环:前端JSP用Bootstrap+Layui混搭,后端SSM三层分得清清楚楚,MySQL建库脚本直接执行就能用,连论文目录结构、章节示例、答辩PPT要点都打包在/doc里。适合两类人:一是大四学生急需一个能过中期检查、能演示、能写进简历的SSM实战项目;二是刚转Java的开发者,想拿一个“有血有肉”的老派Web工程,反向吃透Spring MVC请求流转、MyBatis动态SQL、JSP EL表达式与Servlet生命周期怎么咬合。


2. 环境搭建与项目导入:从零配置到首页可访问的实操路径

2.1 JDK、Tomcat、MySQL版本对齐:为什么必须用JDK 1.8 + Tomcat 8.5?

这个项目根目录下的pom.xml明确声明了Spring版本为4.3.28.RELEASE,而Spring 4.3.x系列官方最低要求JDK 1.8,且不兼容JDK 11+的模块化特性。如果你强行用JDK 17打开,编译时会报java.lang.NoClassDefFoundError: javax/servlet/Filter——这不是缺jar包,是Servlet API包名在JDK 9+被移到jakarta.servlet命名空间,而本项目所有web.xml和依赖仍基于javax.*。Tomcat同理:项目webapp/WEB-INF/web.xml中<web-app>标签声明的是version="3.0",这是Servlet 3.0规范,Tomcat 8.5原生支持,而Tomcat 10+强制升级到Servlet 4.0(jakarta.*),会导致DispatcherServlet注册失败,启动日志里满屏ClassNotFoundException。所以请严格按摘要描述操作:

# 检查JDK版本(必须输出1.8.x) java -version # 检查Tomcat版本(必须是8.5.x,如8.5.99) $TOMCAT_HOME/bin/version.sh # MySQL建议用5.7(避免8.0默认strict mode导致insert忽略空字符串报错) mysql --version

提示:若已装高版本JDK,不必卸载。在IDEA中为本项目单独指定JDK 1.8:File → Project Structure → Project → Project SDK → Add JDK → 选择本地jdk1.8路径。

2.2 IDEA导入Maven工程:三步绕过常见编译错误

很多同学解压后双击.iml文件或直接Open folder,结果IDEA疯狂报红:Cannot resolve symbol 'springframework'、web.xml is missing、jsp taglib not found。这是因为SSM老项目依赖结构和现代IDEA默认配置存在代差。正确流程如下:

  1. 关闭自动导入:IDEA启动页点Configure → Settings → Build, Execution, Deployment → Build Tools → Maven,取消勾选Import Maven projects automatically;
  2. 以Maven方式导入:点击Open or Import→ 选择解压后的根目录(含pom.xml的文件夹)→ 勾选Import project from external model → Maven→ Next;
  3. 关键配置补全:
    • 在Project Structure → Modules中,确认Sources标签页下src/main/java和src/main/resources已标记为蓝色(Sources)和绿色(Resources);
    • Web标签页中,点击+添加Web Application,Web resource directory指向src/main/webapp,Web.xml路径填src/main/webapp/WEB-INF/web.xml;
    • Dependencies标签页,确认Maven: org.springframework:spring-webmvc:4.3.28.RELEASE等核心jar已列出。

完成上述步骤后,右键项目 →Maven → Reload project,等待IDEA下载完所有依赖(约3-5分钟),红色波浪线应全部消失。

2.3 数据库初始化:Navicat执行脚本前必做的三件事

项目/sql目录下zhongyi.sql是完整建库脚本,但直接执行常因字符集问题导致中文乱码或插入失败。务必按顺序操作:

  1. 创建数据库时指定编码:
    在Navicat中右键连接 →New Database→ 填写数据库名(如zhongyi_db)→ 字符集选utf8mb4→ 排序规则选utf8mb4_unicode_ci(不是utf8!utf8在MySQL中实际是utf8mb3,不支持emoji和部分生僻汉字);

  2. 修改MySQL全局配置(仅首次需要):
    编辑MySQL配置文件my.cnf(Linux)或my.ini(Windows),在[mysqld]段落下添加:

    character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci

    重启MySQL服务生效;

  3. 执行SQL脚本时禁用安全模式:
    Navicat执行zhongyi.sql前,在SQL窗口顶部菜单栏点Query → Execute Current Statement旁的小齿轮图标 → 取消勾选Safe Updates。否则DELETE FROM user WHERE id=1这类语句会被拦截。

执行成功后,用命令行验证:

USE zhongyi_db; SHOW TABLES; -- 应显示user, article, questionnaire, report等12张表 SELECT username, password FROM user WHERE id=1; -- 应返回 admin / e10adc3949ba59abbe56e057f20f883e(MD5加密的123456)

2.4 启动与验证:如何确认不是“假成功”?

很多人看到Tomcat控制台输出INFO: Server startup in [xxxx] ms就以为成功,结果浏览器打不开。真正验证需三步:

  1. 检查Tomcat日志末尾是否有部署成功标识:
    在IDEA底部Run窗口中,滚动到最底部,查找类似:

    INFO: Starting ProtocolHandler ["http-nio-8080"] INFO: Server startup in 4523 ms INFO: Initializing Spring FrameworkServlet 'dispatcher' INFO: FrameworkServlet 'dispatcher': initialization completed in 1234 ms

    若出现Failed to load bean class或No mapping found for HTTP request,说明Spring MVC未正确加载;

  2. 验证静态资源是否可访问:
    直接浏览器访问http://localhost:8080/css/bootstrap.min.css,应返回CSS文件内容(非404);访问http://localhost:8080/js/jquery.min.js同理。若404,检查webapp目录结构是否为webapp/css/、webapp/js/,而非webapp/static/css/;

  3. 测试登录接口是否通路:
    用Postman发送POST请求:

    URL: http://localhost:8080/login Method: POST Body (x-www-form-urlencoded): username=admin&password=123456

    正确响应应为HTTP 302重定向到/index.jsp,且响应头含Set-Cookie: JSESSIONID=xxx。若返回405(Method Not Allowed),说明web.xml中<servlet-mapping>未将/login映射给dispatcher。


3. 核心模块拆解:从登录认证到体质报告生成的完整链路

3.1 用户登录模块:基于Session的传统认证如何规避CSRF与密码明文传输

本系统未使用Spring Security,而是采用原始Servlet Session管理,但做了基础防护。关键代码在com.controller.LoginController.java:

@Controller public class LoginController { @Autowired private UserService userService; @RequestMapping(value = "/login", method = RequestMethod.POST) public String login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password) { // 1. 防暴力破解:同一IP 5分钟内失败超3次,返回验证码 String ip = getClientIp(request); if (isBlocked(ip)) { request.setAttribute("msg", "尝试次数过多,请输入验证码"); return "login"; // 返回登录页,前端显示验证码框 } // 2. 密码校验:数据库存的是MD5(明文+salt),salt从配置文件读取 String salt = PropertiesUtil.getProperty("password.salt"); // salt=zhongyi2023 String encryptedPwd = DigestUtils.md5Hex(password + salt); User user = userService.findByUsernameAndPassword(username, encryptedPwd); if (user != null) { // 3. 登录成功:清除该IP失败记录,写入Session clearBlockRecord(ip); request.getSession().setAttribute("user", user); request.getSession().setMaxInactiveInterval(1800); // Session超时30分钟 return "redirect:/index.jsp"; } else { // 4. 记录失败:IP+时间戳存入ConcurrentHashMap(内存缓存,非DB) recordLoginFailure(ip); request.setAttribute("msg", "用户名或密码错误"); return "login"; } } }

参数说明与逻辑延伸:

  • getClientIp()方法在com.util.IPUtil.java中,优先取X-Forwarded-For头(兼容Nginx反向代理), fallback到request.getRemoteAddr();
  • isBlocked()使用ConcurrentHashMap<String, List<Long>>存储IP对应失败时间戳列表,每次调用时过滤5分钟前的记录,若剩余数量≥3则返回true;
  • PropertiesUtil读取src/main/resources/config.properties,其中password.salt=zhongyi2023是硬编码盐值,生产环境必须改为随机UUID并存入数据库每用户独立salt;
  • Session超时设为1800秒(30分钟)是平衡安全与体验,若需更严苛,可在web.xml中全局配置<session-config><session-timeout>15</session-timeout></session-config>。

3.2 养生知识管理:JSP+MyBatis实现图文混排内容的CRUD

前端使用Layui富文本编辑器(/js/layedit/layedit.js),后端通过<textarea name="content">接收HTML字符串。关键在于MyBatis处理长文本与XSS过滤:

<!-- mapper/UserMapper.xml --> <insert id="insertArticle" parameterType="Article"> INSERT INTO article (title, author, content, create_time, status) VALUES (#{title}, #{author}, #{content, jdbcType=LONGVARCHAR}, NOW(), #{status}) </insert>

jdbcType=LONGVARCHAR显式声明类型,避免MySQL驱动对TEXT字段的长度猜测错误。但更关键的是入库前的XSS过滤,在com.service.impl.ArticleServiceImpl.java中:

@Service public class ArticleServiceImpl implements ArticleService { @Autowired private ArticleMapper articleMapper; @Override public int insert(Article article) { // 使用Jsoup清洗HTML,只保留p,br,strong,em,img标签及src、alt属性 String safeContent = Jsoup.clean(article.getContent(), Whitelist.relaxed() // 先用宽松策略 .addTags("p", "br", "strong", "em", "img") .addAttributes("img", "src", "alt") .addProtocols("img", "src", "http", "https")); article.setContent(safeContent); return articleMapper.insertArticle(article); } }

注意:Whitelist.relaxed()是Jsoup 1.11.3版本的API,若你用新版Jsoup(2.x),需替换为Sanitizer类,否则编译报错。

3.3 体质自测与报告生成:规则引擎雏形与PDF导出

体质自测问卷共30题,每题4选项(没有、很少、有时、经常),对应分值0/1/2/3。最终得分映射到9种体质类型(平和质、气虚质等)。核心算法在com.service.impl.QuestionnaireServiceImpl.java:

@Override public Report generateReport(List<Answer> answers) { // 1. 按体质维度分组计算(如气虚质含Q1,Q3,Q5...共10题) Map<String, Integer> scoreMap = new HashMap<>(); scoreMap.put("pinghe", calculateScore(answers, Arrays.asList(1,4,7,10,13,16,19,22,25,28))); scoreMap.put("qixu", calculateScore(answers, Arrays.asList(2,5,8,11,14,17,20,23,26,29))); // ... 其他7种体质 // 2. 判定主体质:得分≥17且占总分比≥30%者为偏颇体质 String dominant = "pinghe"; // 默认平和质 int maxScore = 0; for (Map.Entry<String, Integer> entry : scoreMap.entrySet()) { if (entry.getValue() > maxScore && entry.getValue() >= 17) { double ratio = (double) entry.getValue() / 30; // 30题满分 if (ratio >= 0.3) { dominant = entry.getKey(); maxScore = entry.getValue(); } } } // 3. 生成PDF报告(使用iText 5.5.13) Document document = new Document(PageSize.A4, 50, 50, 50, 50); PdfWriter.getInstance(document, new FileOutputStream("report_" + System.currentTimeMillis() + ".pdf")); document.open(); Font titleFont = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", BaseColor.BLACK); document.add(new Paragraph("中医体质分析报告", titleFont)); document.add(new Paragraph("您的主体质为:" + getZhName(dominant), titleFont)); // ... 添加详细建议 document.close(); return new Report(dominant, scoreMap); }

技术细节深挖:

  • calculateScore()方法遍历answers列表,根据题目ID匹配选项分值,此处隐含业务规则:不同体质的题目权重不同,但本项目未实现加权,所有题等权;
  • PDF字体使用STSong-Light(华文宋体),需确保服务器有该字体文件,否则中文乱码。开发时可先用FontFactory.register("simsum.ttc")注册本地宋体;
  • getZhName()是简单switch映射,如case "qixu": return "气虚质";,真实项目应抽离为配置文件或数据库字典表。

4. 避坑指南:那些让开发者凌晨三点还在看日志的典型问题

4.1 现象:登录成功后跳转到/index.jsp,但页面显示“HTTP Status 404 – Not Found”,且Tomcat日志无ERROR

原因:JSP页面中EL表达式${user.username}无法解析,因为web.xml中<web-app>版本声明为2.3或缺失,导致Servlet容器未启用EL。本项目web.xml头为version="3.0",但部分IDEA模板会错误生成2.3。
解决:打开src/main/webapp/WEB-INF/web.xml,确认第一行是:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

若为2.3,需手动升级,并删除<jsp-config>中过时的<el-ignored>false</el-ignored>(3.0+默认开启EL)。

4.2 现象:新增养生文章时,富文本中的图片上传失败,控制台报java.io.FileNotFoundException: /upload/images/xxx.jpg (No such file or directory)

原因:项目未配置文件上传路径,com.controller.ArticleController.java中uploadPath = "D:/zhongyi/upload/images/"是Windows绝对路径,Linux/Mac下不存在该目录,且未做目录创建逻辑。
解决:修改ArticleController中上传路径为相对路径,并增加创建目录代码:

// 替换原uploadPath赋值 String uploadPath = request.getServletContext().getRealPath("/upload/images/"); File uploadDir = new File(uploadPath); if (!uploadDir.exists()) { uploadDir.mkdirs(); // 递归创建目录 } // 后续文件写入使用uploadDir.getAbsolutePath()

4.3 现象:MySQL执行zhongyi.sql时卡住,Navicat状态栏显示“Executing...”长达10分钟

原因:SQL脚本开头有SET FOREIGN_KEY_CHECKS = 0;,但结尾缺少SET FOREIGN_KEY_CHECKS = 1;,导致后续建表因外键约束失败而阻塞。
解决:用文本编辑器打开zhongyi.sql,在最后一行CREATE TABLE report (...) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;之后,手动添加:

SET FOREIGN_KEY_CHECKS = 1;

4.4 现象:Tomcat启动后,访问/login.jsp显示空白页,查看页面源码发现全是<%@ page contentType="text/html;charset=UTF-8" language="java" %>等指令,HTML未渲染

原因:JSP文件编码不是UTF-8。Windows记事本保存的.jsp文件默认ANSI编码,导致JSP引擎解析失败。
解决:用VS Code或Notepad++打开所有.jsp文件(login.jsp,index.jsp等),右下角确认编码显示为UTF-8,若为GBK或ANSI,点击编码名 →Save with Encoding → UTF-8。

4.5 现象:体质报告PDF生成后,中文显示方块,英文正常

原因:iText 5.x对中文字体支持需显式注册,且STSong-Light在Linux服务器上通常不存在。
解决:在generateReport()方法开头添加字体注册(以Ubuntu为例):

// Ubuntu系统常用中文字体 FontFactory.register("/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", "WenQuanYi Micro Hei"); // 然后创建Font时用 Font titleFont = FontFactory.getFont("WenQuanYi Micro Hei", BaseColor.BLACK);

5. 二次开发实战:从“能跑”到“能改”的三个关键改造点

5.1 将JSP页面迁移至Thymeleaf:不重写业务逻辑的渐进式升级

很多同学想把老JSP换成Thymeleaf,又怕动到Service层。其实可以只改View层,利用Thymeleaf的th:fragment复用布局。步骤如下:

  1. 添加Thymeleaf依赖:在pom.xml中加入:

    <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring4</artifactId> <version>3.0.11.RELEASE</version> </dependency>
  2. 配置ViewResolver:在spring-mvc.xml中注释掉原InternalResourceViewResolver,新增:

    <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver"> <property name="characterEncoding" value="UTF-8"/> <property name="order" value="1"/> <property name="templateEngine"> <bean class="org.thymeleaf.spring4.SpringTemplateEngine"> <property name="templateResolvers"> <set> <bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver"> <property name="prefix" value="/WEB-INF/templates/"/> <property name="suffix" value=".html"/> <property name="templateMode" value="HTML"/> <property name="characterEncoding" value="UTF-8"/> </bean> </set> </property> </bean> </property> </bean>
  3. 创建Thymeleaf模板:新建src/main/webapp/WEB-INF/templates/layout.html,定义header、footer等fragment;再建login.html,用<div th:replace="~{layout :: header}"></div>引入。Controller返回字符串"login"即可自动匹配/WEB-INF/templates/login.html。

这样改造后,原有LoginController一行代码不用动,只是return "login"的含义从“转发到/WEB-INF/jsp/login.jsp”变成“渲染/WEB-INF/templates/login.html”。我一般会在迁移前,用Chrome DevTools抓包确认所有AJAX请求URL和参数不变,确保前后端契约零破坏。

5.2 为体质自测增加“倾向体质”分析:用规则引擎替代硬编码

当前generateReport()用if-else判断主体质,但中医理论中常存在“兼夹体质”(如气虚+痰湿)。我们可以引入Drools轻量规则引擎:

  1. 定义Fact对象:新建com.fact.QualityScore.java,包含String qualityType(体质类型)、int score、int totalQuestions;
  2. 编写DRL规则(src/main/resources/rules/quality.drl):
    rule "Determine Dominant Quality" when $q: QualityScore(score >= 17) $all: List() from collect(QualityScore()) then // 计算占比,存入全局Map insert(new DominantQuality($q.getQualityType(), $q.getScore())); end rule "Detect Mixed Quality" when $q1: QualityScore(score >= 12) $q2: QualityScore(score >= 12, qualityType != $q1.qualityType) then insert(new MixedQuality($q1.getQualityType(), $q2.getQualityType())); end
  3. 在Service中触发规则:QuestionnaireServiceImpl注入KieContainer,构建KieSession,insert()所有QualityScore对象,fireAllRules()后从KieSession中getObjects()提取结果。

这样就把原来散落在Java代码里的业务规则,抽离成可独立测试、可热更新的DRL文件。下次导师说“要加个阳虚质判定”,你只需改DRL,不用动Java类。

5.3 数据库优化:为高频查询添加复合索引与读写分离雏形

观察ArticleMapper.xml中selectByCategory方法:

<select id="selectByCategory" resultType="Article"> SELECT * FROM article WHERE category = #{category} AND status = 1 ORDER BY create_time DESC LIMIT #{offset}, #{limit} </select>

当前article表只有PRIMARY KEY(id),category和status字段无索引,当文章量超万条时,分页查询会变慢。优化方案:

  1. 添加复合索引:

    ALTER TABLE article ADD INDEX idx_category_status_ctime (category, status, create_time);

    顺序很重要:category和status是WHERE等值条件,放前面;create_time是ORDER BY字段,放最后,使索引能同时满足查询与排序。

  2. 读写分离雏形(单机多实例):
    修改spring-mybatis.xml中dataSource配置,用AbstractRoutingDataSource实现路由:

    <bean id="dataSource" class="com.util.RoutingDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry key="master" value-ref="masterDataSource"/> <entry key="slave" value-ref="slaveDataSource"/> </map> </property> <property name="defaultTargetDataSource" ref="masterDataSource"/> </bean>

    在RoutingDataSource中,重写determineCurrentLookupKey():SELECT语句返回"slave",INSERT/UPDATE/DELETE返回"master"。虽然本项目没配从库,但代码已预留扩展点——下次加个MySQL从库,只需改配置,不用动DAO。

从那以后我每次接手老项目,都会先花10分钟扫一遍pom.xml的依赖版本,再用mvn dependency:tree | grep spring确认无版本冲突;然后直奔web.xml看Servlet版本,再检查webapp目录结构是否符合Maven标准。这三步做完,80%的“启动失败”问题当场定位。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询