| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- <?php
- 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\{WaterAlertSetting,WaterAccount,WaterEnterprise,WaterSku,WaterLedger,WaterDelivery};
- use plugin\saiadmin\app\service\{WaterBusiness,WaterAlertDashboard,WaterEnterpriseDashboard};
- use plugin\saiadmin\app\service\alerts\AlertSettings;
- use plugin\saiadmin\app\controller\water\WaterController;
- use plugin\saiadmin\exception\ApiException;
- $checks=0;
- function check(bool $ok,string $label): void { global $checks; $checks++; if (!$ok) throw new RuntimeException($label); }
- function rejects(callable $fn,string $label): void { try {$fn();} catch (ApiException $e) {check(true,$label);return;} throw new RuntimeException($label); }
- function req(string $method,string $url,array $input=[]): \support\Request {
- $body=$method==='POST'?json_encode($input):'';
- return new \support\Request($method.' '.$url." HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nContent-Length: ".strlen($body)."\r\n\r\n".$body);
- }
- $before=[];
- foreach ([WaterAlertSetting::class,WaterEnterprise::class,WaterAccount::class,WaterSku::class,WaterLedger::class,WaterDelivery::class] as $model) $before[$model]=hash('sha256',serialize($model::query()->orderBy('id')->get()->toArray()));
- $db=(new WaterAccount())->getConnection(); $db->beginTransaction();
- try {
- $settings=new AlertSettings(); $business=new WaterBusiness(); $tag='POLICY-'.bin2hex(random_bytes(4));
- $original=$settings->current();
- $enterprise=$business->save('enterprise',['name'=>$tag,'contact'=>'TEST','phone'=>'TEST','address'=>'TEST','employee_count'=>3],1)['id'];
- $sku=$business->save('sku',['name'=>$tag,'liters'=>18.9],1)['id'];
- $account=WaterAccount::query()->insertGetId(['enterprise_id'=>$enterprise,'sku_id'=>$sku,'full_qty'=>5,'threshold_qty'=>999,'daily_liters'=>999,'entitlement'=>20,'updated_at'=>'2026-09-17 12:00:00']);
- $accountBefore=WaterAccount::query()->find($account)->toArray();
- $values=['alert_qty'=>8,'days_per_barrel'=>6,'urgent_qty'=>4,'revision'=>$original['revision']];
- $saved=$business->saveAlertPolicy($values,1);
- check($saved['revision']===$original['revision']+1 && $saved['alert_qty']===8,'saved platform parameters and version');
- check($settings->current()===$saved,'fresh read sees saved values');
- $now=new DateTimeImmutable('2026-09-17 12:00:00');
- $board=(new WaterAlertDashboard($now))->dashboard(['enterprise_id'=>$enterprise]);
- $row=$board['list']['data'][0];
- check($board['policy']===$saved && $board['list']['total']===1,'dashboard reads persisted settings');
- check($row['severity']==='normal' && $row['reasons']===['现场满桶少于8桶'],'configured quantity and severity in SQL and presentation');
- check($row['days_left']===10.0 && $row['estimated_daily_liters']===9.45 && $row['estimated_daily_barrels']===0.5,'six days per barrel changes all estimates');
- check($row['exhausts_at']==='2026-09-27 12:00:00','configured forecast timestamp');
- check((new WaterAlertDashboard($now))->listing(['enterprise_id'=>$enterprise,'exhaust_date'=>'2026-09-27'])['total']===1,'configured date SQL matches');
- $accounts=$business->listing('accounts',['enterprise_id'=>$enterprise]);
- check($accounts['data'][0]['days_left']===10.0 && $accounts['data'][0]['severity']==='normal','account view shares settings');
- check((new WaterEnterpriseDashboard())->detail($enterprise)['enterprise']['replenishment_status']==='pending','enterprise pending follows global threshold');
- check(WaterAccount::query()->find($account)->toArray()===$accountBefore,'settings do not modify inventory, rights or old account fields');
- $audit=WaterLedger::query()->where('reference','platform:alerts')->orderByDesc('id')->first();
- check($audit->kind==='alert_policy' && (int)$audit->created_by===1 && (int)$audit->enterprise_id===0,'platform change audited');
- $count=WaterLedger::query()->count();
- rejects(fn()=>$business->saveAlertPolicy($values,1),'stale revision rejected');
- $valid=['alert_qty'=>8,'days_per_barrel'=>6,'urgent_qty'=>4,'revision'=>$saved['revision']];
- foreach ([['alert_qty'=>0],['alert_qty'=>10000001],['alert_qty'=>1.5],['alert_qty'=>true],['alert_qty'=>[]],['days_per_barrel'=>0],['days_per_barrel'=>366],['days_per_barrel'=>null],['urgent_qty'=>-1],['urgent_qty'=>8],['revision'=>null],['id'=>1],['threshold_qty'=>9],['enterprise_id'=>$enterprise]] as $invalid) rejects(fn()=>$business->saveAlertPolicy(array_replace($valid,$invalid),1),'invalid or per-company override rejected');
- check($settings->current()===$saved && WaterLedger::query()->count()===$count,'failed writes preserve settings and audit');
- $reflection=new ReflectionClass(WaterController::class); $controller=$reflection->newInstanceWithoutConstructor();
- $reflection->getProperty('adminId')->setValue($controller,0);
- rejects(fn()=>$controller->alertPolicy(req('GET','/core/water/alerts/policy')),'policy read requires permission');
- rejects(fn()=>$controller->save(req('POST','/core/water/alerts',['action'=>'policy']+$valid),'alerts'),'policy write requires permission');
- $reflection->getProperty('adminId')->setValue($controller,1);
- $read=json_decode($controller->alertPolicy(req('GET','/core/water/alerts/policy'))->rawBody(),true);
- check($read['data']===$saved,'policy controller reads current global parameters');
- $response=json_decode($controller->save(req('POST','/core/water/alerts',['action'=>'policy']+array_replace($valid,['urgent_qty'=>5])),'alerts')->rawBody(),true);
- check($response['code']===200 && $response['data']['urgent_qty']===5,'JSON controller saves platform policy');
- check((new WaterAlertDashboard())->listing(['enterprise_id'=>$enterprise,'severity'=>'urgent'])['total']===1,'urgent SQL filter uses saved threshold');
- check((new WaterEnterpriseDashboard())->detail($enterprise)['enterprise']['replenishment_status']==='urgent','enterprise urgent follows edited global threshold');
- $updated=$settings->current();
- $business->saveAlertPolicy(['alert_qty'=>5,'days_per_barrel'=>3,'urgent_qty'=>0,'revision'=>$updated['revision']],1);
- check((new WaterAlertDashboard())->listing(['enterprise_id'=>$enterprise])['total']===0,'equality at edited threshold excludes alert');
- check((new WaterEnterpriseDashboard())->detail($enterprise)['enterprise']['replenishment_status']==='healthy','enterprise returns healthy after threshold edit');
- WaterAccount::query()->where('id',$account)->update(['full_qty'=>0]);
- check((new WaterAlertDashboard())->listing(['enterprise_id'=>$enterprise,'severity'=>'urgent'])['total']===1,'zero urgent threshold correctly alerts zero stock');
- } finally { $db->rollBack(); }
- foreach ($before as $model=>$hash) check(hash('sha256',serialize($model::query()->orderBy('id')->get()->toArray()))===$hash,'original model restored: '.$model);
- if (in_array('--http',$argv,true)) {
- $token=\Tinywan\Jwt\JwtToken::generateToken(['id'=>1,'username'=>'admin','plat'=>'saiadmin','type'=>'web','client'=>'policy-test','access_exp'=>120,'refresh_exp'=>120])['access_token'];
- $ch=curl_init('http://127.0.0.1:8787/core/water/alerts/policy');
- curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_TIMEOUT=>15,CURLOPT_HTTPHEADER=>['Authorization: Bearer '.$token]]);
- $body=json_decode((string)curl_exec($ch),true); $status=curl_getinfo($ch,CURLINFO_HTTP_CODE); curl_close($ch);
- check($status===200 && ($body['code']??0)===200 && $body['data']===(new AlertSettings())->current(),'HTTP current persisted policy');
- }
- echo "PASS: $checks platform policy checks; fixtures and settings rolled back.\n";
|