load(); \support\App::loadAllConfig(['route']); date_default_timezone_set('Asia/Shanghai'); use plugin\saiadmin\app\model\water\WaterModel; use plugin\saiadmin\app\model\water\WaterSku; use plugin\saiadmin\app\model\water\WaterAccount; use plugin\saiadmin\app\model\water\WaterEnterprise; use plugin\saiadmin\app\model\water\WaterDelivery; use plugin\saiadmin\app\model\water\WaterOrder; use plugin\saiadmin\app\model\water\WaterLedger; use plugin\saiadmin\app\service\WaterBusiness; use plugin\saiadmin\app\service\WaterDispatch; $batch = 'TEST-WATER-TODAY-20260911'; $date = '2026-09-11'; $apply = in_array('--apply', $argv, true); $origin = ['origin_address'=>'深圳市南山区南油第四工业区4栋(测试出发点)', 'origin_longitude'=>113.924140, 'origin_latitude'=>22.522806]; // 复用此前高德实查 GCJ-02 坐标;测试企业为虚构名称,园区坐标不代表具体楼栋。 $places = [ ['address'=>'深圳市南山区海德一道中洲控股中心(模拟任务,请勿实际配送)','longitude'=>113.935124,'latitude'=>22.516070], ['address'=>'深圳市南山区科技园(园区级测试坐标,请勿实际配送)','longitude'=>113.946040,'latitude'=>22.544610], ['address'=>'深圳市南山区深圳湾科技生态园(园区级测试坐标,请勿实际配送)','longitude'=>113.952743,'latitude'=>22.530351], ]; $samples = [ ['name'=>'【测试0911】晨光办公','quantity'=>4,'place'=>0,'state'=>'pending','time'=>'16:00:00'], ['name'=>'【测试0911】青禾研发','quantity'=>6,'place'=>1,'state'=>'pending','time'=>'17:00:00'], ['name'=>'【测试0911】星海科技','quantity'=>5,'place'=>1,'state'=>'assigned','time'=>'18:00:00'], ['name'=>'【测试0911】云帆商务','quantity'=>7,'place'=>0,'state'=>'assigned','time'=>'15:00:00'], ['name'=>'【测试0911】绿洲创意','quantity'=>3,'place'=>2,'state'=>'assigned','time'=>'17:00:00'], ['name'=>'【测试0911】远景服务','quantity'=>8,'place'=>2,'state'=>'transit','time'=>'18:00:00'], ]; $dispatch = new WaterDispatch(); $before = $dispatch->board(['date'=>$date])['summary']; $replayed = false; if ($apply) { $replayed = WaterModel::transaction(function () use ($batch,$date,$origin,$places,$samples,$dispatch) { $sku = WaterSku::query()->where('name','【测试0910】18.9L桶装水')->lock(true)->first()?->toArray(); if (!$sku || (int)$sku['status'] !== 1) throw new RuntimeException('缺少启用的指定测试商品'); if (WaterLedger::query()->where(['kind'=>'demo_seed','reference'=>$batch])->exists()) return true; if (date('Y-m-d') !== $date) throw new RuntimeException('固定日期测试批次仅可在对应日期首次执行'); $business = new WaterBusiness(); $skuId = (int)$sku['id']; $oldTasks = WaterDelivery::query()->orderBy('id')->get()->toArray(); $oldOrders = WaterOrder::query()->orderBy('id')->get()->toArray(); $oldAccounts = WaterAccount::query()->orderBy('id')->get()->toArray(); $total = array_sum(array_column($samples,'quantity')); $business->stock(['sku_id'=>$skuId,'mode'=>'inbound','quantity'=>$total,'reference'=>$batch.'-STOCK', 'note'=>'【测试数据】今日调度模拟入库33桶,未发生真实入库'],1); $taskIds = []; foreach ($samples as $index=>$sample) { if (WaterEnterprise::query()->where('name',$sample['name'])->exists()) throw new RuntimeException('测试企业重名,停止避免覆盖'); $place = $places[$sample['place']]; $enterpriseId = $business->save('enterprise', $place+[ 'name'=>$sample['name'],'contact'=>'测试联系人'.($index+1),'phone'=>'TEST-0911-'.($index+1), 'region'=>'深圳市南山区','status'=>1, ],1)['id']; WaterAccount::query()->insert(['enterprise_id'=>$enterpriseId,'sku_id'=>$skuId, 'entitlement'=>$sample['quantity'],'threshold_qty'=>2,'updated_at'=>date('Y-m-d H:i:s')]); WaterLedger::query()->insert(['kind'=>'demo_seed','enterprise_id'=>$enterpriseId,'sku_id'=>$skuId, 'quantity'=>$sample['quantity'],'reference'=>$batch.'-RIGHTS-'.($index+1), 'note'=>'【测试数据】仅用于今日配送的模拟权益,无真实收款','created_by'=>1,'created_at'=>date('Y-m-d H:i:s')]); $id = $business->createDelivery(['enterprise_id'=>$enterpriseId,'sku_id'=>$skuId,'quantity'=>$sample['quantity'],'date'=>$date],1)['id']; $dispatch->schedule($origin+['id'=>$id,'date'=>$date,'deadline_at'=>$date.' '.$sample['time'], 'longitude'=>$place['longitude'],'latitude'=>$place['latitude']],1); $taskIds[] = $id; } $driverIds = []; foreach (['合单体验','三站优化','签收体验'] as $index=>$name) { $id = $dispatch->saveDriver(['name'=>'【测试0911】'.$name.'配送员','phone'=>'TEST-0911-DRIVER-'.($index+1), 'region'=>'深圳市南山区','capacity'=>30,'vehicle'=>'测试车辆0911-'.($index+1)],1)['id']; $dispatch->shift(['driver_id'=>$id,'date'=>$date,'state'=>'on'],1); $driverIds[] = $id; } $routes = $dispatch->commit(['date'=>$date,'request_key'=>$batch,'routes'=>[ ['driver_id'=>$driverIds[1],'task_ids'=>array_slice($taskIds,2,3)], ['driver_id'=>$driverIds[2],'task_ids'=>[$taskIds[5]]], ]],1); if (count($routes['route_ids']) !== 2) throw new RuntimeException('路线数量不正确'); $business->transition(['id'=>$taskIds[5],'action'=>'depart'],1); $afterSku = WaterSku::query()->where('id',$skuId)->first()->toArray(); if ((int)$afterSku['available'] !== (int)$sku['available'] || (int)$afterSku['locked'] !== (int)$sku['locked']+25 || (int)$afterSku['transit'] !== (int)$sku['transit']+8) throw new RuntimeException('测试库存核对失败'); if (WaterOrder::query()->orderBy('id')->get()->toArray() !== $oldOrders) throw new RuntimeException('原收款订单发生变化'); foreach ($oldTasks as $row) { if (WaterDelivery::query()->find($row['id'])->toArray() !== $row) throw new RuntimeException('原任务发生变化'); } foreach ($oldAccounts as $row) { if (WaterAccount::query()->find($row['id'])->toArray() !== $row) throw new RuntimeException('原账户发生变化'); } foreach ($taskIds as $index=>$id) { $task = WaterDelivery::query()->find($id)->toArray(); $account = WaterAccount::query()->where(['enterprise_id'=>$task['enterprise_id'],'sku_id'=>$skuId])->first()->toArray(); if ($task['status'] !== $samples[$index]['state'] || $task['scheduled_date'] !== $date || (int)$account['frozen'] !== $samples[$index]['quantity']) throw new RuntimeException('任务状态或权益不一致'); } WaterLedger::query()->insert(['kind'=>'demo_seed','sku_id'=>$skuId,'quantity'=>0,'reference'=>$batch, 'note'=>'【测试数据】今日6单33桶;任务ID:'.implode(',',$taskIds).';2单待派单、3单同路线已派单、1单配送中,无真实收款', 'created_by'=>1,'created_at'=>date('Y-m-d H:i:s')]); return false; }); } $board = $dispatch->board(['date'=>$date]); $rows = array_values(array_filter($board['tasks'],fn($task)=>in_array($task['enterprise_name'],array_column($samples,'name'),true))); echo json_encode(['mode'=>$apply?'applied':'inspect','date'=>$date,'batch'=>$batch,'replayed'=>$replayed, 'before'=>$before,'after'=>$board['summary'],'added_batch'=>array_map(fn($row)=>array_intersect_key($row,array_flip([ 'id','enterprise_name','quantity','status','scheduled_date','deadline_at','route_id','stop_order','driver', ])),$rows),'plan'=>$samples],JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT).PHP_EOL;