WaterAgentDashboard.php 5.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. <?php
  2. declare(strict_types=1);
  3. namespace plugin\saiadmin\app\service;
  4. use plugin\saiadmin\app\model\water\{WaterModel,WaterAgent,WaterEnterprise,WaterOrder,WaterCommission};
  5. use plugin\saiadmin\app\service\agent\{AgentFilters,AgentHierarchyPolicy,AgentAccessPolicy};
  6. use plugin\saiadmin\app\service\agent\WaterCommissionPolicy;
  7. use plugin\saiadmin\exception\ApiException;
  8. /** Read facade. Water sales never reuse legacy device-rental income or profit percentages. */
  9. final class WaterAgentDashboard
  10. {
  11. private readonly \DateTimeImmutable $now;
  12. public function __construct(?\DateTimeImmutable $now=null) { $this->now=$now??new \DateTimeImmutable('now',new \DateTimeZone('Asia/Shanghai')); }
  13. private function rows(): array {
  14. $end=$this->now->format('Y-m-d H:i:s'); $start=$this->now->format('Y-m-01 00:00:00');
  15. $enterprises=WaterEnterprise::query()->selectRaw('agent_id, COUNT(*) AS enterprise_count')->groupBy('agent_id');
  16. $sales=WaterOrder::query()->alias('o')->join('sa_water_enterprise as e','e.id','=','o.enterprise_id')
  17. ->where('o.created_at','<=',$end)
  18. ->selectRaw('e.agent_id, SUM(o.amount_cents) AS sales_cents, SUM(CASE WHEN o.created_at >= ? THEN o.amount_cents ELSE 0 END) AS month_cents',[$start])->groupBy('e.agent_id');
  19. $rows=WaterAgent::query()->alias('a')
  20. ->leftJoin('sa_system_user as u',function ($join) { $join->on('u.id','=','a.user_id')->whereNull('u.delete_time'); })
  21. ->leftJoinSub($enterprises,'customers','customers.agent_id','=','a.id')
  22. ->leftJoinSub($sales,'sales','sales.agent_id','=','a.id')
  23. ->selectRaw('a.id,a.name,a.contact,a.phone,a.region,a.address,a.user_id,a.parent_id,a.status,a.created_at,a.updated_at,
  24. u.username,u.status AS operator_status,COALESCE(customers.enterprise_count,0) AS enterprise_count,
  25. COALESCE(sales.sales_cents,0) AS sales_cents,COALESCE(sales.month_cents,0) AS month_cents')
  26. ->orderByDesc('a.id')->get()->toArray();
  27. $levels=(new AgentHierarchyPolicy())->levels($rows); $access=new AgentAccessPolicy(); $granted=$access->grantedUserIds();
  28. $names=array_column($rows,'name','id');
  29. $pendingCommission=WaterCommission::query()->where('status','pending')
  30. ->selectRaw('agent_id,SUM(amount_cents) AS amount_cents')->groupBy('agent_id')
  31. ->get()->pluck('amount_cents','agent_id')->all();
  32. foreach ($rows as &$row) {
  33. foreach (['id','status','enterprise_count','sales_cents','month_cents'] as $key) $row[$key]=(int)$row[$key];
  34. $row['user_id']=$row['user_id']===null?null:(int)$row['user_id'];
  35. $row['parent_id']=$row['parent_id']===null?null:(int)$row['parent_id'];
  36. $row['level']=$levels[$row['id']];
  37. $row['parent_name']=$names[$row['parent_id']]??null;
  38. $row['authorization']=$access->state($row,$granted);
  39. $row['commission_cents']=(int)($pendingCommission[$row['id']]??0);
  40. }
  41. unset($row);
  42. return $rows;
  43. }
  44. public function dashboard(array $input): array {
  45. $filters=new AgentFilters($input);
  46. return WaterModel::transaction(function () use ($filters) {
  47. $all=$this->rows();
  48. $rows=array_values(array_filter($all,function ($row) use ($filters) {
  49. return ($filters->keyword==='' || mb_stripos($row['name'].' '.$row['phone'].' '.$row['contact'],$filters->keyword)!==false)
  50. && ($filters->level==='' || ($filters->level==='unset'?$row['level']===null:$row['level']===(int)$filters->level))
  51. && ($filters->authorization==='' || $row['authorization']===$filters->authorization);
  52. }));
  53. $sales=array_sum(array_column($all,'sales_cents'));
  54. $platform=(int)WaterOrder::query()->where('created_at','<=',$this->now->format('Y-m-d H:i:s'))->sum('amount_cents');
  55. $members=array_filter($all,fn($row)=>$row['parent_id']!==null && $row['parent_id']>0);
  56. $rank=array_values(array_filter($all,fn($row)=>$row['month_cents']>0));
  57. usort($rank,fn($a,$b)=>($b['month_cents']<=>$a['month_cents'])?:($a['id']<=>$b['id']));
  58. $start=$this->now->format('Y-m-01 00:00:00'); $end=$this->now->format('Y-m-d H:i:s');
  59. return ['list'=>['data'=>array_slice($rows,($filters->page-1)*$filters->limit,$filters->limit),'total'=>count($rows),
  60. 'current_page'=>$filters->page,'per_page'=>$filters->limit],
  61. 'summary'=>['total'=>count($all),'new_month'=>count(array_filter($all,fn($r)=>$r['created_at']>=$start && $r['created_at']<=$end)),
  62. 'sales_cents'=>$sales,'platform_share'=>$platform?round(100*$sales/$platform,1):null,'commission_cents'=>array_sum(array_column($all,'commission_cents')),
  63. 'members'=>count($members),'active_members'=>count(array_filter($members,fn($r)=>$r['status']===1 && $r['month_cents']>0))],
  64. 'ranking'=>array_slice($rank,0,4),
  65. 'options'=>array_map(fn($r)=>array_intersect_key($r,array_flip(['id','name','status','level'])),$all),
  66. 'commission'=>['configured'=>true,'message'=>WaterCommissionPolicy::message()],
  67. 'updated_at'=>$end];
  68. });
  69. }
  70. public function detail(int $id): array {
  71. return WaterModel::transaction(function () use ($id) {
  72. $all=$this->rows(); $map=array_column($all,null,'id');
  73. if (!isset($map[$id])) throw new ApiException('代理不存在');
  74. return ['agent'=>$map[$id],
  75. 'children'=>array_values(array_filter($all,fn($r)=>$r['parent_id']===$id)),
  76. 'enterprises'=>WaterEnterprise::query()->where('agent_id',$id)->orderByDesc('id')->limit(20)->get(['id','name','region','status'])->toArray()];
  77. });
  78. }
  79. }