Access.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <?php
  2. declare(strict_types=1);
  3. namespace app\miniapi\service;
  4. use app\miniapi\auth\Principal;
  5. use app\miniapi\model\MiniUser;
  6. use app\miniapi\model\MiniGrant;
  7. use app\miniapi\exception\ApiException;
  8. use plugin\saiadmin\app\model\water\WaterEnterprise;
  9. use plugin\saiadmin\app\model\water\WaterDriver;
  10. use plugin\saiadmin\app\model\water\WaterAgent;
  11. use plugin\saiadmin\app\model\water\WaterDelivery;
  12. /** Resource permissions are loaded every request; tokens do not contain mutable roles. */
  13. final class Access
  14. {
  15. private const ROLE_MODELS = [
  16. 'enterprise_admin' => WaterEnterprise::class,
  17. 'enterprise_member' => WaterEnterprise::class,
  18. 'driver' => WaterDriver::class,
  19. 'agent' => WaterAgent::class,
  20. ];
  21. public static function roles(): array
  22. {
  23. return array_keys(self::ROLE_MODELS);
  24. }
  25. public static function resourceModel(string $role): ?string
  26. {
  27. return self::ROLE_MODELS[$role] ?? null;
  28. }
  29. public function __construct(public readonly Principal $actor)
  30. {
  31. }
  32. private function grants()
  33. {
  34. if (!MiniUser::query()->where('id', $this->actor->userId)->where('status', 1)->exists()) {
  35. throw new ApiException(40101, '登录状态已失效', 401);
  36. }
  37. return MiniGrant::query()->where('user_id', $this->actor->userId)->where('status', 1);
  38. }
  39. public function identities(): array
  40. {
  41. $rows = [];
  42. foreach ($this->grants()->orderBy('id')->get() as $grant) {
  43. $model = self::resourceModel($grant->role);
  44. if (!$model) {
  45. continue;
  46. }
  47. $resource = $model::query()->where('id', $grant->resource_id)->where('status', 1)->first();
  48. if ($resource) {
  49. $rows[] = [
  50. 'role' => $grant->role,
  51. 'resource_id' => (int) $resource->id,
  52. 'name' => $resource->name,
  53. ];
  54. }
  55. }
  56. if (count($rows) > 1) {
  57. throw new ApiException(40903, '账号绑定了多个业务身份,请联系管理员处理', 409);
  58. }
  59. return $rows;
  60. }
  61. public function enterprise(int $id, bool $write = false): WaterEnterprise
  62. {
  63. $roles = $write ? ['enterprise_admin'] : ['enterprise_admin', 'enterprise_member'];
  64. if (!$this->grants()->whereIn('role', $roles)->where('resource_id', $id)->exists()) {
  65. $this->deny();
  66. }
  67. $row = WaterEnterprise::query()->where('id', $id)->where('status', 1)->first();
  68. if (!$row) {
  69. $this->deny();
  70. }
  71. return $row;
  72. }
  73. public function isEnterpriseAdmin(int $id): bool
  74. {
  75. return $this->grants()->where([
  76. 'role' => 'enterprise_admin',
  77. 'resource_id' => $id,
  78. ])->exists();
  79. }
  80. public function resource(string $role, int $id)
  81. {
  82. if (!in_array($role, ['driver', 'agent'], true) || !$this->grants()->where([
  83. 'role' => $role,
  84. 'resource_id' => $id,
  85. ])->exists()) {
  86. $this->deny();
  87. }
  88. $model = self::resourceModel($role);
  89. $row = $model::query()->where('id', $id)->where('status', 1)->first();
  90. if (!$row) {
  91. $this->deny();
  92. }
  93. return $row;
  94. }
  95. public function driverTask(int $driver, int $task): WaterDelivery
  96. {
  97. $this->resource('driver', $driver);
  98. $row = WaterDelivery::query()->where('id', $task)->where('driver_id', $driver)->first();
  99. if (!$row) {
  100. throw new ApiException(40401, '配送任务不存在', 404);
  101. }
  102. return $row;
  103. }
  104. public function enterpriseTask(int $enterprise, int $task): WaterDelivery
  105. {
  106. $this->enterprise($enterprise);
  107. $row = WaterDelivery::query()->where('id', $task)->where('enterprise_id', $enterprise)->first();
  108. if (!$row) {
  109. throw new ApiException(40401, '配送任务不存在', 404);
  110. }
  111. return $row;
  112. }
  113. public function agentIds(int $id): array
  114. {
  115. $this->resource('agent', $id);
  116. $ids = [$id];
  117. $frontier = [$id];
  118. // Existing hierarchy is limited to three levels. Refuse malformed cycles rather than widening scope.
  119. for ($depth = 0;$depth < 3 && $frontier;$depth++) {
  120. $next = WaterAgent::query()->whereIn('parent_id', $frontier)->where('status', 1)->pluck('id')->map(fn ($v) => (int) $v)->all();
  121. if (array_intersect($next, $ids) || ($depth === 2 && $next)) {
  122. throw new ApiException(40903, '代理关系异常,请联系管理员', 409);
  123. }
  124. $ids = array_merge($ids, $next);
  125. $frontier = $next;
  126. }
  127. return $ids;
  128. }
  129. private function deny(): never
  130. {
  131. throw new ApiException(40301, '无权访问该业务身份或资源已停用', 403);
  132. }
  133. }