这个基于Python+Django的适老化健康预警系统是我近期完成的一个毕业设计项目,主要面向老年人群体的健康监测需求。系统通过实时采集和分析老年人的生理指标数据,结合智能算法进行健康风险评估,当检测到异常情况时自动触发预警机制,通知家属或医护人员及时干预。
在实际开发过程中,我采用了前后端分离的架构设计,前端使用Vue.js实现响应式界面,后端基于Django REST framework构建API服务,数据库选用MySQL进行数据存储。系统特别注重适老化设计,包括大字体显示、语音播报、一键呼叫等老年人友好功能。
系统采用B/S架构,整体分为表现层、业务逻辑层和数据访问层:
技术栈选择考虑因素:
数据库设计遵循第三范式,主要包含以下核心表:
用户表(user):存储系统用户信息
sql复制CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(255) NOT NULL,
`real_name` varchar(50) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`role` enum('admin','family','elderly') NOT NULL,
`created_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
健康数据表(health_data):记录老年人健康指标
sql复制CREATE TABLE `health_data` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`heart_rate` int DEFAULT NULL,
`blood_pressure` varchar(20) DEFAULT NULL,
`temperature` decimal(3,1) DEFAULT NULL,
`blood_oxygen` int DEFAULT NULL,
`record_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
CONSTRAINT `health_data_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
预警记录表(alert):存储系统生成的预警信息
sql复制CREATE TABLE `alert` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`alert_type` enum('heart_rate','blood_pressure','temperature','blood_oxygen') NOT NULL,
`alert_level` enum('low','medium','high') NOT NULL,
`message` varchar(255) NOT NULL,
`status` enum('unprocessed','processing','processed') NOT NULL DEFAULT 'unprocessed',
`created_at` datetime NOT NULL,
`processed_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
CONSTRAINT `alert_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
系统采用JWT(JSON Web Token)实现用户认证,主要流程如下:
核心代码实现:
python复制# settings.py
JWT_AUTH = {
'JWT_SECRET_KEY': 'your-secret-key',
'JWT_ALGORITHM': 'HS256',
'JWT_ALLOW_REFRESH': True,
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=30),
}
# views.py
from rest_framework_jwt.views import ObtainJSONWebToken
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class UserLoginView(ObtainJSONWebToken):
def post(self, request, *args, **kwargs):
response = super().post(request, *args, **kwargs)
if response.status_code == 200:
user = self.user
# 添加自定义用户信息到响应中
response.data['user'] = {
'id': user.id,
'username': user.username,
'role': user.role,
'real_name': user.real_name
}
return response
class UserInfoView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
user = request.user
return Response({
'id': user.id,
'username': user.username,
'role': user.role,
'real_name': user.real_name,
'phone': user.phone
})
系统支持多种方式采集健康数据:
数据处理流程:
核心代码实现:
python复制# serializers.py
from rest_framework import serializers
from .models import HealthData
class HealthDataSerializer(serializers.ModelSerializer):
class Meta:
model = HealthData
fields = ['id', 'user_id', 'heart_rate', 'blood_pressure',
'temperature', 'blood_oxygen', 'record_time']
extra_kwargs = {
'user_id': {'required': True},
'record_time': {'required': True}
}
def validate_heart_rate(self, value):
if value is not None and (value < 30 or value > 200):
raise serializers.ValidationError("心率值应在30-200之间")
return value
def validate_temperature(self, value):
if value is not None and (value < 35 or value > 42):
raise serializers.ValidationError("体温值应在35-42之间")
return value
# views.py
from rest_framework import generics
from .models import HealthData
from .serializers import HealthDataSerializer
from rest_framework.permissions import IsAuthenticated
class HealthDataListCreateView(generics.ListCreateAPIView):
queryset = HealthData.objects.all()
serializer_class = HealthDataSerializer
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
serializer.save(user_id=self.request.user.id)
def get_queryset(self):
user = self.request.user
if user.role == 'elderly':
return HealthData.objects.filter(user_id=user.id)
elif user.role == 'family':
# 家属可以看到关联老人的数据
related_elders = User.objects.filter(family_relation__family_member=user)
return HealthData.objects.filter(user_id__in=related_elders.values('id'))
return HealthData.objects.none()
系统内置的健康风险评估算法主要基于以下指标:
预警级别划分:
核心算法实现:
python复制# services/risk_assessment.py
from datetime import datetime, timedelta
from .models import HealthData, Alert
from django.db.models import Max
class RiskAssessmentService:
@staticmethod
def assess_health_risk(user_id):
# 获取最近24小时的健康数据
time_threshold = datetime.now() - timedelta(hours=24)
latest_data = HealthData.objects.filter(
user_id=user_id,
record_time__gte=time_threshold
).order_by('-record_time').first()
if not latest_data:
return None
alerts = []
# 心率评估 (正常范围: 60-100)
if latest_data.heart_rate:
if latest_data.heart_rate < 50:
alerts.append(('heart_rate', 'high', '心率过低,可能存在严重风险'))
elif latest_data.heart_rate < 60:
alerts.append(('heart_rate', 'medium', '心率偏低,请注意观察'))
elif latest_data.heart_rate > 120:
alerts.append(('heart_rate', 'high', '心率过高,可能存在严重风险'))
elif latest_data.heart_rate > 100:
alerts.append(('heart_rate', 'medium', '心率偏高,请注意观察'))
# 血压评估 (正常范围: 收缩压90-140, 舒张压60-90)
if latest_data.blood_pressure:
systolic, diastolic = map(int, latest_data.blood_pressure.split('/'))
if systolic < 90 or diastolic < 60:
alerts.append(('blood_pressure', 'high', '血压过低,可能存在严重风险'))
elif systolic > 180 or diastolic > 110:
alerts.append(('blood_pressure', 'high', '血压过高,可能存在严重风险'))
elif systolic > 140 or diastolic > 90:
alerts.append(('blood_pressure', 'medium', '血压偏高,请注意观察'))
# 创建预警记录
for alert_type, alert_level, message in alerts:
Alert.objects.create(
user_id=user_id,
alert_type=alert_type,
alert_level=alert_level,
message=message
)
return alerts
针对老年人用户群体,系统在前端界面设计上做了以下优化:
前端实现代码示例:
vue复制<template>
<div class="elderly-mode-container">
<!-- 大字体模式 -->
<div :class="['text-content', {'large-text': isLargeText}]">
<h1>健康数据概览</h1>
<p>最新测量时间: {{ lastMeasureTime }}</p>
</div>
<!-- 高对比度模式 -->
<div :class="['data-card', {'high-contrast': isHighContrast}]">
<div class="data-item">
<span class="label">心率</span>
<span class="value">{{ heartRate }} bpm</span>
</div>
<!-- 其他数据项... -->
</div>
<!-- 大按钮 -->
<button class="big-button" @click="emergencyCall">
<i class="icon-phone"></i>
<span>一键呼叫</span>
</button>
<!-- 语音控制 -->
<div class="voice-control">
<button @click="toggleVoiceAssistant">
{{ voiceAssistantActive ? '关闭' : '开启' }}语音助手
</button>
<audio ref="voiceAudio" :src="voicePromptUrl"></audio>
</div>
</div>
</template>
<script>
export default {
data() {
return {
isLargeText: true,
isHighContrast: true,
voiceAssistantActive: false,
// 其他数据...
}
},
methods: {
emergencyCall() {
// 一键呼叫逻辑
this.$emit('emergency-call')
},
toggleVoiceAssistant() {
this.voiceAssistantActive = !this.voiceAssistantActive
if (this.voiceAssistantActive) {
this.playVoicePrompt()
}
},
playVoicePrompt() {
if (this.voiceAssistantActive) {
this.$refs.voiceAudio.play()
}
}
// 其他方法...
}
}
</script>
<style scoped>
.elderly-mode-container {
font-size: 16px;
}
.large-text {
font-size: 20px;
}
.large-text h1 {
font-size: 28px;
}
.high-contrast {
background-color: black;
color: white;
}
.big-button {
min-width: 120px;
min-height: 60px;
font-size: 20px;
padding: 15px 30px;
}
.data-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.data-item {
margin: 15px 0;
}
.data-item .label {
font-weight: bold;
margin-right: 10px;
}
.data-item .value {
font-size: 24px;
}
</style>
系统提供多种预警通知方式,确保紧急情况能够及时传达:
通知服务实现代码:
python复制# services/notification_service.py
import requests
from django.conf import settings
from twilio.rest import Client
from .models import Alert, User
class NotificationService:
@staticmethod
def send_alert_notification(alert_id):
alert = Alert.objects.get(id=alert_id)
user = alert.user
# 获取需要通知的家属用户
family_members = User.objects.filter(
family_relation__elderly=user,
role='family'
)
# 站内消息
for member in family_members:
NotificationService.send_in_app_notification(member, alert)
# 短信通知
if member.phone:
NotificationService.send_sms_notification(member, alert)
# 对于高级预警,额外发送语音电话
if alert.alert_level == 'high':
for member in family_members:
if member.phone:
NotificationService.send_voice_call(member, alert)
@staticmethod
def send_in_app_notification(user, alert):
# 实际项目中这里会创建站内消息记录
# 前端通过WebSocket或轮询获取新消息
pass
@staticmethod
def send_sms_notification(user, alert):
if not settings.SMS_ENABLED:
return
message = f"【健康预警】{alert.user.real_name}的{alert.get_alert_type_display()}异常:" \
f"{alert.message},请及时处理!"
# 使用短信服务商API发送短信
try:
response = requests.post(
settings.SMS_API_URL,
data={
'mobile': user.phone,
'content': message,
'sign': settings.SMS_SIGN
},
headers={'Authorization': f'Bearer {settings.SMS_API_KEY}'}
)
response.raise_for_status()
except Exception as e:
# 记录发送失败日志
pass
@staticmethod
def send_voice_call(user, alert):
if not settings.TWILIO_ENABLED:
return
client = Client(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN)
message = f"紧急通知,{alert.user.real_name}的健康状况出现严重异常," \
f"{alert.get_alert_type_display()}指标{alert.message}," \
"请立即查看处理!"
try:
call = client.calls.create(
twiml=f'<Response><Say language="zh-CN">{message}</Say></Response>',
to=user.phone,
from_=settings.TWILIO_PHONE_NUMBER
)
return call.sid
except Exception as e:
# 记录发送失败日志
return None
系统支持多种部署方式,满足不同场景需求:
本地开发环境部署
bash复制python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
pip install -r requirements.txt
python复制# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'elderly_health',
'USER': 'your_username',
'PASSWORD': 'your_password',
'HOST': 'localhost',
'PORT': '3306',
}
}
bash复制python manage.py migrate
bash复制python manage.py runserver
生产环境部署
nginx复制# /etc/nginx/sites-available/elderly_health
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static/ {
alias /path/to/your/static/files/;
}
}
系统测试采用分层测试策略,确保各组件质量:
单元测试:测试各个独立模块功能
python复制# tests/test_models.py
from django.test import TestCase
from .models import HealthData
from django.contrib.auth import get_user_model
User = get_user_model()
class HealthDataModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
password='testpass123',
role='elderly'
)
def test_create_health_data(self):
health_data = HealthData.objects.create(
user=self.user,
heart_rate=75,
blood_pressure='120/80',
temperature=36.5,
blood_oxygen=98
)
self.assertEqual(health_data.heart_rate, 75)
self.assertEqual(health_data.get_blood_pressure_systolic(), 120)
集成测试:测试模块间交互
python复制# tests/test_views.py
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework import status
from .models import HealthData
class HealthDataAPITest(APITestCase):
def setUp(self):
# 创建测试用户和数据...
pass
def test_create_health_data_authenticated(self):
self.client.force_authenticate(user=self.user)
url = reverse('healthdata-list')
data = {
'heart_rate': 72,
'blood_pressure': '118/76',
'temperature': 36.6,
'blood_oxygen': 97
}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(HealthData.objects.count(), 1)
系统测试:测试完整业务流程
python复制# tests/test_system.py
from django.test import TestCase
from selenium import webdriver
from .models import User
class SystemTest(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.selenium = webdriver.Chrome()
cls.selenium.implicitly_wait(10)
@classmethod
def tearDownClass(cls):
cls.selenium.quit()
super().tearDownClass()
def test_user_login(self):
User.objects.create_user(
username='testuser',
password='testpass123',
role='elderly'
)
self.selenium.get('http://localhost:8000/login')
username_input = self.selenium.find_element_by_name('username')
username_input.send_keys('testuser')
password_input = self.selenium.find_element_by_name('password')
password_input.send_keys('testpass123')
self.selenium.find_element_by_xpath('//button[text()="登录"]').click()
# 验证登录后跳转的页面
self.assertIn('健康数据', self.selenium.title)
性能测试:使用Locust进行负载测试
python复制# locustfile.py
from locust import HttpUser, task, between
class WebsiteUser(HttpUser):
wait_time = between(1, 2.5)
@task
def view_health_data(self):
self.client.get("/api/health-data/")
@task(3)
def login(self):
self.client.post("/api/auth/login/", {
"username": "testuser",
"password": "testpass123"
})
在开发这个适老化健康预警系统的过程中,我积累了一些有价值的经验,特别是一些容易忽视但非常重要的细节:
适老化设计不仅仅是放大字体:
健康数据处理注意事项:
预警机制设计经验:
性能优化技巧:
安全注意事项:
这个项目从需求分析到最终实现历时3个月,期间遇到了不少挑战,比如如何平衡系统的复杂度和易用性,如何设计有效的预警算法等。通过这个项目,我不仅巩固了Django和Vue的技术栈,更重要的是学会了如何从用户角度出发设计系统,特别是针对特殊用户群体如老年人的需求考虑。