DeviceOperationShares.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. <?php
  2. namespace plugin\saiadmin\app\service;
  3. /**
  4. * 读取既有代理、商户分成,不在设备运营中维护另一份比例。
  5. * 代理档案存占总交易额的比例;商户存相对直属代理的比例。
  6. */
  7. class DeviceOperationShares
  8. {
  9. private array $agents = [];
  10. private array $agentsByDepartment = [];
  11. public function __construct(array $agents)
  12. {
  13. foreach ($agents as $agent) {
  14. $rates = json_decode($agent['profit_rates'] ?? '{}', true);
  15. $agent['rates'] = is_array($rates) ? $rates : [];
  16. $this->agents[(int) $agent['id']] = $agent;
  17. $this->agentsByDepartment[(int) $agent['dept_id']][] = (int) $agent['id'];
  18. }
  19. }
  20. public function calculate(array $device, string $product): array
  21. {
  22. if (empty($device['merchant_id'])) {
  23. return ['merchant_rate' => 0.0, 'manager_rate' => 0.0];
  24. }
  25. $merchantAgent = (int) $device['merchant_agent_id'];
  26. $manager = (int) $device['manage_agent_id'];
  27. $parentRate = $this->agentRate($merchantAgent, $product);
  28. $relativeRate = $this->validRate($device['merchant_relative_rate']);
  29. // 不分成和入驻费没有按订单比例分成;明确的 0 不依赖上级档案。
  30. if (in_array($device['profit_method'], ['不分成', '入驻费'], true) || $relativeRate === 0.0) {
  31. $merchantRate = 0.0;
  32. } else {
  33. $merchantRate = $parentRate !== null && $relativeRate !== null
  34. ? round($parentRate * $relativeRate / 100, 4) : null;
  35. }
  36. $managerRate = $this->agentRate($manager, $product);
  37. if ($manager === $merchantAgent) {
  38. $childRate = $merchantRate;
  39. } else {
  40. $childId = $this->findBranchChild($manager, $merchantAgent);
  41. // 0 表示管理人不在这条分成链上;NULL 表示关系有歧义,不能猜测。
  42. if ($childId === 0) {
  43. return ['merchant_rate' => $merchantRate, 'manager_rate' => 0.0];
  44. }
  45. $childRate = $childId === null ? null : $this->agentRate($childId, $product);
  46. }
  47. $netManagerRate = $managerRate !== null && $childRate !== null && $managerRate >= $childRate
  48. ? round($managerRate - $childRate, 4) : null;
  49. return ['merchant_rate' => $merchantRate, 'manager_rate' => $netManagerRate];
  50. }
  51. private function agentRate(int $id, string $product): ?float
  52. {
  53. return $this->validRate($this->agents[$id]['rates'][$product] ?? null);
  54. }
  55. private function validRate(mixed $value): ?float
  56. {
  57. return is_numeric($value) && $value >= 0 && $value <= 100 ? (float) $value : null;
  58. }
  59. /** 沿商户代理的部门祖先路径,查找管理人下面参与该笔分成的第一个代理。 */
  60. private function findBranchChild(int $managerId, int $merchantAgentId): ?int
  61. {
  62. $manager = $this->agents[$managerId] ?? null;
  63. $merchantAgent = $this->agents[$merchantAgentId] ?? null;
  64. if (!$manager || !$merchantAgent) {
  65. return null;
  66. }
  67. $path = array_map('intval', array_filter(explode(',', $merchantAgent['dept_level'] ?? ''), 'strlen'));
  68. $path[] = (int) $merchantAgent['dept_id'];
  69. $position = array_search((int) $manager['dept_id'], $path, true);
  70. if ($position === false) {
  71. return 0;
  72. }
  73. if ((int) $manager['dept_id'] === (int) $merchantAgent['dept_id']) {
  74. return null;
  75. }
  76. foreach (array_slice($path, $position + 1) as $departmentId) {
  77. if ($departmentId === (int) $merchantAgent['dept_id']) {
  78. return $merchantAgentId;
  79. }
  80. $candidates = $this->agentsByDepartment[$departmentId] ?? [];
  81. if (count($candidates) > 1) {
  82. return null;
  83. }
  84. if (count($candidates) === 1) {
  85. return $candidates[0];
  86. }
  87. }
  88. return null;
  89. }
  90. }