AgentProfitCommissionAssignmentService.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. <?php
  2. namespace App\Services\Agent;
  3. use App\Models\Agent\Agent;
  4. use App\Models\Agent\AgentCommission;
  5. use App\Models\Agent\AgentProfitCommissionAssignment;
  6. use App\Models\Agent\AgentProfitCommissionProfile;
  7. use App\Models\Agent\AgentRebateRecord;
  8. use Carbon\Carbon;
  9. use Illuminate\Support\Facades\DB;
  10. class AgentProfitCommissionAssignmentService
  11. {
  12. public function at(Agent $agent, string $date): AgentProfitCommissionAssignment
  13. {
  14. $day = Carbon::parse($date)->toDateString();
  15. $assignment = AgentProfitCommissionAssignment::query()
  16. ->where('agent_id', $agent->id)
  17. ->where('effective_from', '<=', $day)
  18. ->orderByDesc('effective_from')
  19. ->orderByDesc('id')
  20. ->first();
  21. if (!$assignment) {
  22. throw new \RuntimeException("代理 {$agent->id} 缺少 {$day} 可用的盈亏佣金比例历史");
  23. }
  24. return $assignment;
  25. }
  26. public function assign(
  27. Agent $agent,
  28. AgentProfitCommissionProfile $profile,
  29. string $effectiveFrom,
  30. ?int $createdBy = null
  31. ): AgentProfitCommissionAssignment {
  32. $this->assignMany([(int) $agent->id], $profile, $effectiveFrom, $createdBy);
  33. return AgentProfitCommissionAssignment::query()
  34. ->where('agent_id', $agent->id)
  35. ->where('effective_from', Carbon::parse($effectiveFrom)->toDateString())
  36. ->firstOrFail();
  37. }
  38. public function assignMany(
  39. array $agentIds,
  40. AgentProfitCommissionProfile $profile,
  41. string $effectiveFrom,
  42. ?int $createdBy = null
  43. ): void {
  44. $agentIds = array_values(array_unique(array_filter(array_map('intval', $agentIds))));
  45. if ($agentIds === []) return;
  46. $day = Carbon::parse($effectiveFrom)->toDateString();
  47. $values = array_merge($profile->rateSnapshot(), [
  48. 'profit_commission_profile_id' => (int) $profile->id,
  49. 'profile_name' => (string) $profile->name,
  50. 'created_by' => $createdBy,
  51. ]);
  52. DB::transaction(function () use ($agentIds, $day, $values) {
  53. $this->assertMutable($agentIds, $day);
  54. foreach (array_chunk($agentIds, 500) as $chunk) {
  55. foreach ($chunk as $agentId) {
  56. AgentProfitCommissionAssignment::query()->updateOrCreate(
  57. ['agent_id' => $agentId, 'effective_from' => $day],
  58. $values
  59. );
  60. }
  61. }
  62. });
  63. }
  64. private function assertMutable(array $agentIds, string $day): void
  65. {
  66. $newCredited = AgentRebateRecord::query()->whereIn('agent_id', $agentIds)
  67. ->where('settlement_date', '>=', $day)
  68. ->lockForUpdate()
  69. ->pluck('profit_status')
  70. ->contains(AgentRebateRecord::STATUS_CREDITED);
  71. $legacyCredited = AgentCommission::query()->whereIn('agent_id', $agentIds)
  72. ->where('type', 'profit')
  73. ->where('settlement_date', '>=', $day)
  74. ->lockForUpdate()
  75. ->pluck('status')
  76. ->contains(AgentRebateRecord::STATUS_CREDITED);
  77. if ($newCredited || $legacyCredited) {
  78. throw new \RuntimeException('生效日期已有盈亏佣金入账,不能修改比例方案');
  79. }
  80. }
  81. }