toDateString(); $assignment = AgentProfitCommissionAssignment::query() ->where('agent_id', $agent->id) ->where('effective_from', '<=', $day) ->orderByDesc('effective_from') ->orderByDesc('id') ->first(); if (!$assignment) { throw new \RuntimeException("代理 {$agent->id} 缺少 {$day} 可用的盈亏佣金比例历史"); } return $assignment; } public function assign( Agent $agent, AgentProfitCommissionProfile $profile, string $effectiveFrom, ?int $createdBy = null ): AgentProfitCommissionAssignment { $this->assignMany([(int) $agent->id], $profile, $effectiveFrom, $createdBy); return AgentProfitCommissionAssignment::query() ->where('agent_id', $agent->id) ->where('effective_from', Carbon::parse($effectiveFrom)->toDateString()) ->firstOrFail(); } public function assignMany( array $agentIds, AgentProfitCommissionProfile $profile, string $effectiveFrom, ?int $createdBy = null ): void { $agentIds = array_values(array_unique(array_map('intval', $agentIds))); if ($agentIds === []) return; $day = Carbon::parse($effectiveFrom)->toDateString(); $this->assertMutable($agentIds, $day); $values = array_merge($profile->rateSnapshot(), [ 'profit_commission_profile_id' => (int) $profile->id, 'profile_name' => (string) $profile->name, 'created_by' => $createdBy, ]); DB::transaction(function () use ($agentIds, $day, $values) { foreach (array_chunk($agentIds, 500) as $chunk) { foreach ($chunk as $agentId) { AgentProfitCommissionAssignment::query()->updateOrCreate( ['agent_id' => $agentId, 'effective_from' => $day], $values ); } } }); } private function assertMutable(array $agentIds, string $day): void { $newCredited = AgentRebateRecord::query()->whereIn('agent_id', $agentIds) ->where('settlement_date', '>=', $day) ->where('profit_status', AgentRebateRecord::STATUS_CREDITED) ->exists(); $legacyCredited = AgentCommission::query()->whereIn('agent_id', $agentIds) ->where('type', 'profit') ->where('settlement_date', '>=', $day) ->where('status', AgentRebateRecord::STATUS_CREDITED) ->exists(); if ($newCredited || $legacyCredited) { throw new \RuntimeException('生效日期已有盈亏佣金入账,不能修改比例方案'); } } }