feat: enhance agent management and validation logic
- Updated AGENTS.md to clarify agent account restrictions and permissions. - Implemented checks in AgentNodeAdminUserStoreController and AgentNodeRoleStoreController to restrict admin user and role creation to the agent's own node. - Enhanced validation in AdminPlayerStoreController and AdminPlayerUpdateController to enforce credit limit and rebate rate rules based on player funding mode. - Refactored various request classes to utilize shared admin account field rules for consistency. - Improved error handling in services related to credit allocation and rebate limits to ensure proper validation and messaging.
This commit is contained in:
@@ -26,10 +26,20 @@ final class AgentProfileService
|
||||
{
|
||||
$parent = $parent ?? ($node->parent_id !== null ? AgentNode::query()->find($node->parent_id) : null);
|
||||
|
||||
$totalShare = (float) ($payload['total_share_rate'] ?? 0);
|
||||
$creditLimit = (int) ($payload['credit_limit'] ?? 0);
|
||||
$rebateLimit = (float) ($payload['rebate_limit'] ?? 0) / 100;
|
||||
$defaultRebate = (float) ($payload['default_player_rebate'] ?? 0) / 100;
|
||||
$existingProfile = AgentProfile::query()->where('agent_node_id', $node->id)->first();
|
||||
|
||||
$totalShare = array_key_exists('total_share_rate', $payload)
|
||||
? (float) $payload['total_share_rate']
|
||||
: (float) ($existingProfile->total_share_rate ?? 0);
|
||||
$creditLimit = array_key_exists('credit_limit', $payload)
|
||||
? (int) $payload['credit_limit']
|
||||
: (int) ($existingProfile->credit_limit ?? 0);
|
||||
$rebateLimit = array_key_exists('rebate_limit', $payload)
|
||||
? (float) $payload['rebate_limit'] / 100
|
||||
: (float) ($existingProfile->rebate_limit ?? 0);
|
||||
$defaultRebate = array_key_exists('default_player_rebate', $payload)
|
||||
? (float) $payload['default_player_rebate'] / 100
|
||||
: (float) ($existingProfile->default_player_rebate ?? 0);
|
||||
|
||||
$useRelative = $parent !== null && array_key_exists('relative_share_rate', $payload);
|
||||
|
||||
@@ -68,18 +78,24 @@ final class AgentProfileService
|
||||
}
|
||||
|
||||
if ($parent !== null) {
|
||||
$delta = $isNew ? $creditLimit : max(0, $creditLimit - $previousCredit);
|
||||
if ($delta > 0) {
|
||||
$this->creditAllocationValidator->assertAllocationWithinParent($parent, $delta);
|
||||
}
|
||||
$this->creditAllocationValidator->assertChildCreditLimitWithinParent(
|
||||
$parent,
|
||||
$isNew ? 0 : $previousCredit,
|
||||
$creditLimit,
|
||||
$isNew,
|
||||
);
|
||||
}
|
||||
|
||||
if ($defaultRebate > $rebateLimit && $rebateLimit > 0) {
|
||||
if ($defaultRebate > $rebateLimit) {
|
||||
throw ValidationException::withMessages([
|
||||
'default_player_rebate' => ['exceeds_limit'],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($parent !== null) {
|
||||
$this->rebateLimitValidator->assertChildRebateLimitWithinParent($parent, $rebateLimit);
|
||||
}
|
||||
|
||||
$profile->fill([
|
||||
'total_share_rate' => $totalShare,
|
||||
'credit_limit' => $creditLimit,
|
||||
@@ -245,7 +261,7 @@ final class AgentProfileService
|
||||
*/
|
||||
public function assertChildCapabilityGrantsWithinParent(AgentNode $parent, array $childPayload, AdminUser $actor): void
|
||||
{
|
||||
if ($actor->isSuperAdmin()) {
|
||||
if ($actor->isSuperAdmin() || \App\Support\AdminAgentSettlementScope::canManageSitePeriods($actor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,13 +13,47 @@ final class CreditAllocationValidator
|
||||
private readonly AgentCreditAllocatedSyncService $allocatedSync,
|
||||
) {}
|
||||
|
||||
public function assertChildCreditLimitWithinParent(
|
||||
AgentNode $parent,
|
||||
int $previousChildLimit,
|
||||
int $newChildLimit,
|
||||
bool $isNewChild,
|
||||
): void {
|
||||
if ($newChildLimit < 0) {
|
||||
throw ValidationException::withMessages([
|
||||
'credit_limit' => ['invalid'],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->allocatedSync->syncForAgent($parent);
|
||||
|
||||
$profile = AgentProfile::query()->where('agent_node_id', $parent->id)->first();
|
||||
if ($profile === null) {
|
||||
throw ValidationException::withMessages([
|
||||
'credit_limit' => ['parent_profile_required'],
|
||||
]);
|
||||
}
|
||||
|
||||
$available = max(0, (int) $profile->credit_limit - (int) $profile->allocated_credit);
|
||||
$floor = $isNewChild ? 0 : max(0, $previousChildLimit);
|
||||
$maxAllowed = $floor + $available;
|
||||
|
||||
if ($newChildLimit > $maxAllowed) {
|
||||
throw ValidationException::withMessages([
|
||||
'credit_limit' => ['exceeds_available'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function assertAllocationWithinParent(AgentNode $parent, int $additionalCredit): void
|
||||
{
|
||||
$this->allocatedSync->syncForAgent($parent);
|
||||
|
||||
$profile = AgentProfile::query()->where('agent_node_id', $parent->id)->first();
|
||||
if ($profile === null) {
|
||||
return;
|
||||
throw ValidationException::withMessages([
|
||||
'credit_limit' => ['parent_profile_required'],
|
||||
]);
|
||||
}
|
||||
|
||||
$available = max(0, (int) $profile->credit_limit - (int) $profile->allocated_credit);
|
||||
|
||||
@@ -12,7 +12,9 @@ final class RebateLimitValidator
|
||||
{
|
||||
$profile = AgentProfile::query()->where('agent_node_id', $agent->id)->first();
|
||||
if ($profile === null) {
|
||||
return;
|
||||
throw ValidationException::withMessages([
|
||||
'rebate_rate' => ['agent_profile_required'],
|
||||
]);
|
||||
}
|
||||
|
||||
// Both $rebateRate and $profile->rebate_limit are ratios (0-1)
|
||||
@@ -23,10 +25,38 @@ final class RebateLimitValidator
|
||||
]);
|
||||
}
|
||||
|
||||
if ($extraRebateRate > $limit) {
|
||||
throw ValidationException::withMessages([
|
||||
'extra_rebate_rate' => ['exceeds_limit'],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($extraRebateRate > 0 && ! $profile->can_grant_extra_rebate) {
|
||||
throw ValidationException::withMessages([
|
||||
'extra_rebate_rate' => ['not_allowed'],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($limit > 0 && ($rebateRate + $extraRebateRate) > $limit + 1e-9) {
|
||||
throw ValidationException::withMessages([
|
||||
'rebate_rate' => ['exceeds_limit'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function assertChildRebateLimitWithinParent(AgentNode $parent, float $childRebateLimitRatio): void
|
||||
{
|
||||
$profile = AgentProfile::query()->where('agent_node_id', $parent->id)->first();
|
||||
if ($profile === null) {
|
||||
throw ValidationException::withMessages([
|
||||
'rebate_limit' => ['parent_profile_required'],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($childRebateLimitRatio > (float) $profile->rebate_limit) {
|
||||
throw ValidationException::withMessages([
|
||||
'rebate_limit' => ['exceeds_parent'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user