| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- <?php
- /** 罗湖/福田/宝安今日配送演示;默认只读,--apply 原子写入,固定批次防重。 */
- declare(strict_types=1);
- require dirname(__DIR__).'/vendor/autoload.php';
- \Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__))->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-DISTRICTS-20260911';
- $date = '2026-09-11';
- $apply = in_array('--apply',$argv,true);
- $origin = ['origin_address'=>'深圳市南山区南油第四工业区4栋(测试出发点)',
- 'origin_longitude'=>113.924140,'origin_latitude'=>22.522806];
- // 各区内人工模拟选点,仅供地图和调度测试,不代表真实企业或已核实的门牌位置。
- $districts = [
- ['name'=>'罗湖','code'=>'LH','stops'=>[
- ['name'=>'东门商贸','quantity'=>4,'longitude'=>114.117000,'latitude'=>22.547000],
- ['name'=>'翠竹办公','quantity'=>6,'longitude'=>114.130000,'latitude'=>22.563000],
- ]],
- ['name'=>'福田','code'=>'FT','stops'=>[
- ['name'=>'中心商务','quantity'=>5,'longitude'=>114.057000,'latitude'=>22.543000],
- ['name'=>'车公庙研发','quantity'=>7,'longitude'=>114.025000,'latitude'=>22.536000],
- ]],
- ['name'=>'宝安','code'=>'BA','stops'=>[
- ['name'=>'新安科技','quantity'=>6,'longitude'=>113.883000,'latitude'=>22.554000],
- ['name'=>'西乡创意','quantity'=>8,'longitude'=>113.863000,'latitude'=>22.578000],
- ]],
- ];
- $dispatch = new WaterDispatch();
- $before = $dispatch->board(['date'=>$date])['summary'];
- $replayed = false;
- if ($apply) {
- $replayed = WaterModel::transaction(function () use ($batch,$date,$origin,$districts,$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();
- $oldAccounts = WaterAccount::query()->orderBy('id')->get()->toArray();
- $oldOrders = WaterOrder::query()->orderBy('id')->get()->toArray();
- $total = array_sum(array_map(fn($d)=>array_sum(array_column($d['stops'],'quantity')),$districts));
- $business->stock(['sku_id'=>$skuId,'mode'=>'inbound','quantity'=>$total,'reference'=>$batch.'-STOCK',
- 'note'=>'【测试数据】三区配送模拟入库36桶,未发生真实入库'],1);
- $groups = []; $taskIds = [];
- foreach ($districts as $district) {
- $region = '深圳市'.$district['name'].'区';
- $ids = [];
- foreach ($district['stops'] as $index=>$stop) {
- $name = '【测试0911】'.$district['name'].$stop['name'];
- if (WaterEnterprise::query()->where('name',$name)->exists()) throw new RuntimeException('同名测试企业已存在,停止避免覆盖');
- $enterpriseId = $business->save('enterprise',[
- 'name'=>$name,'contact'=>'片区测试联系人','phone'=>'TEST-0911-'.$district['code'].'-'.($index+1),
- 'region'=>$region,'address'=>$region.$stop['name'].'测试点(虚构企业、模拟坐标,请勿实际配送)',
- 'longitude'=>$stop['longitude'],'latitude'=>$stop['latitude'],'status'=>1,
- ],1)['id'];
- WaterAccount::query()->insert(['enterprise_id'=>$enterpriseId,'sku_id'=>$skuId,
- 'entitlement'=>$stop['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'=>$stop['quantity'],'reference'=>$batch.'-RIGHTS-'.$district['code'].'-'.$index,
- 'note'=>'【测试数据】三区调度模拟权益,无真实收款','created_by'=>1,'created_at'=>date('Y-m-d H:i:s')]);
- $id = $business->createDelivery(['enterprise_id'=>$enterpriseId,'sku_id'=>$skuId,'quantity'=>$stop['quantity'],'date'=>$date],1)['id'];
- $dispatch->schedule($origin+['id'=>$id,'date'=>$date,'deadline_at'=>$date.($index===0?' 17:00:00':' 18:00:00'),
- 'longitude'=>$stop['longitude'],'latitude'=>$stop['latitude']],1);
- $ids[] = $id; $taskIds[] = $id;
- }
- $driverId = $dispatch->saveDriver(['name'=>'【测试0911】'.$district['name'].'配送员',
- 'phone'=>'TEST-0911-DRIVER-'.$district['code'],'region'=>$region,'capacity'=>30,
- 'vehicle'=>'测试车辆-'.$district['code']],1)['id'];
- $dispatch->shift(['driver_id'=>$driverId,'date'=>$date,'state'=>'on'],1);
- $groups[] = ['driver_id'=>$driverId,'task_ids'=>$ids];
- }
- $result = $dispatch->commit(['date'=>$date,'request_key'=>$batch,'routes'=>$groups],1);
- if (count($result['route_ids']) !== 3) throw new RuntimeException('路线数量不正确');
- $afterSku = WaterSku::query()->find($skuId)->toArray();
- if ((int)$afterSku['available'] !== (int)$sku['available'] || (int)$afterSku['locked'] !== (int)$sku['locked']+$total
- || (int)$afterSku['transit'] !== (int)$sku['transit']) 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 ($groups as $index=>$group) {
- $route = $dispatch->route(['id'=>$result['route_ids'][$index]]);
- if (count($route['tasks']) !== 2) throw new RuntimeException('每条测试路线必须包含两个站点');
- foreach ($route['tasks'] as $task) {
- $account = WaterAccount::query()->where(['enterprise_id'=>$task['enterprise_id'],'sku_id'=>$skuId])->first()->toArray();
- if ($task['status'] !== 'assigned' || $task['scheduled_date'] !== $date
- || $task['region'] !== '深圳市'.$districts[$index]['name'].'区'
- || (int)$account['frozen'] !== (int)$task['quantity']) throw new RuntimeException('片区、任务状态或冻结权益不一致');
- }
- }
- WaterLedger::query()->insert(['kind'=>'demo_seed','sku_id'=>$skuId,'quantity'=>0,'reference'=>$batch,
- 'note'=>'【测试数据】罗湖、福田、宝安各2单,合计36桶及3条两站路线;模拟坐标;任务ID:'.implode(',',$taskIds),
- 'created_by'=>1,'created_at'=>date('Y-m-d H:i:s')]);
- return false;
- });
- }
- $board = $dispatch->board(['date'=>$date]);
- $filters = [];
- foreach ($districts as $district) {
- $region = '深圳市'.$district['name'].'区';
- $filtered = $dispatch->board(['date'=>$date,'region'=>$region]);
- $filters[$region] = ['summary'=>$filtered['summary'],'route_ids'=>array_values(array_unique(array_column($filtered['tasks'],'route_id'))),
- 'tasks'=>array_map(fn($row)=>array_intersect_key($row,array_flip(['id','enterprise_name','quantity','status','route_id','stop_order'])),$filtered['tasks'])];
- }
- echo json_encode(['mode'=>$apply?'applied':'inspect','batch'=>$batch,'replayed'=>$replayed,'before'=>$before,
- 'after'=>$board['summary'],'areas'=>$board['areas'],'district_filters'=>$filters],JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT).PHP_EOL;
|