1. 项目概述:全栈宠物健康管理解决方案
这个宠物健康顾问系统是我去年为本地连锁宠物医院开发的核心业务平台,采用当下主流的前后端分离架构。系统上线后帮助医院客户留存率提升了40%,最让我意外的是后台数据分析功能让兽医们发现了季节性宠物皮肤病的发病规律。整套技术栈选型经过多次推敲:SpringBoot提供稳健的后端服务,Vue3负责灵活的前端交互,MyBatis-Plus作为数据访问层桥梁,配合MySQL8.0的JSON字段支持,完美处理了宠物健康档案中的非结构化数据。
关键设计原则:将复杂的健康评估算法封装成微服务,前端通过可视化图表展示健康趋势,兽医工作站采用WebSocket实现实时问诊提醒。
2. 技术架构深度解析
2.1 后端技术栈设计
SpringBoot 2.7采用多模块设计:
pet-core基础组件模块pet-admin管理后台模块pet-api移动端接口模块pet-job定时任务模块
特别配置的MyBatis-Plus动态数据源,支持主从分离:
@Configuration @MapperScan("com.pet.**.mapper") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor()); return interceptor; } }2.2 前端工程化实践
Vue3组合式API带来的代码组织优势:
// 健康记录组件 const { petId } = useRoute().params const healthData = ref([]) onMounted(async () => { const res = await getHealthRecords(petId) healthData.value = res.data.map(item => ({ ...item, vaccineStatus: formatVaccine(item.vaccines) })) })采用Pinia状态管理解决跨组件数据共享:
// stores/alert.js export const useAlertStore = defineStore('alert', { state: () => ({ unreadCount: 0 }), actions: { async fetchCount() { this.unreadCount = await api.getUnreadAlerts() } } })3. 核心业务模块实现
3.1 健康评估算法引擎
基于规则引擎Drools实现的健康评分系统:
rule "WeightEvaluation" when $p : Pet(age > 5, weight > standardWeight*1.3) then insert(new HealthAlert("肥胖风险", $p.getId())); end配套的MySQL函数计算BMI指数:
CREATE FUNCTION calc_pet_bmi(weight_kg DECIMAL(5,2), height_cm INT) RETURNS DECIMAL(5,2) DETERMINISTIC BEGIN RETURN weight_kg / POW(height_cm/100, 2); END3.2 问诊日历系统
使用FullCalendar实现的交互式问诊排班:
const calendar = new Calendar(calendarEl, { initialView: 'timeGridWeek', slotMinTime: '09:00:00', events: async (info, successCallback) => { const res = await fetchAppointments(info.start, info.end) successCallback(res.data) } })后端采用弹性时间桶算法防止预约冲突:
public List<TimeSlot> generateSlots(LocalDate date, int duration) { List<Appointment> exists = appointmentMapper.selectByDate(date); return TimeBucketGenerator.generate( LocalTime.of(9, 0), LocalTime.of(18, 0), Duration.ofMinutes(duration), exists.stream().map(Appointment::getTime).collect(Collectors.toList()) ); }4. 数据库优化实践
4.1 宠物档案表设计
CREATE TABLE `pet_profile` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `owner_id` BIGINT NOT NULL, `name` VARCHAR(20) NOT NULL, `species` ENUM('DOG','CAT','OTHER') NOT NULL, `birth_date` DATE, `weight_history` JSON DEFAULT NULL, `medical_notes` TEXT, `qr_code` VARCHAR(100) UNIQUE, PRIMARY KEY (`id`), INDEX `idx_owner` (`owner_id`), INDEX `idx_species` (`species`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;4.2 查询性能优化方案
- 为高频查询添加覆盖索引:
ALTER TABLE health_record ADD INDEX idx_pet_date (pet_id, record_date) INCLUDE (temperature, weight);- 使用窗口函数优化报表查询:
SELECT pet_id, record_date, weight, AVG(weight) OVER (PARTITION BY pet_id ORDER BY record_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM health_record WHERE pet_id = 123;5. 部署与监控方案
5.1 Docker Compose编排
version: '3.8' services: backend: build: ./pet-server ports: - "8080:8080" depends_on: - redis - mysql environment: SPRING_PROFILES_ACTIVE: prod frontend: build: ./pet-web ports: - "80:80" volumes: - ./pet-web/dist:/usr/share/nginx/html mysql: image: mysql:8.0 volumes: - mysql_data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: pet@12345.2 Prometheus监控配置
SpringBoot Actuator端点暴露:
management.endpoints.web.exposure.include=health,info,metrics,prometheus management.metrics.tags.application=${spring.application.name}Grafana监控看板关键指标:
- JVM内存使用率
- MySQL连接池活跃数
- API接口响应时间P99
- 健康评估请求QPS
6. 典型问题排查实录
6.1 N+1查询问题
症状:获取宠物列表时产生大量冗余查询
解决方案:
- MyBatis-Plus开启SQL打印
- 添加@TableField(select = false)延迟加载非必要字段
- 使用@Select注解手动编写联查SQL
6.2 前端内存泄漏
现象:切换路由后DOM节点未释放
排查步骤:
- Chrome DevTools录制内存快照
- 发现被keep-alive缓存的组件
- 配置include属性明确缓存组件:
<router-view v-slot="{ Component }"> <keep-alive :include="['PetDetail']"> <component :is="Component" /> </keep-alive> </router-view>7. 安全防护措施
7.1 接口安全设计
- Spring Security JWT过滤链配置:
http.addFilterBefore( new JwtAuthenticationFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class );- 接口幂等性控制:
@Idempotent @PostMapping("/vaccine") public Result recordVaccine(@RequestBody @Valid VaccineDTO dto) { // 业务逻辑 }7.2 敏感数据保护
- 宠物主人信息脱敏处理:
public String desensitizePhone(String phone) { return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"); }- 数据库字段加密:
mybatis-plus: encryptor: password: ${DB_ENCRYPT_KEY} algorithm: AES8. 扩展功能实现方案
8.1 微信小程序对接
Uniapp跨端方案封装通用方法:
// 获取微信运动步数 export const getWeRunData = () => { return new Promise((resolve, reject) => { uni.getWeRunData({ success(res) { resolve(decryptData(res.encryptedData, res.iv)) } }) }) }8.2 智能硬件接入
MQTT协议设备通信模块:
@Slf4j @Component public class MqttCallback implements MqttCallbackExtended { @Override public void connectComplete(boolean reconnect, String serverURI) { if(reconnect) { client.subscribe("pet/+/weight", 1); } } }项目源码中特别加入了宠物营养计算器模块,根据品种、年龄和活动量推荐每日摄入量。测试阶段发现金毛犬的喂养建议需要特别调整系数,这个细节后来成为了系统的特色功能