seed_water_today_dispatch_demo.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. <?php
  2. /** 2026-09-11 今日调度测试;默认检查,--apply 整批事务写入,固定批次防重复。 */
  3. declare(strict_types=1);
  4. require dirname(__DIR__).'/vendor/autoload.php';
  5. \Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__))->load();
  6. \support\App::loadAllConfig(['route']);
  7. date_default_timezone_set('Asia/Shanghai');
  8. use plugin\saiadmin\app\model\water\WaterModel;
  9. use plugin\saiadmin\app\model\water\WaterSku;
  10. use plugin\saiadmin\app\model\water\WaterAccount;
  11. use plugin\saiadmin\app\model\water\WaterEnterprise;
  12. use plugin\saiadmin\app\model\water\WaterDelivery;
  13. use plugin\saiadmin\app\model\water\WaterOrder;
  14. use plugin\saiadmin\app\model\water\WaterLedger;
  15. use plugin\saiadmin\app\service\WaterBusiness;
  16. use plugin\saiadmin\app\service\WaterDispatch;
  17. $batch = 'TEST-WATER-TODAY-20260911';
  18. $date = '2026-09-11';
  19. $apply = in_array('--apply', $argv, true);
  20. $origin = ['origin_address'=>'深圳市南山区南油第四工业区4栋(测试出发点)',
  21. 'origin_longitude'=>113.924140, 'origin_latitude'=>22.522806];
  22. // 复用此前高德实查 GCJ-02 坐标;测试企业为虚构名称,园区坐标不代表具体楼栋。
  23. $places = [
  24. ['address'=>'深圳市南山区海德一道中洲控股中心(模拟任务,请勿实际配送)','longitude'=>113.935124,'latitude'=>22.516070],
  25. ['address'=>'深圳市南山区科技园(园区级测试坐标,请勿实际配送)','longitude'=>113.946040,'latitude'=>22.544610],
  26. ['address'=>'深圳市南山区深圳湾科技生态园(园区级测试坐标,请勿实际配送)','longitude'=>113.952743,'latitude'=>22.530351],
  27. ];
  28. $samples = [
  29. ['name'=>'【测试0911】晨光办公','quantity'=>4,'place'=>0,'state'=>'pending','time'=>'16:00:00'],
  30. ['name'=>'【测试0911】青禾研发','quantity'=>6,'place'=>1,'state'=>'pending','time'=>'17:00:00'],
  31. ['name'=>'【测试0911】星海科技','quantity'=>5,'place'=>1,'state'=>'assigned','time'=>'18:00:00'],
  32. ['name'=>'【测试0911】云帆商务','quantity'=>7,'place'=>0,'state'=>'assigned','time'=>'15:00:00'],
  33. ['name'=>'【测试0911】绿洲创意','quantity'=>3,'place'=>2,'state'=>'assigned','time'=>'17:00:00'],
  34. ['name'=>'【测试0911】远景服务','quantity'=>8,'place'=>2,'state'=>'transit','time'=>'18:00:00'],
  35. ];
  36. $dispatch = new WaterDispatch();
  37. $before = $dispatch->board(['date'=>$date])['summary'];
  38. $replayed = false;
  39. if ($apply) {
  40. $replayed = WaterModel::transaction(function () use ($batch,$date,$origin,$places,$samples,$dispatch) {
  41. $sku = WaterSku::query()->where('name','【测试0910】18.9L桶装水')->lock(true)->first()?->toArray();
  42. if (!$sku || (int)$sku['status'] !== 1) throw new RuntimeException('缺少启用的指定测试商品');
  43. if (WaterLedger::query()->where(['kind'=>'demo_seed','reference'=>$batch])->exists()) return true;
  44. if (date('Y-m-d') !== $date) throw new RuntimeException('固定日期测试批次仅可在对应日期首次执行');
  45. $business = new WaterBusiness();
  46. $skuId = (int)$sku['id'];
  47. $oldTasks = WaterDelivery::query()->orderBy('id')->get()->toArray();
  48. $oldOrders = WaterOrder::query()->orderBy('id')->get()->toArray();
  49. $oldAccounts = WaterAccount::query()->orderBy('id')->get()->toArray();
  50. $total = array_sum(array_column($samples,'quantity'));
  51. $business->stock(['sku_id'=>$skuId,'mode'=>'inbound','quantity'=>$total,'reference'=>$batch.'-STOCK',
  52. 'note'=>'【测试数据】今日调度模拟入库33桶,未发生真实入库'],1);
  53. $taskIds = [];
  54. foreach ($samples as $index=>$sample) {
  55. if (WaterEnterprise::query()->where('name',$sample['name'])->exists()) throw new RuntimeException('测试企业重名,停止避免覆盖');
  56. $place = $places[$sample['place']];
  57. $enterpriseId = $business->save('enterprise', $place+[
  58. 'name'=>$sample['name'],'contact'=>'测试联系人'.($index+1),'phone'=>'TEST-0911-'.($index+1),
  59. 'region'=>'深圳市南山区','status'=>1,
  60. ],1)['id'];
  61. WaterAccount::query()->insert(['enterprise_id'=>$enterpriseId,'sku_id'=>$skuId,
  62. 'entitlement'=>$sample['quantity'],'threshold_qty'=>2,'updated_at'=>date('Y-m-d H:i:s')]);
  63. WaterLedger::query()->insert(['kind'=>'demo_seed','enterprise_id'=>$enterpriseId,'sku_id'=>$skuId,
  64. 'quantity'=>$sample['quantity'],'reference'=>$batch.'-RIGHTS-'.($index+1),
  65. 'note'=>'【测试数据】仅用于今日配送的模拟权益,无真实收款','created_by'=>1,'created_at'=>date('Y-m-d H:i:s')]);
  66. $id = $business->createDelivery(['enterprise_id'=>$enterpriseId,'sku_id'=>$skuId,'quantity'=>$sample['quantity'],'date'=>$date],1)['id'];
  67. $dispatch->schedule($origin+['id'=>$id,'date'=>$date,'deadline_at'=>$date.' '.$sample['time'],
  68. 'longitude'=>$place['longitude'],'latitude'=>$place['latitude']],1);
  69. $taskIds[] = $id;
  70. }
  71. $driverIds = [];
  72. foreach (['合单体验','三站优化','签收体验'] as $index=>$name) {
  73. $id = $dispatch->saveDriver(['name'=>'【测试0911】'.$name.'配送员','phone'=>'TEST-0911-DRIVER-'.($index+1),
  74. 'region'=>'深圳市南山区','capacity'=>30,'vehicle'=>'测试车辆0911-'.($index+1)],1)['id'];
  75. $dispatch->shift(['driver_id'=>$id,'date'=>$date,'state'=>'on'],1);
  76. $driverIds[] = $id;
  77. }
  78. $routes = $dispatch->commit(['date'=>$date,'request_key'=>$batch,'routes'=>[
  79. ['driver_id'=>$driverIds[1],'task_ids'=>array_slice($taskIds,2,3)],
  80. ['driver_id'=>$driverIds[2],'task_ids'=>[$taskIds[5]]],
  81. ]],1);
  82. if (count($routes['route_ids']) !== 2) throw new RuntimeException('路线数量不正确');
  83. $business->transition(['id'=>$taskIds[5],'action'=>'depart'],1);
  84. $afterSku = WaterSku::query()->where('id',$skuId)->first()->toArray();
  85. if ((int)$afterSku['available'] !== (int)$sku['available'] || (int)$afterSku['locked'] !== (int)$sku['locked']+25
  86. || (int)$afterSku['transit'] !== (int)$sku['transit']+8) throw new RuntimeException('测试库存核对失败');
  87. if (WaterOrder::query()->orderBy('id')->get()->toArray() !== $oldOrders) throw new RuntimeException('原收款订单发生变化');
  88. foreach ($oldTasks as $row) {
  89. if (WaterDelivery::query()->find($row['id'])->toArray() !== $row) throw new RuntimeException('原任务发生变化');
  90. }
  91. foreach ($oldAccounts as $row) {
  92. if (WaterAccount::query()->find($row['id'])->toArray() !== $row) throw new RuntimeException('原账户发生变化');
  93. }
  94. foreach ($taskIds as $index=>$id) {
  95. $task = WaterDelivery::query()->find($id)->toArray();
  96. $account = WaterAccount::query()->where(['enterprise_id'=>$task['enterprise_id'],'sku_id'=>$skuId])->first()->toArray();
  97. if ($task['status'] !== $samples[$index]['state'] || $task['scheduled_date'] !== $date
  98. || (int)$account['frozen'] !== $samples[$index]['quantity']) throw new RuntimeException('任务状态或权益不一致');
  99. }
  100. WaterLedger::query()->insert(['kind'=>'demo_seed','sku_id'=>$skuId,'quantity'=>0,'reference'=>$batch,
  101. 'note'=>'【测试数据】今日6单33桶;任务ID:'.implode(',',$taskIds).';2单待派单、3单同路线已派单、1单配送中,无真实收款',
  102. 'created_by'=>1,'created_at'=>date('Y-m-d H:i:s')]);
  103. return false;
  104. });
  105. }
  106. $board = $dispatch->board(['date'=>$date]);
  107. $rows = array_values(array_filter($board['tasks'],fn($task)=>in_array($task['enterprise_name'],array_column($samples,'name'),true)));
  108. echo json_encode(['mode'=>$apply?'applied':'inspect','date'=>$date,'batch'=>$batch,'replayed'=>$replayed,
  109. 'before'=>$before,'after'=>$board['summary'],'added_batch'=>array_map(fn($row)=>array_intersect_key($row,array_flip([
  110. 'id','enterprise_name','quantity','status','scheduled_date','deadline_at','route_id','stop_order','driver',
  111. ])),$rows),'plan'=>$samples],JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT).PHP_EOL;