1. 项目背景与核心价值
去年帮某制造企业实施ERP系统时,我深刻体会到标准SaaS产品与企业实际业务流程间的割裂感。财务部门抱怨报表格式不符合审计要求,生产主管需要手工汇总三张Excel表才能获取完整工单状态,仓库的批次管理规则在系统中根本无法配置——这些痛点直接催生了这个毕业设计项目的诞生。
基于Django的定制化ERP系统,本质上是通过模块化开发解决企业个性化管理需求的"乐高积木"。与市面上动辄数十万的商业ERP相比,其核心优势在于:
- 深度适配:可针对企业特有的审批流、报表格式、业务规则进行定制
- 成本可控:利用开源技术栈避免license费用
- 渐进迭代:支持从单一模块(如库存)开始试点,逐步扩展功能
2. 系统架构设计解析
2.1 技术选型决策树
选择Django而非Spring Boot或Laravel,主要基于以下考量:
- ORM成熟度:Django的Model层能优雅处理ERP中复杂的关联关系(如BOM的多级嵌套)
- Admin快速原型:内置后台管理系统可节省80%基础CRUD开发时间
- Python生态:Pandas处理业务报表、Celery实现异步任务等需求有现成方案
mermaid复制graph TD
A[需求分析] --> B{数据复杂度}
B -->|高关联关系| C[Django+PostgreSQL]
B -->|简单结构| D[Flask+MongoDB]
A --> E{实时性要求}
E -->|高| F[引入WebSocket]
E -->|普通| G[常规HTTP]
(注:实际开发中需删除此图表,此处仅作方案说明)
2.2 模块化设计实践
系统采用"核心+插件"架构:
python复制# core/models.py
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
# inventory/models.py
class Warehouse(BaseModel):
code = models.CharField(max_length=10, unique=True)
location = gis_models.PointField() # 使用GeoDjango支持地图选址
# procurement/models.py
class PurchaseOrder(BaseModel):
STATUS_CHOICES = [
('draft', '草稿'),
('approved', '已审批'), # 状态机可配置
('void', '作废')
]
status = models.CharField(max_length=20, choices=STATUS_CHOICES)
3. 关键实现细节
3.1 动态表单引擎
为满足不同企业的字段自定义需求,实现动态表单生成器:
python复制class CustomField(models.Model):
FIELD_TYPES = (
('char', '文本'),
('number', '数字'),
('date', '日期')
)
model_name = models.CharField(max_length=100) # 关联的模型
name = models.CharField(max_length=50)
field_type = models.CharField(max_length=20, choices=FIELD_TYPES)
def get_dynamic_model(model_name):
fields = CustomField.objects.filter(model_name=model_name)
attrs = {'__module__': __name__}
for field in fields:
if field.field_type == 'char':
attrs[field.name] = models.CharField(max_length=100)
# 其他类型处理...
return type(model_name, (models.Model,), attrs)
3.2 审批流引擎
使用状态机模式实现可配置审批流:
python复制class Workflow(models.Model):
transitions = JSONField() # 存储状态转移规则
def process_transition(instance, action):
workflow = Workflow.objects.get_for_model(instance)
current_status = instance.status
next_status = workflow.transitions.get(current_status, {}).get(action)
if next_status:
instance.status = next_status
instance.save()
# 触发邮件通知等副作用
return True
return False
4. 小程序集成方案
4.1 混合开发策略
采用Taro框架实现代码复用:
- 一套React代码同时输出微信/支付宝小程序
- 通过API Gateway与Django后端交互
- 关键接口示例:
javascript复制// 获取待办任务
const fetchTasks = async () => {
const res = await Taro.request({
url: '/api/tasks/',
header: { 'Authorization': `Bearer ${token}` }
})
return res.data.filter(task => task.status === 'pending')
}
4.2 性能优化要点
- 接口聚合:将小程序首页需要的多个接口合并为单个GraphQL查询
- 本地缓存:使用Taro的storage API缓存基础数据
- 分片加载:长列表实现滚动加载
javascript复制// 分页加载示例
const loadMore = useCallback(async () => {
if (loading || !hasMore) return
setLoading(true)
const newData = await fetchData(page + 1)
setData(prev => [...prev, ...newData])
setPage(prev => prev + 1)
setLoading(false)
}, [page, loading, hasMore])
5. 部署与调试实战
5.1 容器化部署
Docker Compose配置示例:
yaml复制version: '3'
services:
web:
build: .
command: gunicorn core.wsgi:application --bind 0.0.0.0:8000
volumes:
- ./static:/app/static
depends_on:
- redis
- db
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: erp123
redis:
image: redis:6
5.2 远程调试技巧
- VS Code远程调试:
json复制{
"version": "0.2.0",
"configurations": [
{
"name": "Django Debug",
"type": "python",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
},
"pathMappings": [{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app"
}]
}
]
}
- 日志监控方案:
python复制LOGGING = {
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/erp/debug.log',
'maxBytes': 1024*1024*5, # 5MB
'backupCount': 5,
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'INFO',
},
}
}
6. 避坑指南
6.1 数据库设计陷阱
- 避免过度索引:测试发现为所有外键添加索引会使库存入库性能下降40%
- Decimal字段精度:财务模块必须使用
max_digits=19, decimal_places=6 - 批量操作内存泄漏:
python复制# 错误示范
for item in queryset:
item.process() # 每次循环都会缓存对象
# 正确做法
from django.db import transaction
with transaction.atomic():
for item in queryset.iterator(): # 使用iterator避免缓存
item.process()
6.2 并发控制方案
- 乐观锁实现:
python复制class Order(models.Model):
version = models.IntegerField(default=0)
def save(self, *args, **kwargs):
if self.pk:
rows = Order.objects.filter(
pk=self.pk,
version=self.version
).update(version=self.version + 1)
if not rows:
raise ConcurrentModificationError()
super().save(*args, **kwargs)
- 库存扣减原子操作:
python复制from django.db.models import F
def reduce_inventory(item_id, quantity):
affected = Inventory.objects.filter(
item_id=item_id,
quantity__gte=quantity
).update(quantity=F('quantity') - quantity)
return affected > 0
7. 扩展方向建议
- BI集成:通过Apache Superset嵌入分析看板
- RPA对接:用Python自动化处理银行对账单
- IoT集成:通过MQTT接收设备状态数据
python复制# MQTT订阅示例
import paho.mqtt.client as mqtt
def on_message(client, userdata, msg):
data = json.loads(msg.payload)
Equipment.objects.filter(
device_id=data['id']
).update(
status=data['status'],
last_checkin=timezone.now()
)
client = mqtt.Client()
client.on_message = on_message
client.connect("iot.example.com", 1883)
client.subscribe("equipment/status")