基于SpringBoot+Vue3的荣成市健康管理平台设计与实现
源码获取:https://mbd.pub/o/bread/YZWcmJtsZw==
摘要:本文详细介绍了一个基于SpringBoot和Vue3技术栈开发的健康管理平台的完整设计与实现过程。系统采用前后端分离架构,后端使用SpringBoot 2.5.2框架,结合MyBatis-Plus、Spring Security、JWT等技术;前端采用Vue3.2+Element Plus组合。系统实现了用户管理、权限控制、体检预约、健康档案、体检报告生成等核心功能模块,为地区居民提供便捷的健康管理服务。
一、项目背景与意义
1.1 项目背景
随着我国经济社会的快速发展和人民生活水平的不断提高,公众对健康管理的需求日益增长。传统的健康管理模式存在信息孤岛、服务效率低下、数据管理混乱等问题,已无法满足现代医疗服务的需求。特别是在后疫情时代,人们对自身健康状况的关注度显著提升,建立一套完善的健康管理系统具有重要的现实意义。
荣成市作为山东省威海市下辖的县级市,拥有较为完善的医疗卫生资源,但在居民健康管理信息化建设方面仍有提升空间。本项目旨在为荣成市打造一个集健康资讯、体检预约、健康档案管理、体检报告生成于一体的综合性健康管理平台。
1.2 项目意义
-
提升医疗服务效率:通过信息化手段优化体检预约流程,减少居民排队等候时间,提高医疗机构服务效率。
-
实现健康数据统一管理:建立居民电子健康档案,实现健康数据的长期存储和动态更新,为医生诊断提供全面的历史数据支持。
-
促进健康信息共享:打破医疗机构之间的信息壁垒,实现健康信息在授权范围内的安全共享。
-
增强居民健康意识:通过健康资讯发布和健康档案可视化展示,帮助居民更好地了解自身健康状况。
二、技术选型与架构设计
2.1 技术栈概述
本项目采用目前业界主流的前后端分离架构,具体技术选型如下:
2.1.1 后端技术栈
| 技术组件 | 版本 | 用途说明 |
|---|---|---|
| Spring Boot | 2.5.2 | 核心应用框架,简化Spring应用开发 |
| MyBatis-Plus | 3.4.3.1 | ORM框架,简化数据库操作 |
| Spring Security | 2.5.2 | 安全框架,提供认证授权功能 |
| JWT | 3.4.0 | 实现无状态的Token认证机制 |
| MySQL | 8.0.31 | 关系型数据库,存储业务数据 |
| Druid | 1.2.3 | 数据库连接池,提供监控功能 |
| Hutool | 5.7.3 | Java工具类库,简化开发 |
| POI | 4.1.2 | Excel导入导出功能 |
| X-EasyPDF | 2.11.2 | PDF文档生成 |
| 阿里云OSS | 3.10.2 | 文件存储服务 |
2.1.2 前端技术栈
| 技术组件 | 版本 | 用途说明 |
|---|---|---|
| Vue | 3.2.4 | 渐进式JavaScript框架 |
| Vue Router | 4.0.11 | 前端路由管理 |
| Vuex | 4.0.0 | 全局状态管理 |
| Element Plus | 2.3.7 | UI组件库 |
| Axios | 0.21.1 | HTTP客户端 |
| ECharts | 5.1.2 | 数据可视化图表 |
| WangEditor | 4.7.6 | 富文本编辑器 |
| Moment.js | 2.29.4 | 日期时间处理 |
2.2 系统架构设计
2.2.1 整体架构
系统采用经典的三层架构设计:
┌─────────────────────────────────────────────────────────────┐
│ 表现层(Presentation) │
│ Vue3 + Element Plus │
├─────────────────────────────────────────────────────────────┤
│ 业务层(Business) │
│ Controller → Service → Mapper │
├─────────────────────────────────────────────────────────────┤
│ 数据层(Data) │
│ MySQL + 阿里云OSS │
└─────────────────────────────────────────────────────────────┘
2.2.2 后端项目结构
healthBoot/
├── src/main/java/com/example/health/
│ ├── common/ # 公共组件
│ │ ├── config/ # 配置类
│ │ ├── AuthInterceptor.java # JWT拦截器
│ │ └── Result.java # 统一响应结果
│ ├── controller/ # 控制层
│ │ └── dto/ # 数据传输对象
│ ├── entity/ # 实体类
│ ├── enums/ # 枚举类
│ ├── exception/ # 异常处理
│ ├── mapper/ # 数据访问层
│ ├── service/ # 业务逻辑层
│ │ └── impl/ # 实现类
│ ├── utils/ # 工具类
│ └── HealthBootApplication.java # 启动类
├── src/main/resources/
│ ├── mapper/ # MyBatis映射文件
│ └── application.yml # 配置文件
└── pom.xml # Maven配置
2.2.3 前端项目结构
healthVue/
├── public/ # 静态资源
├── src/
│ ├── assets/ # 资源文件
│ │ └── css/ # 全局样式
│ ├── components/ # 公共组件
│ ├── layout/ # 布局组件
│ ├── router/ # 路由配置
│ ├── store/ # Vuex状态管理
│ ├── utils/ # 工具函数
│ ├── views/ # 页面视图
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── package.json # 依赖配置
└── vue.config.js # Vue配置
2.3 数据库设计
系统采用MySQL 8.0作为数据存储方案,共设计16张核心数据表:
2.3.1 用户权限相关表
- user:用户基础信息表,存储用户名、密码、昵称、年龄、性别、地址等
- role:角色表,定义系统角色(管理员、用户、医生、访客)
- permission:权限表,记录系统功能权限
- user_role:用户角色关联表,实现多对多关系
- role_permission:角色权限关联表
2.3.2 体检业务相关表
- check_items:检查项表,定义体检项目基本信息
- check_group:检查组表,将多个检查项组合成组
- check_set_meal:检查套餐表,组合多个检查组
- appointment:预约信息表,记录用户预约详情
- appointment_rule:预约规则表,设置每日可预约人数
- check_items_data:检查项数据表,存储体检结果
- check_files:健康档案表,记录用户健康信息
2.3.3 内容管理相关表
- news:健康资讯表,存储健康文章
- area:区域信息表,用于地址选择
三、核心功能模块详解
3.1 用户认证与权限管理模块
3.1.1 JWT认证机制
系统采用JWT(JSON Web Token)实现无状态的用户认证。用户登录成功后,服务端生成包含用户ID的Token返回给客户端,后续请求通过Header携带Token进行身份验证。
Token生成逻辑:
java
public class TokenUtils {
public static String genToken(User user) {
return JWT.create()
.withAudience(user.getId().toString())
.withExpiresAt(DateUtil.offsetHour(new Date(), 2))
.sign(Algorithm.HMAC256(user.getPassword()));
}
}
Token验证拦截器:
java
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("token");
if (StrUtil.isBlank(token)) {
throw new CustomException("401", "未获取到token, 请重新登录");
}
Integer userId = Integer.valueOf(JWT.decode(token).getAudience().get(0));
User user = userMapper.selectById(userId);
if (user == null) {
throw new CustomException("401", "token不合法");
}
// 验证token签名
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token);
} catch (Exception e) {
throw new CustomException("401", "token不合法");
}
return true;
}
}
3.1.2 RBAC权限模型
系统采用RBAC(Role-Based Access Control)基于角色的访问控制模型,实现细粒度的权限管理:
- 权限(Permission):对应系统的具体功能点,如用户管理、体检预约等
- 角色(Role):权限的集合,如管理员拥有所有权限,普通用户仅有部分权限
- 用户(User):通过分配角色间接获得权限
登录时权限加载流程:
用户登录 → 验证用户名密码 → 查询用户角色 → 查询角色权限 →
组装权限集合 → 生成JWT Token → 返回用户信息和Token
前端动态路由实现:
javascript
// 根据用户权限动态生成路由
function activeRouter() {
const userStr = sessionStorage.getItem("user")
if (userStr) {
const user = JSON.parse(userStr)
let root = {
path: '/',
name: 'Layout',
component: Layout,
redirect: "/home",
children: []
}
user.permissions.forEach(p => {
let obj = {
path: p.path,
name: p.name,
component: () => import("@/views/" + p.name)
};
root.children.push(obj)
})
router.addRoute(root)
}
}
3.2 用户管理模块
3.2.1 用户CRUD操作
用户管理模块提供完整的增删改查功能,支持分页查询和条件搜索:
java
@GetMapping
public Result<?> findPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(defaultValue = "") String search) {
LambdaQueryWrapper<User> wrapper = Wrappers.<User>lambdaQuery()
.orderByAsc(User::getId);
if (StrUtil.isNotBlank(search)) {
wrapper.like(User::getNickName, search);
}
Page<User> userPage = userMapper.findPage(new Page<>(pageNum, pageSize), search);
// 设置用户的角色id列表
for (User record : userPage.getRecords()) {
List<UserRole> roles = roleMapper.getUserRoleByUserId(record.getId());
List<Integer> roleIds = roles.stream()
.map(UserRole::getRoleId)
.distinct()
.collect(Collectors.toList());
record.setRoles(roleIds);
}
return Result.success(userPage);
}
3.2.2 密码加密存储
系统使用BCrypt算法对用户密码进行加密存储,确保密码安全性:
java
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
// 密码加密示例
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
// 密码验证示例
if (!bCryptPasswordEncoder.matches(inputPassword, storedPassword)) {
return Result.error("-1", "密码错误");
}
3.2.3 Excel导入导出
系统支持用户信息的批量导入导出功能,使用Hutool工具库实现:
导出功能:
java
@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = CollUtil.newArrayList();
List<User> all = userMapper.selectList(null);
for (User user : all) {
Map<String, Object> row = new LinkedHashMap<>();
row.put("用户名", user.getUsername());
row.put("昵称", user.getNickName());
row.put("年龄", user.getAge());
row.put("性别", user.getSex());
row.put("地址", user.getAddress());
list.add(row);
}
ExcelWriter writer = ExcelUtil.getWriter(true);
writer.write(list, true);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
String fileName = URLEncoder.encode("用户信息", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
ServletOutputStream out = response.getOutputStream();
writer.flush(out, true);
writer.close();
}
导入功能:
java
@PostMapping("/import")
public Result<?> upload(MultipartFile file) throws IOException {
InputStream inputStream = file.getInputStream();
List<List<Object>> lists = ExcelUtil.getReader(inputStream).read(1);
List<User> saveList = new ArrayList<>();
for (List<Object> row : lists) {
User user = new User();
user.setUsername(row.get(0).toString());
user.setNickName(row.get(1).toString());
user.setAge(Integer.valueOf(row.get(2).toString()));
user.setSex(row.get(3).toString());
user.setAddress(row.get(4).toString());
user.setPassword(bCryptPasswordEncoder.encode("123456"));
saveList.add(user);
}
for (User user : saveList) {
userMapper.insert(user);
// 添加默认角色
UserRole userRole = UserRole.builder()
.userId(user.getId())
.roleId(RoleEnum.USER.getRoleId())
.build();
userRoleMapper.insert(userRole);
}
return Result.success();
}
3.2.4 用户地址统计
系统提供基于用户地址的数据统计功能,按城镇统计用户分布:
java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public Map<String, Long> getUserAddressByTown() {
List<User> userList = userMapper.selectList(null);
HashMap<String, Long> map = new HashMap<>();
userList.forEach(user -> {
String town = getTownByAddress(user.getAddress());
map.put(town, map.getOrDefault(town, 0L) + 1);
});
return map;
}
private String getTownByAddress(String address) {
if (address == null) return "未知";
String[] addStr = address.split("-");
if (addStr.length < 2 || " ".equals(addStr[1])) {
return "未知";
}
return addStr[1];
}
}
3.3 体检预约模块
3.3.1 预约规则管理
管理员可设置每日的预约人数限制,系统支持批量设置和单条设置:
java
/**
* 批量保存规则信息
*/
@PostMapping("/batchSaveAppointment")
public Result<?> batchSaveAppointment(@RequestBody Map map) {
return appointmentService.batchSaveAppointmentRule(map);
}
/**
* 获取当月设置好的规则信息
*/
@PostMapping("/getAppointmentRuleList")
public Result<?> getAppointmentRuleList(AppointmentRule appointmentRule) {
return appointmentService.getAppointmentRuleList(appointmentRule);
}
3.3.2 预约人数校验
用户提交预约时,系统自动校验当日预约人数是否已满:
java
@PostMapping("/validateCount")
public Result<?> validateCount(@RequestBody Map map) throws ParseException {
return appointmentService.validateCount(map);
}
3.3.3 预约流程设计
- 选择体检日期:系统展示可预约日期及剩余名额
- 选择体检套餐:用户可选择预设套餐或自定义检查组
- 填写预约信息:确认预约人信息和联系方式
- 提交预约:系统校验并保存预约信息
- 生成预约凭证:预约成功后生成预约单号
3.4 体检报告模块
3.4.1 检查数据录入
医生可录入用户的体检数据,系统支持批量保存:
java
@Override
public Result<?> batchSaveCheckItemsData(Map map) {
if (map.get("checkItemsListData") == null) {
return Result.error("-1", "参数错误");
}
List<Map> list = (List<Map>) map.get("checkItemsListData");
int saveSize = 0;
for (Map mapEntity : list) {
CheckItemsData checkItemsData = JSON.parseObject(
JSON.toJSONString(mapEntity), CheckItemsData.class);
// 检查是否已存在记录
QueryWrapper<CheckItemsData> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("appointment_id", checkItemsData.getAppointmentId());
queryWrapper.eq("item_id", checkItemsData.getItemId());
CheckItemsData existData = checkItemsDataMapper.selectOne(queryWrapper);
checkItemsData.setCreatePeople((String) map.get("createPeople"));
checkItemsData.setCheckTime(new Date());
if (existData == null) {
saveSize += checkItemsDataMapper.insert(checkItemsData);
} else {
checkItemsData.setId(existData.getId());
saveSize += checkItemsDataMapper.updateById(checkItemsData);
}
}
return Result.success(saveSize);
}
3.4.2 体检报告生成
系统使用X-EasyPDF库生成PDF格式的体检报告:
java
@Slf4j
public class PDFUtils {
public XEasyPdfTemplateDocument document(Appointment appointment, User user, Report report) {
// 创建标题
XEasyPdfTemplateText title = XEasyPdfTemplateHandler.Text.build()
.setId("title")
.setText("体检报告")
.setFontSize("20pt")
.setHorizontalStyle("center");
// 创建扩展文本(打印时间、负责单位)
XEasyPdfTemplateTextExtend extendText = XEasyPdfTemplateHandler.TextExtend.build()
.setFontSize("12pt")
.addText(
XEasyPdfTemplateHandler.Text.build().setText("打印时间:" + DateUtil.today()),
XEasyPdfTemplateHandler.Text.build().setText("负责单位:荣成市健康管理平台")
);
// 创建表格
XEasyPdfTemplateTable table = XEasyPdfTemplateHandler.Table.build()
.setBody(XEasyPdfTemplateHandler.Table.Body.build()
.addRow(this.createPersonHeader(user))
.addRow(this.createMetal(appointment))
.addRow(this.creatBodyList(report))
)
.setMinRowHeight("30pt")
.setVerticalStyle("center");
// 创建页面
XEasyPdfTemplatePage page = XEasyPdfTemplateHandler.Page.build()
.setId("pageId")
.setFontFamily("微软雅黑")
.setFontSize("15pt")
.setMargin("20pt")
.addBodyComponent(title, extendText, table);
return XEasyPdfTemplateHandler.Document.build().addPage(page);
}
}
3.4.3 报告导出功能
java
@Override
public void export(Integer appointmentId, HttpServletResponse response) {
Appointment appointment = appointmentMapper.selectById(appointmentId);
PDFUtils pdfUtils = new PDFUtils();
Report report = this.getCheckReportByUser(appointment);
User user = userMapper.selectById(appointment.getAppointmentPeopleId());
XEasyPdfTemplateDocument document = pdfUtils.document(appointment, user, report);
response.setContentType("application/pdf;charset=utf-8");
String fileName = URLEncoder.encode("体检报告", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".pdf");
ServletOutputStream out = response.getOutputStream();
document.transform(out);
}
3.5 健康档案模块
健康档案模块记录用户的基本健康信息,包括身高、体重、血型、既往病史等,支持历史版本管理:
java
@Entity
@TableName("check_files")
public class CheckFiles {
private Integer id;
private Integer userId; // 用户ID
private String userName; // 用户姓名
private String sex; // 性别
private String age; // 年龄
private String cardNumber; // 身份证号
private String phone; // 电话
private String address; // 住址
private String weight; // 体重
private String height; // 身高
private String bloodType; // 血型
private String medicalHistory; // 既往病史
private String remark; // 说明
private Timestamp createTime; // 创建时间
}
3.6 健康资讯模块
健康资讯模块支持富文本编辑,使用WangEditor实现文章发布功能:
vue
<template>
<div>
<div ref="editor"></div>
<el-button type="primary" @click="save">发布文章</el-button>
</div>
</template>
<script>
import E from "wangeditor";
export default {
data() {
return {
editor: null,
content: ''
}
},
mounted() {
this.editor = new E(this.$refs.editor);
this.editor.config.uploadImgServer = '/api/file/upload';
this.editor.create();
},
methods: {
save() {
this.content = this.editor.txt.html();
// 提交文章内容
}
}
}
</script>
四、关键技术实现
4.1 跨域处理
前后端分离架构下,使用CORS解决跨域问题:
java
@Configuration
public class CorsConfig {
private static final long MAX_AGE = 24 * 60 * 60;
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setMaxAge(MAX_AGE);
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
}
4.2 统一异常处理
使用@ControllerAdvice实现全局异常处理:
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = CustomException.class)
public Result<?> customExceptionHandler(CustomException e) {
log.error("业务异常:", e);
return Result.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(value = Exception.class)
public Result<?> exceptionHandler(Exception e) {
log.error("系统异常:", e);
return Result.error("-1", "系统异常,请联系管理员");
}
}
4.3 统一响应封装
java
@Data
public class Result<T> {
private String code;
private String msg;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode("0");
result.setMsg("成功");
result.setData(data);
return result;
}
public static <T> Result<T> error(String code, String msg) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
4.4 前端请求封装
javascript
import axios from 'axios'
import router from "@/router";
const request = axios.create({
baseURL: "/api",
timeout: 5000
})
// 请求白名单
const whiteUrls = ["/user/login", '/user/register']
// 请求拦截器
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
let userJson = sessionStorage.getItem("user")
if (!whiteUrls.includes(config.url)) {
if(!userJson) {
router.push("/login")
} else {
let user = JSON.parse(userJson);
config.headers['token'] = user.token;
}
}
return config
})
// 响应拦截器
request.interceptors.response.use(response => {
let res = response.data;
if (res.code === '401') {
router.push("/login")
}
return res;
})
export default request
五、系统部署与运行
5.1 环境要求
- JDK:1.8及以上
- Node.js:16.16.0
- MySQL:8.0及以上
- Maven:3.6及以上
5.2 后端部署
- 导入数据库脚本
health_system.sql - 修改
application.yml中的数据库连接配置 - 执行Maven打包命令:
mvn clean package - 运行jar包:
java -jar healthBoot-0.0.1-SNAPSHOT.jar
5.3 前端部署
bash
# 安装依赖
npm install
# 开发环境运行
npm run serve
# 生产环境构建
npm run build
5.4 配置文件说明
application.yml:
yaml
server:
port: 9090
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3308/health_system?useUnicode=true&characterEncoding=utf-8
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
servlet:
multipart:
max-file-size: 100MB
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
六、项目亮点与创新点
6.1 技术亮点
- 前后端分离架构:采用Vue3+SpringBoot前后端分离架构,职责清晰,便于维护
- RBAC权限控制:基于角色的细粒度权限管理,支持动态路由
- JWT无状态认证:使用JWT实现无状态认证,支持分布式部署
- 数据可视化:集成ECharts实现用户分布统计图表
- PDF报告生成:使用X-EasyPDF生成专业体检报告
6.2 业务亮点
- 灵活的预约机制:支持预设套餐和自定义检查组两种预约方式
- 完善的档案管理:支持健康档案的历史版本追溯
- 批量数据操作:支持Excel批量导入导出用户信息
- 富文本编辑器:健康资讯支持图文混排编辑
七、总结与展望
7.1 项目总结
本项目成功实现了一个功能完善的健康管理平台,涵盖了用户管理、权限控制、体检预约、健康档案、体检报告等核心功能。系统采用主流的前后端分离架构,技术选型合理,代码结构清晰,具有良好的可维护性和扩展性。
7.2 未来展望
- 移动端适配:开发微信小程序或App,提升用户体验
- 数据分析:引入大数据分析,提供健康趋势预测
- 智能推荐:基于用户健康数据,智能推荐体检项目
- 消息推送:集成短信和邮件通知,及时提醒预约信息
- 接口优化:引入Redis缓存,提升系统响应速度
八、附录
8.1 项目源码结构
RC-Health-main/
├── healthBoot/ # 后端项目
├── healthVue/ # 前端项目
├── health_system.sql # 数据库脚本
└── README.md # 项目说明
8.2 主要依赖版本
| 依赖 | 版本 |
|---|---|
| Spring Boot | 2.5.2 |
| Vue | 3.2.4 |
| Element Plus | 2.3.7 |
| MyBatis-Plus | 3.4.3.1 |
| MySQL | 8.0.31 |
8.3 参考资料
- Spring Boot官方文档:https://spring.io/projects/spring-boot
- Vue3官方文档:https://v3.vuejs.org/
- Element Plus文档:https://element-plus.org/
- MyBatis-Plus文档:https://baomidou.com/












