这个生产制造执行系统(MES)项目基于ThinkPHP和Laravel两大PHP框架构建,面向制造业企业的生产流程管理需求。在实际工厂环境中,生产数据分散、工序衔接不畅、质量追溯困难是普遍痛点。我们团队通过这个项目,实现了从订单下达到产品交付的全流程数字化管控。
为什么选择PHP框架来做MES?很多人第一反应可能是Java或.NET更适合企业级应用。但经过对客户现状的评估,我们发现:
ThinkPHP的ORM特性和Laravel的队列系统完美匹配了这些需求。特别是Laravel Horizon提供的可视化队列监控,对实时处理生产数据异常非常有用。
采用双框架混合开发模式:
这种组合充分发挥了各自优势:
系统包含7个关键模块:
特别说明质量门模块的实现:采用ThinkPHP处理基础检验标准维护,Laravel实现实时检验数据采集。两个框架通过Redis共享会话数据,确保用户体验一致。
生产高峰期单小时可能产生2w+报工记录。我们采用以下方案确保系统稳定:
php复制// Laravel队列工作者配置
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'block_for' => 5,
'after_commit' => false,
],
配套的优化措施:
两个框架共用数据库时的数据同步是个挑战。我们的解决方案:
sql复制CREATE TABLE `data_versions` (
`table_name` varchar(50) NOT NULL,
`version` bigint(20) NOT NULL DEFAULT 0,
PRIMARY KEY (`table_name`)
) ENGINE=InnoDB;
php复制class VersionBehavior extends Behavior {
public function run(&$params){
Db::name('data_versions')
->where('table_name', $this->table)
->inc('version')
->update();
}
}
php复制class DataVersionObserver {
public function checkVersion($model) {
$current = DataVersion::where('table_name', $model->getTable())->value('version');
if($model->version < $current) {
$model->refresh();
}
}
}
采用Docker Swarm集群部署:
针对ThinkPHP的优化:
php复制// config/app.php
return [
'app_trace' => false,
'sql_explain' => false,
'opcache' => [
'enable' => true,
'preload' => true
]
];
Laravel特别配置:
php复制// config/view.php
'compiled' => env(
'VIEW_COMPILED_PATH',
realpath(storage_path('framework/views'))
),
// config/cache.php
'file' => [
'path' => env('CACHE_PATH', '/dev/shm/laravel_cache'),
],
现象:用户登录后随机跳转到登录页
原因:两个框架默认都使用PHPSESSID作为session名
解决:
php复制// ThinkPHP配置
'session' => [
'name' => 'TP_SESSID',
'prefix' => 'tp_'
],
// Laravel配置
SESSION_COOKIE=laravel_session
SESSION_PREFIX=laravel_
现象:同一条报工记录被处理多次
排查发现是Laravel队列的retry_after和超时时间设置不当:
env复制QUEUE_TIMEOUT=60
QUEUE_RETRY_AFTER=65
调整原则:retry_after必须大于timeout,建议差值至少5秒。
特殊案例:/api/login在两个框架都有定义
解决方案:
nginx复制location ~ ^/api/tp/ {
rewrite ^/api/tp/(.*) /$1 break;
proxy_pass http://thinkphp_servers;
}
location ~ ^/api/laravel/ {
rewrite ^/api/laravel/(.*) /$1 break;
proxy_pass http://laravel_servers;
}
在ThinkPHP侧实现可插拔的功能模块:
php复制// 插件目录结构
addons/
├── workshop/
│ ├── controller/
│ ├── model/
│ └── plugin.php
└── quality/
├── controller/
├── model/
└── plugin.php
通过Hook类动态加载插件路由:
php复制class Hook {
public static function addRoute($router) {
$plugins = glob('addons/*/plugin.php');
foreach($plugins as $plugin) {
$config = include $plugin;
$router->group($config['prefix'], function() use ($config){
// 注册路由...
});
}
}
}
虽然当前是单体架构,但已预留微服务拆分路径:
通过API Gateway统一接入,各服务通过RabbitMQ交换数据。我们在代码中已经采用DDD模式组织代码结构,为未来拆分做准备。
针对设备直连场景的特殊防护:
php复制// Laravel中间件
class ModbusFilter {
public function handle($request, $next) {
$allowed = ['192.168.1.100', '192.168.1.101'];
if(!in_array($request->ip(), $allowed)) {
abort(403, 'Device not whitelisted');
}
return $next($request);
}
}
ThinkPHP侧:
php复制// 强制参数绑定
Db::name('user')
->where('id', '=', input('id/d'))
->select();
Laravel侧:
php复制// 使用查询构造器
DB::table('users')
->where('id', request('id'))
->get();
使用Prometheus+Grafana监控:
php复制// Laravel服务
$counter = $this->registry->getOrRegisterCounter(
'mes',
'workorder_processed_total',
'Total processed work orders'
);
$counter->inc();
ThinkPHP开启SQL监听:
php复制Db::listen(function($sql, $time) {
if($time > 1000) { // 超过1秒记录
Log::record("Slow query: {$sql} Time: {$time}ms");
}
});
Laravel使用调试条:
php复制// AppServiceProvider
if(config('app.debug')) {
DB::enableQueryLog();
}
统一响应格式:
json复制{
"code": 200,
"data": {},
"msg": "success",
"timestamp": 1630000000
}
错误码标准化:
php复制// 共用错误码类
class ErrorCode {
const PARAM_ERROR = 1001;
const AUTH_FAILED = 2001;
// ...
}
处理车间现场照片上传:
php复制// Laravel控制器
public function upload(Request $request) {
$file = $request->file('image');
$path = $file->storeAs(
'workshop/' . date('Ym'),
md5(time()) . '.' . $file->extension(),
'oss' // 使用阿里云OSS驱动
);
return response()->json([
'path' => $path,
'url' => Storage::disk('oss')->url($path)
]);
}
经过这个项目的实践,我总结了几个关键经验:
双框架混用要尽早确立规范
生产数据容灾必须多层级
车间网络环境要提前摸底
对于想要尝试类似项目的团队,我的建议是: