seed_water_agent_ranking_demo.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. <?php
  2. /** Clearly marked simulated sales; --apply writes a fixed batch once, --http verifies the read API. */
  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,WaterAgent,WaterEnterprise,WaterOperator,WaterLedger,WaterAccount,WaterOrder,WaterDelivery,WaterSku,WaterPackage,WaterDevice};
  9. use plugin\saiadmin\app\service\{WaterBusiness,WaterAgentDashboard};
  10. const RANKING_BATCH = 'TEST-WATER-AGENT-RANKING-20260916';
  11. const RANKING_TAG = '【业绩测试0916】';
  12. function ensure(bool $ok,string $message): void { if (!$ok) throw new RuntimeException($message); }
  13. function existingRows(): array {
  14. $snapshot=[];
  15. foreach ([WaterAgent::class,WaterEnterprise::class,WaterAccount::class,WaterOrder::class,WaterDelivery::class,
  16. WaterSku::class,WaterPackage::class,WaterDevice::class,WaterLedger::class] as $model) {
  17. $snapshot[$model]=$model::query()->orderBy('id')->get()->toArray();
  18. }
  19. return $snapshot;
  20. }
  21. function rankingSummary(array $ranking): array {
  22. return array_map(fn($row)=>['agent_id'=>$row['id'],'agent'=>$row['name'],'month_yuan'=>$row['month_cents']/100],$ranking);
  23. }
  24. $samples=[
  25. ['customer'=>'星澜科技','agent_id'=>1,'cents'=>120000,'buckets'=>60],
  26. ['customer'=>'青禾创意','agent_id'=>1,'cents'=>80000,'buckets'=>40],
  27. ['customer'=>'云帆商贸','agent_id'=>6,'cents'=>90000,'buckets'=>45],
  28. ['customer'=>'沐光设计','agent_id'=>6,'cents'=>60000,'buckets'=>30],
  29. ['customer'=>'森屿办公','agent_id'=>7,'cents'=>60000,'buckets'=>30],
  30. ];
  31. $result=['applied'=>false,'batch'=>RANKING_BATCH,'planned_orders'=>5,'planned_yuan'=>4100,'planned_entitlement'=>205,
  32. 'ranking_before'=>rankingSummary((new WaterAgentDashboard())->dashboard([])['ranking'])];
  33. if (in_array('--apply',$argv,true)) {
  34. $result+=WaterModel::transaction(function () use ($samples): array {
  35. ensure((bool)WaterOperator::query()->where('id',1)->lockForUpdate()->first(),'缺少操作账号');
  36. if (WaterLedger::query()->where(['kind'=>'demo_seed','reference'=>RANKING_BATCH])->exists()) return ['replayed'=>true];
  37. ensure(date('Y-m-d')==='2026-09-16','此固定批次首次只能在2026-09-16执行,避免误写其他月份');
  38. ensure(!WaterSku::query()->where('name','like',RANKING_TAG.'%')->exists(),'已有同名测试规格,禁止覆盖');
  39. ensure(!WaterPackage::query()->where('name','like',RANKING_TAG.'%')->exists(),'已有同名测试套餐,禁止覆盖');
  40. ensure(!WaterOrder::query()->where('reference','like',RANKING_BATCH.'%')->exists(),'已有批次订单但缺少完成标记,请核对');
  41. $before=existingRows();
  42. $dashboard=new WaterAgentDashboard(); $beforeSales=[];
  43. foreach ([1,6,7] as $id) {
  44. $agent=$dashboard->detail($id)['agent'];
  45. ensure($agent['status']===1,'测试关联代理已停用');
  46. $beforeSales[$id]=$agent['month_cents'];
  47. }
  48. $service=new WaterBusiness();
  49. $sku=$service->save('sku',['name'=>RANKING_TAG.'18.9L模拟水','liters'=>18.9,'safety_stock'=>0,'status'=>1],1)['id'];
  50. $orders=[]; $expected=[];
  51. foreach ($samples as $index=>$sample) {
  52. $customers=WaterEnterprise::query()->where('name','【代理测试0916】'.$sample['customer'])->lockForUpdate()->get();
  53. ensure($customers->count()===1,'测试客户缺失或重名:'.$sample['customer']);
  54. $customer=$customers->first();
  55. ensure((int)$customer->agent_id===$sample['agent_id'] && (int)$customer->status===1,'测试客户归属或状态已变化,禁止覆盖');
  56. $packageValues=['name'=>RANKING_TAG.'模拟套餐'.($index+1),'sku_id'=>$sku,'buckets'=>$sample['buckets'],
  57. 'price_cents'=>$sample['cents'],'status'=>1];
  58. $package=$service->save('package',$packageValues,1)['id'];
  59. $reference=RANKING_BATCH.'-'.($index+1);
  60. $order=$service->purchase(['enterprise_id'=>(int)$customer->id,'package_id'=>$package,'reference'=>$reference],1);
  61. WaterLedger::query()->where(['kind'=>'purchase','reference'=>$reference])->update([
  62. 'note'=>'【测试数据】代理业绩排行模拟订单及权益,未发生真实收款,请勿实际配送',
  63. ]);
  64. $saved=WaterOrder::query()->findOrFail($order['id']);
  65. ensure((int)$saved->amount_cents===$sample['cents'] && (int)$saved->buckets===$sample['buckets'],'订单数据不一致');
  66. $account=WaterAccount::query()->where(['enterprise_id'=>$customer->id,'sku_id'=>$sku])->firstOrFail();
  67. ensure((int)$account->entitlement===$sample['buckets'] && (int)$account->frozen===0,'测试权益不一致');
  68. $expected[$sample['agent_id']]=($expected[$sample['agent_id']]??0)+$sample['cents'];
  69. $orders[]=['id'=>$order['id'],'customer'=>$customer->name,'agent_id'=>$sample['agent_id'],
  70. 'amount_yuan'=>$sample['cents']/100,'buckets'=>$sample['buckets']];
  71. // Keep the historical order and entitlement; prevent accidental further purchases of this demo package.
  72. $service->save('package',['id'=>$package]+array_replace($packageValues,['status'=>2]),1);
  73. }
  74. $service->save('sku',['id'=>$sku,'name'=>RANKING_TAG.'18.9L模拟水','liters'=>18.9,'safety_stock'=>0,'status'=>2],1);
  75. foreach ($expected as $agentId=>$cents) {
  76. ensure((new WaterAgentDashboard())->detail($agentId)['agent']['month_cents']-$beforeSales[$agentId]===$cents,'代理本月业绩增量不一致');
  77. }
  78. foreach ($before as $model=>$rows) {
  79. $after=$model::query()->whereIn('id',array_column($rows,'id'))->orderBy('id')->get()->toArray();
  80. ensure($rows===$after,'已有业务记录发生变化:'.$model);
  81. }
  82. ensure(WaterDelivery::query()->count()===count($before[WaterDelivery::class]),'不得创建配送任务');
  83. WaterLedger::query()->create(['kind'=>'demo_seed','reference'=>RANKING_BATCH,'created_by'=>1,'enterprise_id'=>0,
  84. 'sku_id'=>$sku,'quantity'=>205,'amount_cents'=>410000,'note'=>'代理排行模拟数据;5单4100元,无真实收款;规格及套餐已停用',
  85. 'created_at'=>date('Y-m-d H:i:s')]);
  86. return ['replayed'=>false,'sku_id'=>$sku,'orders'=>$orders,'checks'=>'sales, entitlements, prior rows and no delivery verified'];
  87. });
  88. $result['applied']=true;
  89. }
  90. $expectedRanking=(new WaterAgentDashboard())->dashboard([])['ranking'];
  91. $result['ranking_after']=rankingSummary($expectedRanking);
  92. if (in_array('--http',$argv,true)) {
  93. $token=\Tinywan\Jwt\JwtToken::generateToken(['id'=>1,'username'=>'admin','plat'=>'saiadmin','type'=>'web',
  94. 'client'=>RANKING_BATCH,'access_exp'=>120,'refresh_exp'=>120])['access_token'];
  95. $ch=curl_init('http://127.0.0.1:8787/core/water/agents/dashboard');
  96. curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_TIMEOUT=>15,CURLOPT_HTTPHEADER=>['Authorization: Bearer '.$token]]);
  97. $raw=curl_exec($ch); $status=curl_getinfo($ch,CURLINFO_HTTP_CODE); curl_close($ch); unset($token);
  98. $body=json_decode((string)$raw,true);
  99. ensure($status===200 && ($body['code']??0)===200,'HTTP业绩排行查询失败');
  100. ensure(rankingSummary($body['data']['ranking'])===rankingSummary($expectedRanking),'HTTP排行与业务查询不一致');
  101. $result['http_ranking_verified']=true;
  102. }
  103. echo json_encode($result,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE).PHP_EOL;