AgentFlowCommissionAssignmentService.php 3.2 KB

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