操作系统选择Ubuntu 22.04 LTS,确保系统已更新:
sudo apt update && sudo apt upgrade -y
安装必备软件包:
sudo apt install -y nginx mysql-server python3-pip python3-venv redis-server git
配置MySQL数据库,创建专用数据库用户:
sudo mysql -e "CREATE DATABASE animal_archive DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
sudo mysql -e "CREATE USER 'archive_admin'@'localhost' IDENTIFIED BY 'YourSecurePassword123!';"
sudo mysql -e "GRANT ALL PRIVILEGES ON animal_archive. TO 'archive_admin'@'localhost';"
sudo mysql -e "FLUSH PRIVILEGES;"
创建项目目录并初始化Python虚拟环境:
mkdir -p /opt/animal_archive/{backend,frontend,logs,backups}
cd /opt/animal_archive/backend
python3 -m venv venv
source venv/bin/activate
安装Python依赖包,创建requirements.txt文件:
echo "Django==4.2.7
djangorestframework==3.14.0
django-cors-headers==4.2.0
mysqlclient==2.2.0
redis==5.0.1
Pillow==10.1.0
openpyxl==3.1.2
reportlab==4.0.9" > requirements.txt
pip install -r requirements.txt
创建Django项目和应用:
django-admin startproject archive_core .
python manage.py startapp animal_records
python manage.py startapp vaccine_management
配置settings.py数据库连接:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'animal_archive',
'USER': 'archive_admin',
'PASSWORD': 'YourSecurePassword123!',
'HOST': 'localhost',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8mb4',
}
}
}
添加Redis缓存配置:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
在animal_records/models.py中定义核心数据模型:
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
class Animal(models.Model):
ANIMAL_TYPES = [
('cattle', '牛'),
('sheep', '羊'),
('horse', '马'),
('pig', '猪'),
]
ear_tag = models.CharField('耳标号', max_length=50, unique=True)
animal_type = models.CharField('畜种', max_length=20, choices=ANIMAL_TYPES)
breed = models.CharField('品种', max_length=100)
birth_date = models.DateField('出生日期')
gender = models.CharField('性别', max_length=10, choices=[('male', '公'), ('female', '母')])
weight = models.DecimalField('当前体重(kg)', max_digits=6, decimal_places=2)
health_status = models.CharField('健康状况', max_length=20,
choices=[('healthy', '健康'), ('sick', '患病'), ('recovering', '恢复期')])
pen_number = models.CharField('圈舍编号', max_length=20)
entry_date = models.DateField('入场日期', auto_now_add=True)
photo = models.ImageField('照片', upload_to='animal_photos/', null=True, blank=True)
class Meta:
verbose_name = '牲畜档案'
verbose_name_plural = '牲畜档案'
def __str__(self):
return f"{self.ear_tag} - {self.get_animal_type_display()}"
class VaccineRecord(models.Model):
animal = models.ForeignKey(Animal, on_delete=models.CASCADE, related_name='vaccines')
vaccine_name = models.CharField('疫苗名称', max_length=100)
batch_number = models.CharField('批号', max_length=50)
vaccination_date = models.DateField('接种日期')
next_due_date = models.DateField('下次接种日期')
veterinarian = models.CharField('兽医', max_length=50)
notes = models.TextField('备注', blank=True)
class Meta:
verbose_name = '疫苗接种记录'
verbose_name_plural = '疫苗接种记录'
执行数据库迁移:
python manage.py makemigrations
python manage.py migrate
创建序列化器animal_records/serializers.py:

from rest_framework import serializers
from .models import Animal, VaccineRecord
class AnimalSerializer(serializers.ModelSerializer):
age_days = serializers.SerializerMethodField()
class Meta:
model = Animal
fields = '__all__'
def get_age_days(self, obj):
from datetime import date
return (date.today() - obj.birth_date).days
class VaccineRecordSerializer(serializers.ModelSerializer):
class Meta:
model = VaccineRecord
fields = '__all__'
创建视图animal_records/views.py:
from rest_framework import viewsets, filters
from django_filters.rest_framework import DjangoFilterBackend
from .models import Animal, VaccineRecord
from .serializers import AnimalSerializer, VaccineRecordSerializer
class AnimalViewSet(viewsets.ModelViewSet):
queryset = Animal.objects.all().order_by('-entry_date')
serializer_class = AnimalSerializer
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
filterset_fields = ['animal_type', 'gender', 'health_status', 'pen_number']
search_fields = ['ear_tag', 'breed']
class VaccineRecordViewSet(viewsets.ModelViewSet):
queryset = VaccineRecord.objects.all().order_by('-vaccination_date')
serializer_class = VaccineRecordSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['animal__ear_tag', 'vaccine_name']
配置URL路由archive_core/urls.py:
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from animal_records.views import AnimalViewSet, VaccineRecordViewSet
router = DefaultRouter()
router.register(r'animals', AnimalViewSet)
router.register(r'vaccines', VaccineRecordViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
path('api-auth/', include('rest_framework.urls')),
]
进入前端目录并初始化项目:
cd /opt/animal_archive/frontend
npm init -y
npm install vue@3 vue-router@4 axios element-plus
创建主入口文件src/main.js:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(router)
app.use(ElementPlus)
app.mount('app')
创建牲畜列表组件src/components/AnimalList.vue:
牲畜档案管理
新增档案
{{ getAnimalTypeName(scope.row.animal_type) }}
{{ scope.row.gender === 'male' ? '公' : '母' }}
{{ getHealthStatusName(scope.row.health_status) }}
编辑
删除
疫苗记录
创建数据导入视图animal_records/import_export.py:
import pandas as pd
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import api_view
from .models import Animal
@api_view(['POST'])
@csrf_exempt
def import_animals_from_excel(request):
try:
excel_file = request.FILES['file']
df = pd.read_excel(excel_file)
imported_count = 0
errors = []
for index, row in df.iterrows():
try:
animal_data = {
'ear_tag': str(row['耳标号']),
'animal_type': row['畜种'],
'breed': row['品种'],
'birth_date': row['出生日期'].date() if pd.notna(row['出生日期']) else None,
'gender': row['性别'],
'weight': float(row['体重']) if pd.notna(row['体重']) else 0,
'health_status': row['健康状况'],
'pen_number': str(row['圈舍编号'])
}
animal, created = Animal.objects.update_or_create(
ear_tag=animal_data['ear_tag'],
defaults=animal_data
)
if created:
imported_count += 1
except Exception as e:
errors.append(f"第{index+2}行错误: {str(e)}")
return JsonResponse({
'success': True,
'imported_count': imported_count,
'error_count': len(errors),
'errors': errors[:10] 最多返回10个错误
})
except Exception as e:
return JsonResponse({
'success': False,
'error': str(e)
}, status=400)
创建报表生成视图animal_records/reports.py:
from django.http import HttpResponse
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
from io import BytesIO
from datetime import datetime
from .models import Animal
def generate_animal_report(request):
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="animal_report.pdf"'
buffer = BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=letter)
elements = []
styles = getSampleStyleSheet()
标题
title = Paragraph(f"牧业档案统计报表 - {datetime.now().strftime('%Y-%m-%d')}",
styles['Title'])
elements.append(title)
统计数据
animals = Animal.objects.all()
total_count = animals.count()
按畜种统计
type_stats = animals.values('animal_type').annotate(count=models.Count('id'))
type_data = [['畜种', '数量', '占比']]
for stat in type_stats:
count = stat['count']
percentage = (count / total_count 100) if total_count > 0 else 0
type_data.append([
dict(Animal.ANIMAL_TYPES)[stat['animal_type']],
str(count),
f"{percentage:.1f}%"
])
创建表格
table = Table(type_data)
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.grey),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('FONTSIZE', (0, 0), (-1, 0), 14),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
elements.append(table)
doc.build(elements)
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response
安装Gunicorn并创建服务文件:
pip install gunicorn
sudo nano /etc/systemd/system/animal-archive.service
服务文件内容:
[Unit]
Description=Animal Archive Gunicorn Service
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/animal_archive/backend
Environment="PATH=/opt/animal_archive/backend/venv/bin"
ExecStart=/opt/animal_archive/backend/venv/bin/gunicorn \
--workers 3 \
--bind unix:/opt/animal_archive/backend/archive.sock \
archive_core.wsgi:application
[Install]
WantedBy=multi-user.target
启动服务:
sudo systemctl start animal-archive
sudo systemctl enable animal-archive
配置Nginx代理:
sudo nano /etc/nginx/sites-