1. 项目概述与背景
宠物健康管理在现代社会正变得越来越重要。作为一名养宠人士,我深切体会到定期记录宠物健康状况、及时获取专业医疗建议的需求。传统的纸质记录和线下咨询方式存在诸多不便:健康档案容易丢失、医生资源分布不均、紧急情况难以及时处理。这正是我们开发这套宠物健康顾问系统的初衷。
本系统采用SpringBoot+Vue前后端分离架构,实现了宠物健康档案管理、在线咨询、预约挂号等核心功能。后端基于SpringBoot框架开发,提供稳定的数据接口和业务逻辑处理;前端使用Vue.js构建响应式用户界面,确保良好的交互体验。数据库选用MySQL,保证数据安全性和可靠性。
2. 系统架构设计
2.1 技术选型分析
选择SpringBoot作为后端框架主要基于以下几点考虑:
- 快速开发:SpringBoot的自动配置和起步依赖大大减少了样板代码
- 微服务友好:便于后期扩展为分布式系统
- 生态丰富:与MyBatis、Redis等常用组件集成简单
- 生产就绪:内置健康检查、指标监控等功能
前端选用Vue.js的原因:
- 渐进式框架:可以根据需求灵活扩展功能
- 组件化开发:提高代码复用性和可维护性
- 响应式数据绑定:简化DOM操作,提升开发效率
- 丰富的生态系统:Vue Router、Vuex等配套工具完善
2.2 系统架构图
系统采用典型的三层架构:
- 表现层:Vue.js构建的Web界面
- 业务逻辑层:SpringBoot实现的核心业务处理
- 数据访问层:MyBatis+MySQL的数据持久化方案
前后端通过RESTful API进行通信,使用JWT进行身份认证。这种架构保证了系统的可扩展性和可维护性。
3. 数据库设计与实现
3.1 核心数据表结构
宠物健康档案表(pet_health_record)
sql复制CREATE TABLE `pet_health_record` (
`pet_id` INT NOT NULL AUTO_INCREMENT,
`pet_name` VARCHAR(50) NOT NULL,
`pet_type` VARCHAR(20) NOT NULL,
`pet_age` INT NOT NULL,
`pet_weight` FLOAT NOT NULL,
`vaccine_status` VARCHAR(50),
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`pet_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
设计考虑:
- 使用自增主键简化ID管理
- 非空约束确保数据完整性
- 默认时间戳自动记录创建时间
- utf8mb4字符集支持emoji等特殊字符
在线咨询表(online_consultation)
sql复制CREATE TABLE `online_consultation` (
`consult_id` INT NOT NULL AUTO_INCREMENT,
`user_id` INT NOT NULL,
`doctor_id` INT NOT NULL,
`consult_content` TEXT NOT NULL,
`reply_content` TEXT,
`consult_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`consult_id`),
INDEX `idx_user` (`user_id`),
INDEX `idx_doctor` (`doctor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
优化点:
- 添加索引提高查询效率
- 使用TEXT类型存储可能较长的咨询内容
- 外键关系通过应用层保证
3.2 数据关联设计
系统主要实体关系:
- 用户-宠物:一对多关系
- 用户-咨询:一对多关系
- 医生-咨询:一对多关系
- 宠物-预约:一对多关系
通过合理的索引设计和关联查询优化,确保系统在高并发场景下的性能。
4. 后端核心实现
4.1 SpringBoot应用配置
主启动类配置:
java复制@SpringBootApplication
@MapperScan("com.pethealth.mapper")
public class PetHealthApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(PetHealthApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(PetHealthApplication.class);
}
}
关键配置说明:
@MapperScan指定MyBatis mapper接口路径- 继承
SpringBootServletInitializer支持传统WAR包部署 - 自动配置简化了大部分Spring相关配置
4.2 用户认证模块
JWT认证实现:
java复制@Service
public class TokenService {
private static final String SECRET = "pethealth-secret-key";
private static final long EXPIRE = 3600 * 24 * 7; // 7天
public String generateToken(Long userId, String username, String role) {
Date now = new Date();
Date expireDate = new Date(now.getTime() + EXPIRE * 1000);
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(userId.toString())
.claim("username", username)
.claim("role", role)
.setIssuedAt(now)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
public Claims getClaimsByToken(String token) {
try {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
return null;
}
}
}
安全注意事项:
- 密钥应存储在配置文件中,不应硬编码
- 合理设置token过期时间
- 敏感接口需要角色权限验证
4.3 宠物健康档案服务
核心业务逻辑实现:
java复制@Service
public class PetHealthServiceImpl implements PetHealthService {
@Autowired
private PetHealthMapper petHealthMapper;
@Override
@Transactional
public void createHealthRecord(PetHealthRecord record) {
// 参数校验
if (StringUtils.isBlank(record.getPetName())) {
throw new BusinessException("宠物名称不能为空");
}
if (record.getPetAge() <= 0) {
throw new BusinessException("年龄必须大于0");
}
// 设置创建时间
record.setCreateTime(new Date());
// 插入数据库
petHealthMapper.insert(record);
}
@Override
public PageInfo<PetHealthRecord> queryRecords(Long userId, Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<PetHealthRecord> records = petHealthMapper.selectByUserId(userId);
return new PageInfo<>(records);
}
}
事务处理要点:
@Transactional注解确保操作原子性- 合理的参数校验避免脏数据
- 分页查询使用PageHelper简化实现
5. 前端实现细节
5.1 Vue项目结构
标准项目目录:
code复制src/
├── api/ # API请求封装
├── assets/ # 静态资源
├── components/ # 公共组件
├── router/ # 路由配置
├── store/ # Vuex状态管理
├── utils/ # 工具函数
├── views/ # 页面组件
├── App.vue # 根组件
└── main.js # 应用入口
5.2 宠物档案页面实现
核心组件代码:
vue复制<template>
<div class="pet-health-container">
<el-card>
<div slot="header">
<span>宠物健康档案</span>
<el-button type="primary" @click="showAddDialog">添加档案</el-button>
</div>
<el-table :data="tableData" border>
<el-table-column prop="petName" label="宠物名称"></el-table-column>
<el-table-column prop="petType" label="宠物种类"></el-table-column>
<el-table-column prop="petAge" label="年龄"></el-table-column>
<el-table-column prop="petWeight" label="体重(kg)"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button size="mini" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pagination.current"
:page-sizes="[10, 20, 50]"
:page-size="pagination.size"
layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total">
</el-pagination>
</el-card>
<pet-record-dialog
:visible.sync="dialogVisible"
:form-data="currentRecord"
@submit="handleSubmit">
</pet-record-dialog>
</div>
</template>
<script>
import { getPetRecords, addPetRecord } from '@/api/petHealth'
import PetRecordDialog from './components/PetRecordDialog'
export default {
components: { PetRecordDialog },
data() {
return {
tableData: [],
pagination: {
current: 1,
size: 10,
total: 0
},
dialogVisible: false,
currentRecord: null
}
},
created() {
this.fetchData()
},
methods: {
async fetchData() {
const { current, size } = this.pagination
const res = await getPetRecords({
pageNum: current,
pageSize: size
})
this.tableData = res.data.list
this.pagination.total = res.data.total
},
showAddDialog() {
this.currentRecord = null
this.dialogVisible = true
},
handleEdit(row) {
this.currentRecord = { ...row }
this.dialogVisible = true
},
async handleSubmit(form) {
if (form.petId) {
await updatePetRecord(form)
} else {
await addPetRecord(form)
}
this.dialogVisible = false
this.fetchData()
},
handleSizeChange(size) {
this.pagination.size = size
this.fetchData()
},
handleCurrentChange(current) {
this.pagination.current = current
this.fetchData()
}
}
}
</script>
关键实现技巧:
- 使用Element UI组件快速构建界面
- 分页参数与表格数据绑定
- 对话框组件封装实现复用
- API调用与状态管理分离
6. 系统部署方案
6.1 后端部署
推荐部署方式:
- 打包为可执行JAR:
bash复制mvn clean package
java -jar target/pet-health-1.0.0.jar
- 使用Docker容器化部署:
dockerfile复制FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY target/pet-health-1.0.0.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
生产环境建议:
- 使用Nginx反向代理
- 配置HTTPS加密
- 设置适当的JVM内存参数
- 使用PM2等进程管理工具
6.2 前端部署
构建与部署步骤:
bash复制# 安装依赖
npm install
# 开发环境运行
npm run serve
# 生产环境构建
npm run build
# 部署到Nginx
cp -r dist/* /usr/share/nginx/html/
Nginx配置示例:
nginx复制server {
listen 80;
server_name pethealth.example.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
7. 常见问题与解决方案
7.1 跨域问题处理
SpringBoot解决方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.maxAge(3600);
}
}
开发环境也可通过Vue代理解决:
javascript复制// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
}
7.2 性能优化建议
数据库优化:
- 合理设计索引
- 避免全表扫描
- 使用连接池配置
前端优化:
- 组件懒加载
- 路由按需加载
- 合理使用keep-alive
- 图片等静态资源压缩
7.3 安全性考虑
必备安全措施:
- SQL注入防护:使用预编译语句
- XSS防护:前端转义HTML特殊字符
- CSRF防护:使用token验证
- 敏感数据加密存储
- 接口权限严格控制
8. 项目扩展方向
8.1 功能扩展建议
- 宠物健康数据分析:基于历史数据生成健康报告
- 疫苗提醒服务:根据接种计划自动发送提醒
- 宠物社交功能:用户间分享养宠经验
- 商城模块:整合宠物用品购买功能
8.2 技术升级路径
- 微服务化:拆分为多个独立服务
- 引入消息队列处理异步任务
- 使用Redis缓存热点数据
- 实现Elasticsearch全文检索
- 增加Docker Compose编排
在实际开发过程中,我特别建议重视日志系统的建设。完善的日志可以帮助快速定位问题,特别是在分布式环境中。我们使用Logback配合ELK栈实现了日志的集中管理和分析,这对系统运维和故障排查帮助很大。