简介本资源是一套面向Java Web初学者与课程设计/毕业设计需求者的家庭健康管理系统源码基于SSMSpringMVC框架搭建后端采用Java前端使用HTML、JSP与JavaScript重点解决家庭成员健康档案与健康指标的管理、上报和报告生成问题适合用来练习MVC分层开发与数据库设计。压缩包共107个文件以34个java源文件、21个jsp页面、17个xml配置为主另有js、css等前端资源以及sql建表脚本、properties配置和md说明文档整体约806KB结构紧凑便于阅读与二次修改。系统覆盖家庭管理、成员管理、指标管理、用户管理、指标上报与健康报告等模块界面部分引入bootstrap及其日期选择组件读者可据此参考需求分析到详细设计的完整实现思路理解实体建模、权限与数据流转并借助sql脚本快速还原数据库、跑通项目。目前已有42人学习浏览适合作为同类健康管理系统的入门参考。1. 家庭健康管理系统拆解一张体检单背后的数据账家里老人量完血压顺手记在纸上三个月后想看看走势纸找不着了。这种事用不上云端健康平台可纯 Excel 又扛不住「多个家庭成员 多项健康指标 按时间轴回溯」这三件事。基于 SSM 框架的家庭健康管理系统正好卡在这个缝里Java 后端 Spring SpringMVC MyBatis前端 HTML JSP jQueryMySQL 存成员档案与指标记录能录入血压、血糖、体重、心率这类日常数据按人、按指标、按时间段拉出变化曲线数值超出参考范围自动打标记。它适合两类人一类是要交课程设计或毕设需要一套分层清楚、能讲明白的 Java Web 工程另一类是想练手 SSM 三层架构拿一个业务不复杂但链路完整的健康管理项目把 Controller、Service、DAO 走通。下面按「结构怎么分、代码怎么写、环境怎么跑、坑在哪、怎么改」的顺序拆。2. 技术栈与工程结构SSM 三件套在这类项目里怎么落位2.1 选型为什么不上 Spring Boot这类健康管理项目多数是从课程作业或毕设长出来的第一诉求不是「先进」而是「每一层都看得见」。Spring Boot 把自动配置藏进 starter初学者排查一个问题要先翻源码SSM 的 XML 配置啰嗦但每个 bean、每个事务切点、每个 Mapper 扫描路径都摆在明面上请求从 DispatcherServlet 进来以后拐了几个弯能一路对上。再加上工程打成 war 丢进 Tomcat 就能跑答辩或演示时换台机器不用重装环境这在实验室机房里是很实际的优势。技术搭配是这样的层次技术常见搭配在这项目里管什么视图HTML / JSP / jQuery / BootstrapJSP 2.x表单录入、列表渲染、图表容器控制Spring MVCSpring 4.x路由映射、参数绑定、JSON 返回业务Spring IoC AOP同上Service 装配、声明式事务持久MyBatis3.xSQL 映射、resultMap 关联查询存储MySQL5.7成员、指标字典、健康记录容器Tomcat8.xwar 部署值得单独说一句的是前端为什么用 JSP 而不是前后端分离。这个项目的页面大多是「查列表 → 填表单 → 提交 → 回列表」用 JSP 加 JSTL 直接渲染少一层接口联调只有趋势图那一块需要 JSON单独开一个ResponseBody接口喂给 ECharts 就够了。分工清楚改起来不打架。2.2 目录结构一个包对应一层别混着装工程是标准的 Maven war 结构src/main/java下按层分包src/main/webapp下放页面和静态资源src/main/resources下放配置和映射文件。这个划分不新鲜但落到具体文件上很多人会乱放比如把 Mapper XML 塞进java目录导致打包丢失或者把jdbc.properties放在 webapp 里被明文下载。family-health/ ├── src/main/java/com/family/health/ │ ├── controller/ # 请求入口只做参数校验和转发 │ │ ├── MemberController.java │ │ ├── RecordController.java │ │ └── LoginController.java │ ├── service/ # 业务规则指标判定写在这里 │ │ ├── MemberService.java │ │ └── impl/MemberServiceImpl.java │ ├── dao/ # 只声明接口SQL 在 XML 里 │ │ ├── MemberMapper.java │ │ └── RecordMapper.java │ ├── entity/ # 与表字段一一对应 │ │ ├── Member.java │ │ ├── Indicator.java │ │ └── HealthRecord.java │ ├── vo/ # 页面用的组合对象如趋势点 │ └── util/ # 日期工具、指标判定工具 ├── src/main/resources/ │ ├── jdbc.properties # 数据源四要素 │ ├── spring-mybatis.xml # 数据源 SqlSessionFactory 事务 │ ├── spring-mvc.xml # 扫描 controller、视图解析器 │ └── mapper/ # 与 dao 接口同名的 XML ├── src/main/webapp/ │ ├── WEB-INF/ │ │ ├── web.xml │ │ └── views/ # jsp 页面 │ └── static/ # js、css、图片 └── pom.xmldao接口和mapper下的 XML 靠「同包名 同类名」的约定绑定前提是mybatis-config里配了mapperLocations。这一点如果不配启动时不报错调用时才抛BindingException: Invalid bound statement属于典型的延迟爆炸。我一般会在spring-mybatis.xml里显式写死路径!-- spring-mybatis.xml 片段 -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ !-- 关键告诉 MyBatis 去哪里找 Mapper XML路径必须精确到 *.xml -- property namemapperLocations valueclasspath:mapper/*.xml/ !-- 实体包别名XML 里 resultType 可以直接写 Member -- property nametypeAliasesPackage valuecom.family.health.entity/ /bean !-- DAO 接口批量扫描省去一个个写 MapperFactoryBean -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.family.health.dao/ /bean !-- 声明式事务Service 层方法抛异常回滚 -- bean idtxManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertxManager/mapperLocations里的classpath:前缀不能省写成mapper/*.xml在某些容器下会解析到 webapp 目录去结果就是 XML 明明在包里却加载不到。typeAliasesPackage是省事用的代价是同一个类名不能重复工程小的时候无所谓。事务管理器认的是dataSource所以数据源 bean 的 id 必须和这里ref一致改名的时候两处一起改。2.3 三处必改配置web.xml、数据源、视图解析接到一个 SSM 工程先别急着跑把这三处对齐基本能省掉一半的启动问题。web.xml决定请求怎么被接住数据源决定连不连得上视图解析决定页面出不出得来。!-- web.xml 核心片段 -- servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class !-- 指定 MVC 配置文件不写默认找 /WEB-INF/dispatcher-servlet.xml -- init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name !-- 拦截 / 但放过静态资源配合 mvc:resources 使用 -- url-pattern//url-pattern /servlet-mapping !-- Spring 根容器加载 Service、数据源、事务 -- context-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mybatis.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener !-- 编码过滤器中文乱码的头号源头 -- filter filter-nameencoding/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-paramparam-nameencoding/param-nameparam-valueUTF-8/param-value/init-param init-paramparam-nameforceEncoding/param-nameparam-valuetrue/param-value/init-param /filter filter-mapping filter-nameencoding/filter-name url-pattern/*/url-pattern /filter-mapping这里有两个容器ContextLoaderListener起的是根容器管 Service、DAO、数据源DispatcherServlet起的是子容器管 Controller。子容器能看到父容器的 bean反过来不行——所以 Controller 里Autowired一个 Service 没问题但如果在 Service 里注入 Controller 就会拿到 null。url-pattern写/而不是/*是为了给静态资源留出交由默认 servlet 处理的空间配/*会把 css、js 一起卷进 DispatcherServlet页面直接白板。jdbc.properties四项加一个时区MySQL 8 的驱动不写时区会报The server time zone value is unrecognizedjdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/family_health?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.password你的密码useUnicode和characterEncoding是给连接层用的和web.xml里的编码过滤器是两回事两边都要设。serverTimezone在 MySQL 5.7 驱动上可以省8.x 上基本必写。视图解析器放在spring-mvc.xmlbean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /bean !-- 静态资源放行 -- mvc:resources mapping/static/** location/static//prefix指向WEB-INF下是为了让 JSP 不能被浏览器直连访问必须走 Controller。这个细节在做权限控制时很关键——如果页面放在 webapp 根目录用户绕过登录直接敲 URL 就能看到数据。3. 核心模块拆解健康指标从录入到趋势图的完整链路3.1 表结构四张表撑起健康管理这个项目的业务不复杂但表必须切干净不然后面加指标就得改代码。核心是四张表账号表、成员表、指标字典表、记录表。指标字典单独拆出来是为了让「血压、血糖、体重、心率、血氧」这些项能增删而不是把字段硬编码在实体里。-- 家庭成员表一个账号可以管多个成员老人、孩子 CREATE TABLE t_member ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL COMMENT 所属账号, name VARCHAR(32) NOT NULL, gender TINYINT DEFAULT 1 COMMENT 1男 2女, birth_date DATE, relation VARCHAR(16) COMMENT 父亲/母亲/本人/子女, height DECIMAL(5,1) COMMENT 身高cm用于算BMI, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 指标字典表参考范围存在这里判定逻辑就不用写死 CREATE TABLE t_indicator ( id INT PRIMARY KEY AUTO_INCREMENT, code VARCHAR(32) NOT NULL UNIQUE COMMENT 如 sbp/dbp/glu/weight, name VARCHAR(32) NOT NULL, unit VARCHAR(16), ref_min DECIMAL(8,2) COMMENT 参考下限, ref_max DECIMAL(8,2) COMMENT 参考上限, sort_no INT DEFAULT 0 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 健康记录表一条记录 一个成员 一个指标 一个时间点 CREATE TABLE t_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, member_id INT NOT NULL, indicator_code VARCHAR(32) NOT NULL, value DECIMAL(8,2) NOT NULL, status TINYINT DEFAULT 1 COMMENT 0偏低 1正常 2偏高, measure_time DATETIME NOT NULL, remark VARCHAR(255), KEY idx_member_ind_time (member_id, indicator_code, measure_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;t_record上那个联合索引(member_id, indicator_code, measure_time)是给趋势图查询用的。页面最常打的一条 SQL 是「某个成员在某个时间段内某项指标的全部值按时间升序」顺序刚好和索引一致能走索引覆盖。不建这个索引记录攒到几千条以后图会明显变慢这是我自己踩过的。status冗余存一份是有意的判定规则以后可能要改比如不同年龄段阈值不同如果每次都实时算历史记录的状态会跟着变用户翻旧数据会发现「上个月显示偏高现在变正常了」很迷惑。所以录入时算一次落库改规则时只影响新数据老数据保持当时的判断。3.2 实体与 Mapper一个成员带出最近一次记录成员列表页要显示「最近一次测量时间和数值」典型的一对一关联。用 MyBatis 的resultMap加association解决比在 Java 里循环查 N 次干净得多。// MemberVO成员 最近一条记录供列表页使用 public class MemberVO extends Member { private String lastIndicatorName; private BigDecimal lastValue; private Date lastTime; // getter / setter 省略 }!-- MemberMapper.xml -- resultMap idmemberVOMap typecom.family.health.vo.MemberVO id propertyid columnid/ result propertyname columnname/ result propertyrelation columnrelation/ result propertybirthDate columnbirth_date/ !-- 最近一次记录子查询只取一条 -- association propertylastValue columnid selectcom.family.health.dao.RecordMapper.selectLatestValue/ /resultMap select idlistByUser resultMapmemberVOMap SELECT id, name, relation, birth_date FROM t_member WHERE user_id #{userId} ORDER BY id /select!-- RecordMapper.xml关联查询的目标语句 -- select idselectLatestValue resultTypejava.math.BigDecimal SELECT r.value FROM t_record r WHERE r.member_id #{id} ORDER BY r.measure_time DESC LIMIT 1 /selectassociation的select属性走的是嵌套查询好处是 SQL 简单、能复用代价是 N1——列表有 10 个成员就发 11 条 SQL。家庭成员量级很小这点开销可以接受如果换成设备上报数据就得改成LEFT JOIN加窗口函数或者子查询取 max一次查完。提示association的column写的是传给子查询的参数列名不是属性名。写成memberId而 SQL 里别名是id子查询会收到 null结果全是空值不报错很难发现。3.3 Service 层指标判定与事务边界判定逻辑放在 Service不放在 Controller 也不放在 SQL 里。原因很直接录入时要用决定 status查询统计时要用算异常占比如果写两遍迟早不一致。Service public class RecordServiceImpl implements RecordService { Autowired private RecordMapper recordMapper; Autowired private IndicatorMapper indicatorMapper; /** * 录入一条健康指标记录 * param record 前端传入含 memberId、indicatorCode、value、measureTime * return 落库后的记录status 已回填 */ Override Transactional(rollbackFor Exception.class) public HealthRecord save(HealthRecord record) { // 1. 取出指标字典拿参考范围 Indicator ind indicatorMapper.selectByCode(record.getIndicatorCode()); if (ind null) { throw new IllegalArgumentException(未知指标编码 record.getIndicatorCode()); } // 2. 判定状态低于下限 0高于上限 2其余 1 BigDecimal v record.getValue(); int status 1; if (ind.getRefMin() ! null v.compareTo(ind.getRefMin()) 0) { status 0; } else if (ind.getRefMax() ! null v.compareTo(ind.getRefMax()) 0) { status 2; } record.setStatus(status); // 3. 测点时间没传就用当前时间避免插入 null 后排序错乱 if (record.getMeasureTime() null) { record.setMeasureTime(new Date()); } recordMapper.insert(record); return record; } }BigDecimal比较必须用compareTo用equals会连 scale 一起比5.0和5.00返回 false判定直接翻车。Transactional加rollbackFor Exception.class是因为 Spring 默认只对RuntimeException回滚抛受检异常时事务不回滚脏数据就留在库里了。measureTime的兜底也很重要。前端用日期控件传值如果用户没选绑定到Date上是 null插入后这一行在ORDER BY measure_time DESC里会跑到最前面或最后面取决于数据库趋势图的时间轴就乱了。宁可在这里补当前时间。3.4 Controller 与前端给 ECharts 喂一份 JSON趋势图那块不需要页面刷新单独开一个接口返回数组前端用 ajax 拉回来直接塞给 ECharts。Controller 只做参数接收和格式转换不做业务判断。Controller RequestMapping(/record) public class RecordController { Autowired private RecordService recordService; /** * 趋势图数据接口 * param memberId 成员 id * param code 指标编码如 sbp * param days 最近天数默认 30 */ RequestMapping(/trend) ResponseBody public MapString, Object trend(RequestParam Integer memberId, RequestParam String code, RequestParam(defaultValue 30) Integer days) { // 起止时间在 Service 里算Controller 不碰业务 ListHealthRecord list recordService.listTrend(memberId, code, days); ListString axis new ArrayList(); ListBigDecimal data new ArrayList(); SimpleDateFormat fmt new SimpleDateFormat(MM-dd); for (HealthRecord r : list) { axis.add(fmt.format(r.getMeasureTime())); data.add(r.getValue()); } MapString, Object result new HashMap(); result.put(axis, axis); result.put(data, data); return result; } }ResponseBody依赖spring-mvc.xml里的mvc:annotation-driven/少了这行返回值会被当成视图名去解析浏览器报 404 而不是返回 JSON——这是个高频误判点看日志会以为是路径写错了。前端拿数据// 页面加载时按成员和指标拉趋势 function loadTrend(memberId, code) { $.get(/record/trend, {memberId: memberId, code: code, days: 30}, function (res) { var chart echarts.init(document.getElementById(trendBox)); chart.setOption({ xAxis: {type: category, data: res.axis}, yAxis: {type: value}, series: [{type: line, data: res.data, smooth: true}] }); }, json); }days默认值 30 靠defaultValue兜底前端不传也不会 400。ECharts 容器必须有明确高度只写宽度不写高度时图渲染不出来控制台无报错纯白——第一次遇到能查半天。4. 跑起来环境准备、建库到部署的完整步骤4.1 环境清单与版本对齐跑 SSM 老工程最省事的办法是「按它出生的年代配环境」别硬上新版本。常见搭配是 JDK 8、Maven 3.6、Tomcat 8.5、MySQL 5.7 或 8.0、IDEA 社区版以上。组件建议版本检查命令常见问题JDK8java -version11/17 编译报package javax.servlet不存在Maven3.6mvn -v镜像未配依赖拉半小时Tomcat8.5启动日志版本号10.x 把 javax 换成 jakarta全崩MySQL5.7 / 8.0mysql -V8.0 要额外配时区与驱动类名Tomcat 10 是最容易踩的它把javax.servlet.*全部改成jakarta.servlet.*而这个工程的pom.xml里依赖的是老坐标编译能过部署时ClassNotFoundException一堆。要么降到 8.5要么整体迁移前者五分钟后者半天。4.2 建库与导入初始数据先建库再导 SQL字符集统一utf8mb4不然成员姓名里有生僻字会变成问号。# 1. 登录并建库 mysql -u root -p -e CREATE DATABASE family_health DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; # 2. 导入结构与初始数据脚本里一般带建表语句和指标字典 mysql -u root -p family_health sql/family_health.sql # 3. 确认字典表有数据没有的话判定逻辑会全部抛「未知指标」 mysql -u root -p -e SELECT code, name, ref_min, ref_max FROM family_health.t_indicator;第三步容易被跳过。有些工程的 SQL 脚本只建表不插字典跑起来后录第一条记录就抛IllegalArgumentException看着像代码 bug其实是数据没初始化。字典表至少要塞进收缩压90–140、舒张压60–90、空腹血糖3.9–6.1、体重、心率这几项。4.3 改配置并打包拿到工程后要改的地方集中在配置文件不改的话连不上库。# src/main/resources/jdbc.properties jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://127.0.0.1:3306/family_health?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password改成你自己的com.mysql.cj.jdbc.Driver对应 MySQL 8 的mysql-connector-java 8.x如果pom.xml里是 5.1.x 的依赖驱动类要写回com.mysql.jdbc.Driver否则启动报ClassNotFoundException。allowPublicKeyRetrievaltrue是 8.0 默认加密方式下本地连接常需要的参数缺了会报Public Key Retrieval is not allowed。改完配置打包# 跳过测试打包产出 target/*.war mvn clean package -DskipTests # 把 war 拷进 Tomcat 的 webapps重命名为 ROOT.war 可省掉一级路径 cp target/family-health.war $TOMCAT_HOME/webapps/ $TOMCAT_HOME/bin/startup.sh-DskipTests是因为这类工程里的测试类经常是空的或者依赖本地数据库跑了反而失败。生产项目别这么干这是本地验证的省事做法。4.4 启动验证按依赖顺序看日志启动不是「没报错就算成功」要按catalina.out里的顺序确认三件事根容器加载完、数据源连上、Mapper 绑定成功。# 盯日志里的关键行 tail -f $TOMCAT_HOME/logs/catalina.out | grep -E Root WebApplicationContext|Initializing|Mapped|BindingException看到Root WebApplicationContext: initialization completed说明 Service 那层起来了出现Mapped {[/record/trend]}说明 Controller 映射成功如果刷出Invalid bound statement (not found)就是 2.2 节说的mapperLocations没配或路径写错。三条都过之后浏览器访问http://localhost:8080/family-health/login用脚本里的初始化账号登入先建一个成员再录一条血压回列表看status是不是按参考范围标了色。这条链路通了后面改功能才有意义。5. 避坑排查SSM 老项目最容易翻车的五个地方5.1 启动报 404日志里一条映射都没有现象Tomcat 启动正常访问任何页面都是 404catalina.out里搜不到Mapped。原因web.xml里DispatcherServlet的contextConfigLocation指错了或者spring-mvc.xml里的context:component-scan base-package...扫的包名和实际不一致导致 Controller 根本没被注册。解决先看spring-mvc.xml的扫描路径是不是覆盖到com.family.health.controller再看web.xml的init-param是不是写成了classpath*:spring-mvc.xml之外的位置。判断依据是日志里有没有Detected RequestMapping之类的行没有就是没扫到。5.2 表单提交中文全变问号现象页面输入的中文姓名存进库变成???从库里读出来也乱。原因三个环节任一没设 UTF-8 都会出问题——页面% page contentTypetext/html;charsetUTF-8 %、web.xml的CharacterEncodingFilter、数据库连接的characterEncodingutf8还有表本身的字符集。解决从后往前查。先SHOW CREATE TABLE t_member看是不是utf8mb4再看jdbc.url有没有characterEncoding最后看过滤器是不是forceEncodingtrue。forceEncoding很重要只设encoding的话响应流的编码不会被强制GET 请求的参数还是会乱。5.3 一对多查询只返回一条现象一个成员有多条记录用collection查出来列表里只有第一条。原因resultMap里的id标签用错了列。MyBatis 靠id判断是不是同一行如果collection关联的resultMap里把主表的 id 当成了id多条子记录会被合并成一条。解决主表和子表的resultMap各自用各自的主键列做id子表用t_record.id主表用t_member.id。另外检查 SQL 里有没有LIMIT 1被顺手带进来了。5.4 页面上的日期后面多个「.0」现象2024-05-12 08:30:00.0时间后面的毫秒位多出来一截看着很业余。原因java.util.Date在 JSP 上用${record.measureTime}直接输出时走的是toString()带毫秒。解决用 JSTL 的fmt:formatDate value${record.measureTime} patternyyyy-MM-dd HH:mm/输出。或者干脆在实体里换成java.time.LocalDateTime但老工程里Date用得多改起来牵连面大格式化更稳。5.5 静态资源全部 404页面裸奔现象JSP 出来了但 css、js 全丢控制台一堆 404。原因DispatcherServlet的url-pattern写成了/静态资源请求也被它接管但没配mvc:resources找不到处理器。解决在spring-mvc.xml里加mvc:resources mapping/static/** location/static//同时确认mvc:annotation-driven/存在。如果页面路径是/css/app.css这种直接挂在根下的mapping 要相应改成/css/**。6. 二次开发把指标阈值和成员隔离抽出来原始工程里最该改的两处一是指标参考范围二是数据归属。参考范围现在存在t_indicator表里已经比硬编码好但它只有一组全局阈值——老人和小孩的正常血压不一样一套值套全家迟早出问题。比较顺手的改法是加一张t_indicator_ref按年龄段分段字段类型说明indicator_codeVARCHAR(32)关联字典表age_min / age_maxINT年龄段左闭右开genderTINYINT0 不限1 男2 女ref_min / ref_maxDECIMAL(8,2)该段参考范围Service 里判定时先按成员出生日期算年龄再按年龄和性别去查阈值查不到就回落到字典表的默认值// 按年龄取阈值取不到就用指标字典的默认范围 IndicatorRef ref refMapper.selectByAge(code, age, gender); BigDecimal min ref ! null ? ref.getRefMin() : ind.getRefMin(); BigDecimal max ref ! null ? ref.getRefMax() : ind.getRefMax();另一处是数据归属。现在listByUser(userId)靠前端传的userId登录后从 session 取但成员详情、记录删除这些接口如果没校验memberId是不是当前账号的改个 id 就能看别人家的数据。我一般加一个HandlerInterceptor在preHandle里统一校验成员归属校验不过直接返回 403比在每个 Controller 里写一遍 if 靠谱。验证方法就三条注册两个账号各建一个成员用 A 的会话去请求 B 的memberId应该被拦给同一个指标配两组年龄段阈值用不同年龄的成员各录一条相同数值status应该不同把旧数据翻出来看历史记录的status不应该因为改了阈值表而变化。从那以后我每次接手这类 SSM 老项目第一件事都是先把阈值、角色、数据归属这三样从代码里抽出来看一眼抽不出来的就先记一笔——不然后面每加一个功能都得回来补一遍。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
论文写作神器PaperXie:智能排版与选题推荐全解析 1. 论文写作痛点与工具需求分析每年毕业季,数百万本科生都会陷入相同的困境——论文格式调整耗费的时间远超内容创作本身。从页眉页脚到参考文献排版,从目录生成到标题编号,这些机械性工作往往要吃掉学生50%以上的论文写作时间。更糟的是&… · 2026/11/10 19:30:20
GB/T 45314-2025车载紧急呼叫系统语音测试标准解析 1. 项目概述GB/T 45314-2025作为我国车载紧急呼叫系统(AECS)领域的最新国家标准,将于2025年正式实施。这份标准特别针对语音测试环节提出了系统性的技术要求,是保障车载紧急呼叫功能可靠性的关键规范。作为从业十余年的汽车电子工… · 2026/11/5 0:47:55
电力负荷预测技术演进与多源数据融合实践 1. 电力负荷预测技术演进与新型系统挑战十年前我刚入行时,负荷预测还停留在简单的线性回归和人工经验修正阶段。如今走进任何一家省级调度中心,大屏上跳动的实时预测曲线背后,是融合了时空特征挖掘、多源数据融合的智能算法集群。这个变化背后… · 2026/11/5 8:25:54
湛江霞山电工证考试保姆级教程:学历不高也能考,关键看这几点 湛江霞山电工证考试保姆级教程:学历不高也能考,关键看这几点 学历不高怕报不上名?湛江霞山电工证考试其实并不难,关键是你怎么准备。别再被那些“学历不够不能考”的话骗了, 很多没上过大学的人也顺利考过电工证… · 2026/11/10 19:29:21
宣传电工证文案到期换证流程 电工证到期换证流程全攻略,怕考不过白交培训费?电子证书怎么搞懂? 怕考不过白交培训费?现在考电工证,很多人就怕花了钱,最后因为操作不当或者复习不到位,考试挂了,钱白花了。尤其是现在推行电子证书后,流程和标准都变了,很多人一不小心就掉进坑里。… · 2026/11/10 19:29:21
湛江霞山电工证考试多久拿证?外省证能不能转过来 湛江霞山电工证考试多久拿证?外省证能不能转过来 之前考的证在外省能不能转过来?这几乎是所有准备考湛江霞山电工证的人的第一反应,尤其是在工地、施工现场或者建筑公司工作的朋友,手里可能拿着外省的电工证,但想在湛江霞山本地工作,就不得不面对证书是… · 2026/11/10 19:29:21
宣传电工证文案模拟考试题库 外省电工证怎么转?看这位驻马店电工的真实经历 之前考的证在外省能不能转过来?这个问了不下十个人,但真正了解的人寥寥无几。今天我就拿自己在 驻马店公路工程 从业的真实经历来说说,看看外省电工证怎么转、要不要重新考,还有哪些你想不到的坑。… · 2026/11/10 19:29:21
考电工证请问怎么考 怎么考才能不被学历卡住 考电工证请问怎么考 怎么考才能不被学历卡住 你是不是也这样,想考电工证但又担心自己学历不够,怕报不上名?别急,其实大多数电工证考试对学历要求并不高,关键是你得知道 在哪考… · 2026/11/10 19:29:21
郑州考电工证金水区学历不高怕报不上名?电子证书怎么查? 郑州考电工证金水区学历不高怕报不上名?电子证书怎么查? 你是不是也担心自己学历不高,报不了电工证?别急,郑州金水区的电工证考试,其实没有你想象的那么难。今天咱们就来聊一聊 郑州考电工证金水区 的那些事,特别是关于 电子证书… · 2026/11/10 19:28:21
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/10 2:16:10
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/10 17:46:39
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/10 17:11:43
丹寨电工证怎么考?内部消息透露报考条件和流程 丹寨电工证怎么考?内部消息透露报考条件和流程 急需特种作业证才能上岗找工作,这是很多电工朋友最真实的焦虑。尤其是像丹寨电工证这样的证书,直接影响你的职业发展和薪资待遇。但你知道吗?很多人都在报考过程中踩坑,有的甚至花了几千块最后白忙一场。今… · 2026/11/10 0:00:13
成都建筑电工证办理郑州报考避坑指南 成都建筑电工证办理郑州报考避坑指南 证书过期了不知道怎么复审延期?别慌!这篇文章手把手带你解决成都建筑电工证办理的难题,顺便避掉郑州报考的那些坑。不管你是刚拿到证的新手,还是准备复审的老电工,这波干货都别错过。… · 2026/11/10 0:00:13
梅州低压电工证怎么考?怕被中介坑?手把手教你全国通用流程 梅州低压电工证怎么考?怕被中介坑?手把手教你全国通用流程 你是不是正为梅州低压电工证发愁?不知道去哪报名,怕被中介骗钱,又担心证书是不是全国通用?其实,这些顾虑都是正常人会有的。今天我就从一个在开封房建工程一线干了几年的电工角度,手把手教你… · 2026/11/10 0:00:13
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 11:23:35
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 19:22:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 17:00:47