seed_water_shenzhen_routes.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. <?php
  2. /** 两条独立地图测试路线;默认只读,--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 support\Db;
  9. use plugin\saiadmin\app\model\water\WaterModel;
  10. use plugin\saiadmin\app\service\WaterBusiness;
  11. use plugin\saiadmin\app\service\WaterDispatch;
  12. $batch='TEST-WATER-SZ-ROUTES-20260910';
  13. $apply=in_array('--apply',$argv,true);
  14. $origin=['origin_address'=>'深圳市南山区南油第四工业区4栋(测试出发点)','origin_longitude'=>113.924140,'origin_latitude'=>22.522806];
  15. // 项目高德 Geocoder 实查 GCJ-02;生态园结果为园区级,不能声称定位到506室。
  16. $targets=[
  17. ['name'=>'【测试0910】科技园配送测试','address'=>'深圳市南山区科技园(地图测试,请勿实际配送)','longitude'=>113.946040,'latitude'=>22.544610,'quantity'=>4,'suffix'=>'KJY'],
  18. ['name'=>'【测试0910】生态园10栋506','address'=>'深圳市南山区深圳湾科技生态园10栋506室(地图测试,坐标暂定位园区,请勿实际配送)','longitude'=>113.952743,'latitude'=>22.530351,'quantity'=>6,'suffix'=>'STY'],
  19. ];
  20. $replayed=false;
  21. if ($apply) {
  22. $replayed=WaterModel::transaction(function () use ($batch,$origin,$targets) {
  23. $business=new WaterBusiness(); $dispatch=new WaterDispatch();
  24. $sku=\plugin\saiadmin\app\model\water\WaterSku::query()->where('name','【测试0910】18.9L桶装水')->lock(true)->first()?->toArray();
  25. if (!$sku) throw new RuntimeException('缺少指定测试商品');
  26. if (\plugin\saiadmin\app\model\water\WaterLedger::query()->where(['kind'=>'demo_seed','reference'=>$batch])->first()?->toArray()) return true;
  27. if ((int)$sku['available']<10) throw new RuntimeException('测试商品可用库存不足10桶');
  28. $skuId=(int)$sku['id']; $date=date('Y-m-d'); $now=date('Y-m-d H:i:s');
  29. $beforeTasks=\plugin\saiadmin\app\model\water\WaterDelivery::query()->orderBy('id')->get()->toArray();
  30. $beforeOrders=\plugin\saiadmin\app\model\water\WaterOrder::query()->orderBy('id')->get()->toArray();
  31. $groups=[]; $taskIds=[];
  32. foreach ($targets as $target) {
  33. if (\plugin\saiadmin\app\model\water\WaterEnterprise::query()->where('name',$target['name'])->count()) throw new RuntimeException('存在同名测试企业,停止避免重复');
  34. $enterprise=$business->save('enterprise',[
  35. 'name'=>$target['name'],'contact'=>'地图测试联系人','phone'=>'TEST-MAP-'.$target['suffix'],
  36. 'region'=>'深圳市南山区','address'=>$target['address'],'longitude'=>$target['longitude'],'latitude'=>$target['latitude'],
  37. ],1)['id'];
  38. // 仅发放本次配送所需的测试权益,不制造收款或购套餐订单。
  39. \plugin\saiadmin\app\model\water\WaterAccount::query()->insert(['enterprise_id'=>$enterprise,'sku_id'=>$skuId,
  40. 'entitlement'=>$target['quantity'],'threshold_qty'=>2,'updated_at'=>$now]);
  41. \plugin\saiadmin\app\model\water\WaterLedger::query()->insert(['kind'=>'demo_seed','enterprise_id'=>$enterprise,'sku_id'=>$skuId,
  42. 'quantity'=>$target['quantity'],'reference'=>$batch.'-'.$target['suffix'].'-RIGHTS',
  43. 'note'=>'【测试数据】地图配送测试权益,无真实收款','created_by'=>1,'created_at'=>$now]);
  44. $task=$business->createDelivery(['enterprise_id'=>$enterprise,'sku_id'=>$skuId,'quantity'=>$target['quantity'],'date'=>$date],1)['id'];
  45. \plugin\saiadmin\app\model\water\WaterDelivery::query()->where('id',$task)->update($origin);
  46. $driver=$dispatch->saveDriver(['name'=>'【测试】地图配送员-'.$target['suffix'],'phone'=>'TEST-MAP-DRIVER-'.$target['suffix'],
  47. 'region'=>'深圳市南山区','capacity'=>20,'vehicle'=>'测试车辆-'.$target['suffix']],1)['id'];
  48. $dispatch->shift(['driver_id'=>$driver,'date'=>$date,'state'=>'on'],1);
  49. $groups[]=['driver_id'=>$driver,'task_ids'=>[$task]]; $taskIds[]=$task;
  50. }
  51. $result=$dispatch->commit(['date'=>$date,'request_key'=>$batch,'routes'=>$groups],1);
  52. if (count($result['route_ids'])!==2) throw new RuntimeException('未生成两条独立路线');
  53. $after=\plugin\saiadmin\app\model\water\WaterSku::query()->where('id',$skuId)->first()?->toArray();
  54. if ((int)$after['available']!==(int)$sku['available']-10 || (int)$after['locked']!==(int)$sku['locked']+10 || $after['transit']!==$sku['transit']) throw new RuntimeException('库存冻结校验失败');
  55. if (\plugin\saiadmin\app\model\water\WaterOrder::query()->orderBy('id')->get()->toArray()!==$beforeOrders) throw new RuntimeException('原收款订单意外改变');
  56. foreach ($beforeTasks as $task) {
  57. if (\plugin\saiadmin\app\model\water\WaterDelivery::query()->where('id',$task['id'])->first()?->toArray()!==$task) throw new RuntimeException('原配送任务意外改变');
  58. }
  59. foreach ($taskIds as $id) {
  60. $task=\plugin\saiadmin\app\model\water\WaterDelivery::query()->where('id',$id)->first()?->toArray();
  61. $account=\plugin\saiadmin\app\model\water\WaterAccount::query()->where(['enterprise_id'=>$task['enterprise_id'],'sku_id'=>$skuId])->first()?->toArray();
  62. if ($task['status']!=='assigned' || !$task['route_id'] || (int)$account['frozen']!==(int)$task['quantity']) throw new RuntimeException('路线与权益冻结不一致');
  63. }
  64. \plugin\saiadmin\app\model\water\WaterLedger::query()->insert(['kind'=>'demo_seed','sku_id'=>$skuId,'quantity'=>0,'reference'=>$batch,
  65. 'note'=>'【测试数据】新增科技园4桶、生态园10栋506室6桶两条已派单路线;任务ID:'.implode(',',$taskIds).';生态园为园区级坐标,无真实收款',
  66. 'created_by'=>1,'created_at'=>$now]);
  67. return false;
  68. });
  69. }
  70. $rows=\plugin\saiadmin\app\model\water\WaterDelivery::query()->alias('d')->join('sa_water_enterprise as e','e.id','=','d.enterprise_id')
  71. ->leftJoin('sa_water_route as r','r.id','=','d.route_id')->whereIn('e.name',array_column($targets,'name'))
  72. ->selectRaw('d.id,e.name,d.quantity,d.status,d.address,d.longitude,d.latitude,d.origin_address,d.origin_longitude,d.origin_latitude,d.driver,d.route_id,r.code AS route_code')->orderBy('d.id')->get()->toArray();
  73. echo json_encode(['mode'=>$apply?'applied':'inspect','replayed'=>$replayed,'plan'=>$targets,'deliveries'=>$rows,
  74. 'summary'=>(new WaterDispatch())->board(['date'=>date('Y-m-d')])['summary']],JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT).PHP_EOL;