这个基于Django框架的生产执行管理系统(MES)项目,是我去年为一家中型制造企业实施的数字化转型解决方案。系统上线后,他们的生产异常处理效率提升了60%,订单交付准时率从78%提高到92%。不同于市面上通用的ERP系统,我们针对离散型制造的特点,用Django构建了一套轻量级但功能完备的生产执行管理平台。
提示:生产执行管理系统(MES)是连接企业计划层(ERP)与车间控制层的关键系统,主要功能包括工单管理、生产调度、质量追溯、设备监控等。
系统采用Django 3.2 LTS版本开发,前端使用Bootstrap 5 + jQuery的组合方案。选择Django而非Spring Boot等Java框架,主要考虑到客户IT团队更熟悉Python技术栈,且项目需要快速迭代交付。数据库选用PostgreSQL 13,因其在事务处理和数据完整性方面的优势非常适合制造业场景。
系统采用经典的三层架构,但在数据层做了特殊优化:
code复制表示层:Bootstrap 5响应式界面 + Django模板引擎
业务层:Django核心 + 自定义Middleware + 异步任务(Celery)
数据层:PostgreSQL主从集群 + Redis缓存 + MinIO文件存储
特别设计了双重缓存策略:
在models.py中定义了7个核心数据模型:
python复制class WorkOrder(models.Model):
"""工单模型"""
STATUS_CHOICES = [
('pending', '待生产'),
('running', '生产中'),
('paused', '已暂停'),
('completed', '已完成')
]
number = models.CharField(max_length=20, unique=True)
product = models.ForeignKey('Product', on_delete=models.PROTECT)
planned_quantity = models.PositiveIntegerField()
actual_quantity = models.PositiveIntegerField(default=0)
start_date = models.DateTimeField(null=True)
end_date = models.DateTimeField(null=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='pending')
def remaining_quantity(self):
return self.planned_quantity - self.actual_quantity
注意:制造业数据模型设计要特别注意字段约束,比如使用PositiveIntegerField确保数量不会出现负值,on_delete=PROTECT防止误删关键数据。
采用Django Channels实现WebSocket通信,关键代码:
python复制# consumers.py
class DashboardConsumer(AsyncWebsocketConsumer):
async def connect(self):
await self.accept()
await self.channel_layer.group_add("dashboard", self.channel_name)
async def disconnect(self, close_code):
await self.channel_layer.group_discard("dashboard", self.channel_name)
async def production_update(self, event):
await self.send(text_data=json.dumps(event["data"]))
前端通过JavaScript接收实时更新:
javascript复制const socket = new WebSocket(`ws://${window.location.host}/ws/dashboard/`);
socket.onmessage = function(e) {
const data = JSON.parse(e.data);
updateDashboard(data);
};
设计RESTful API时特别注意了幂等性处理:
python复制# serializers.py
class OperationReportSerializer(serializers.ModelSerializer):
operator = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
class Meta:
model = OperationReport
fields = ['work_order', 'operation', 'quantity', 'defect_qty', 'operator']
extra_kwargs = {
'work_order': {'required': True},
'operation': {'required': True},
'quantity': {'min_value': 1}
}
# views.py
class OperationReportViewSet(viewsets.ModelViewSet):
queryset = OperationReport.objects.all()
serializer_class = OperationReportSerializer
permission_classes = [IsAuthenticated]
@transaction.atomic
def perform_create(self, serializer):
instance = serializer.save()
update_work_order_progress(instance.work_order)
开发了基于约束理论的排程算法,核心逻辑:
python复制def schedule_work_orders(orders, resources):
"""智能工单排程算法"""
scheduled = []
# 1. 按优先级和交期排序
orders.sort(key=lambda x: (x.priority, x.due_date))
# 2. 资源能力检查
resource_calendar = {r.id: r.available_hours for r in resources}
# 3. 前向排程
for order in orders:
operations = order.operations.all().order_by('sequence')
for op in operations:
required_resources = op.resources.all()
# 查找最早可用时间窗
start_time = find_available_slot(
resource_calendar,
required_resources,
op.estimated_hours
)
# 创建排程记录
scheduled.append({
'order': order,
'operation': op,
'start': start_time,
'end': start_time + timedelta(hours=op.estimated_hours)
})
# 更新资源日历
for res in required_resources:
resource_calendar[res.id] -= op.estimated_hours
return scheduled
通过Django的GenericForeignKey实现灵活的质量记录关联:
python复制class QualityCheck(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
check_type = models.CharField(max_length=50)
result = models.JSONField() # 存储结构化检验数据
inspector = models.ForeignKey(User, on_delete=models.PROTECT)
checked_at = models.DateTimeField(auto_now_add=True)
class Meta:
indexes = [
models.Index(fields=["content_type", "object_id"]),
]
针对生产数据特点做了这些优化:
python复制class WorkOrderHistory(models.Model):
class Meta:
partitioning_options = {
'method': 'range',
'key': ['created_at'],
'partitions': [
{'name': 'p202301', 'values': ['2023-01-01', '2023-02-01']},
# 其他月份分区...
]
}
索引策略:
CREATE INDEX idx_wo_prefix ON workorder (substring(number from 1 for 4))查询优化:
python复制# 错误写法(N+1查询)
for order in WorkOrder.objects.filter(status='running'):
print(order.product.name)
# 正确写法(使用select_related)
for order in WorkOrder.objects.select_related('product').filter(status='running'):
print(order.product.name)
根据数据特性采用多级缓存:
python复制@cache_page(60 * 15) # 15分钟缓存
def product_list(request):
products = Product.objects.all()
return render(request, 'mes/product_list.html', {'products': products})
html复制{% load cache %}
{% cache 300 production_dashboard request.user.id %}
<!-- 复杂的看板HTML -->
{% endcache %}
python复制from django.core.cache import cache
def calculate_oee(machine_id):
cache_key = f'oee_{machine_id}_{date.today()}'
result = cache.get(cache_key)
if result is None:
result = _complex_oee_calculation(machine_id)
cache.set(cache_key, result, timeout=3600)
return result
制造业系统特别要注意时区问题,我们的解决方案:
python复制USE_TZ = True
TIME_ZONE = 'Asia/Shanghai'
python复制from django.utils import timezone
def start_production(order_id):
order = WorkOrder.objects.get(pk=order_id)
order.start_date = timezone.now() # 自动转换为UTC存储
order.save()
javascript复制function displayLocalTime(utcTime) {
const localTime = new Date(utcTime);
return localTime.toLocaleString('zh-CN', {
timeZone: 'Asia/Shanghai'
});
}
生产报工场景要特别注意并发控制:
python复制from django.db import transaction
@transaction.atomic
def report_operation(order_id, quantity):
order = WorkOrder.objects.select_for_update().get(pk=order_id)
if order.remaining_quantity() < quantity:
raise ValueError("报工数量超过剩余工单数量")
order.actual_quantity += quantity
order.save()
python复制class WorkOrder(models.Model):
version = models.IntegerField(default=0)
def save(self, *args, **kwargs):
if self.pk:
rows = WorkOrder.objects.filter(
pk=self.pk,
version=self.version
).update(
version=self.version + 1,
**kwargs
)
if not rows:
raise ConcurrentModificationError
else:
super().save(*args, **kwargs)
经过优化后的系统性能指标:
| 场景 | 请求量 | 平均响应时间 | 数据库查询次数 |
|---|---|---|---|
| 工单列表 | 50次/秒 | 120ms | 3(缓存命中时1) |
| 生产报工 | 30次/秒 | 80ms | 6 |
| 实时看板 | 100WS连接 | 5ms推送延迟 | 2(每10秒) |
通过Django Channels实现与SAP的异步集成:
python复制# consumers.py
class SAPIntegrationConsumer(AsyncConsumer):
async def sap_message(self, message):
msg_type = message['type']
if msg_type == 'material_update':
await self.handle_material_update(message)
async def handle_material_update(self, message):
try:
material = message['data']
with transaction.atomic():
item, created = Material.objects.update_or_create(
sap_code=material['code'],
defaults={
'name': material['name'],
'spec': material['spec'],
'unit': material['unit']
}
)
if created:
await self.channel_layer.group_send(
'inventory',
{'type': 'new_material', 'data': material}
)
except Exception as e:
logger.error(f"SAP集成错误: {str(e)}")
await self.send_error_to_monitor(e)
基于Bootstrap 5的移动端优化技巧:
html复制<div class="table-responsive">
<table class="table table-sm">
<!-- 生产数据表格 -->
</table>
</div>
javascript复制$('.operation-btn').on('touchstart click', function(e) {
e.preventDefault();
const opId = $(this).data('op-id');
showOperationDialog(opId);
});
javascript复制// 注册Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => {
console.log('ServiceWorker 注册成功');
});
}
// 离线数据同步
function syncOfflineReports() {
const reports = getOfflineReports();
reports.forEach(report => {
fetch('/api/reports/', {
method: 'POST',
body: JSON.stringify(report),
headers: {
'Content-Type': 'application/json'
}
}).then(() => removeOfflineReport(report.id));
});
}
这个Django生产执行系统项目让我深刻体会到,制造业软件最关键的不仅是技术实现,更是对生产业务逻辑的准确把握。比如工单状态机的设计,我们迭代了5个版本才最终确定包含"暂停-恢复"在内的完整状态流转逻辑。建议开发类似系统的同行,一定要先花时间深入车间观察实际生产流程,这比任何技术选型都重要。