feat: add AgentNodeIndexController for node listing and remove settlement_cycle field from AgentProfile logic

This commit is contained in:
2026-06-11 18:01:58 +08:00
parent 4d1c2b3d63
commit e14b7b4569
30 changed files with 383 additions and 91 deletions

View File

@@ -77,7 +77,6 @@ final class AgentDashboardOverviewBuilder
(int) ($profile?->credit_limit ?? 0) - (int) ($profile?->allocated_credit ?? 0),
),
'total_share_rate' => (float) ($profile?->total_share_rate ?? 0),
'settlement_cycle' => (string) ($profile?->settlement_cycle ?? 'weekly'),
'can_create_child_agent' => (bool) ($profile?->can_create_child_agent ?? false),
'can_create_player' => (bool) ($profile?->can_create_player ?? false),
'direct_child_count' => $directChildCount,

View File

@@ -30,7 +30,6 @@ final class AgentNodeService
* credit_limit?: int,
* rebate_limit?: float|int,
* default_player_rebate?: float|int,
* settlement_cycle?: string,
* can_grant_extra_rebate?: bool,
* can_create_child_agent?: bool,
* can_create_player?: bool
@@ -125,7 +124,6 @@ final class AgentNodeService
'credit_limit' => (int) ($payload['credit_limit'] ?? 0),
'rebate_limit' => (float) ($payload['rebate_limit'] ?? 0),
'default_player_rebate' => (float) ($payload['default_player_rebate'] ?? 0),
'settlement_cycle' => (string) ($payload['settlement_cycle'] ?? 'weekly'),
'can_grant_extra_rebate' => (bool) ($payload['can_grant_extra_rebate'] ?? false),
'can_create_child_agent' => (bool) ($payload['can_create_child_agent'] ?? false),
'can_create_player' => (bool) ($payload['can_create_player'] ?? true),

View File

@@ -7,7 +7,6 @@ use App\Models\AgentNode;
use App\Models\AgentProfile;
use App\Support\AdminAgentScope;
use App\Support\AgentOverdueGuard;
use App\Support\AgentSettlementCycle;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
@@ -86,9 +85,6 @@ final class AgentProfileService
'credit_limit' => $creditLimit,
'rebate_limit' => $rebateLimit,
'default_player_rebate' => $defaultRebate,
'settlement_cycle' => AgentSettlementCycle::normalize(
$payload['settlement_cycle'] ?? $profile->settlement_cycle ?? 'weekly',
),
'can_grant_extra_rebate' => (bool) ($payload['can_grant_extra_rebate'] ?? $profile->can_grant_extra_rebate ?? false),
'can_create_child_agent' => (bool) ($payload['can_create_child_agent'] ?? ($isNew ? false : $profile->can_create_child_agent)),
'can_create_player' => (bool) ($payload['can_create_player'] ?? ($isNew ? true : $profile->can_create_player ?? true)),
@@ -126,7 +122,6 @@ final class AgentProfileService
'available_credit' => $available,
'rebate_limit' => round((float) $profile->rebate_limit * 100, 4),
'default_player_rebate' => round((float) $profile->default_player_rebate * 100, 4),
'settlement_cycle' => AgentSettlementCycle::normalize($profile->settlement_cycle),
'can_grant_extra_rebate' => (bool) $profile->can_grant_extra_rebate,
'can_create_child_agent' => (bool) $profile->can_create_child_agent,
'can_create_player' => (bool) $profile->can_create_player,

View File

@@ -105,7 +105,6 @@ final class AgentSiteProvisioningService
'credit_limit' => (int) ($payload['credit_limit'] ?? $defaults['credit_limit'] ?? 0),
'rebate_limit' => (float) ($payload['rebate_limit'] ?? $defaults['rebate_limit'] ?? 0),
'default_player_rebate' => (float) ($payload['default_player_rebate'] ?? $defaults['default_player_rebate'] ?? 0),
'settlement_cycle' => (string) ($payload['settlement_cycle'] ?? $defaults['settlement_cycle'] ?? 'weekly'),
'can_grant_extra_rebate' => (bool) ($payload['can_grant_extra_rebate'] ?? true),
'can_create_child_agent' => (bool) ($payload['can_create_child_agent'] ?? true),
'can_create_player' => (bool) ($payload['can_create_player'] ?? true),

View File

@@ -15,6 +15,7 @@ final class RebateLimitValidator
return;
}
// Both $rebateRate and $profile->rebate_limit are ratios (0-1)
$limit = (float) $profile->rebate_limit;
if ($rebateRate > $limit) {
throw ValidationException::withMessages([

View File

@@ -2,9 +2,9 @@
namespace App\Services\AgentSettlement;
use App\Models\Player;
use App\Models\AgentNode;
use App\Models\AgentProfile;
use App\Models\Player;
use Illuminate\Support\Facades\DB;
final class BetSettlementSnapshotBuilder

View File

@@ -3,25 +3,45 @@
namespace App\Services\Integration;
use App\Models\AdminSite;
use App\Models\AdminRole;
use App\Models\AdminUser;
use App\Support\AdminPermissionInheritance;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\DB;
final class IntegrationSiteService
{
/** @var list<string> */
private const SITE_ADMIN_PERMISSION_SLUGS = [
'prd.agent.manage',
'prd.agent.profile.manage',
'prd.agent.user.manage',
'prd.agent.role.manage',
'prd.users.manage',
'prd.tickets.view',
'prd.report.view',
'prd.settlement.agent.view',
'prd.settlement.agent.manage',
];
public function __construct(
private readonly PartnerSiteConfigResolver $configResolver,
) {}
/**
* @param array<string, mixed> $data
* @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}}
* @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}, admin_user: AdminUser}
*/
public function create(array $data): array
{
$secrets = $this->generateSecrets();
$site = DB::transaction(function () use ($data, $secrets): AdminSite {
return AdminSite::query()->create([
['site' => $site, 'admin_user' => $adminUser] = DB::transaction(function () use ($data, $secrets): array {
/** @var array{username: string, nickname: string, password: string, email?: string|null} $adminAccount */
$adminAccount = $data['admin_account'];
$site = AdminSite::query()->create([
'code' => (string) $data['code'],
'name' => (string) $data['name'],
'currency_code' => (string) ($data['currency_code'] ?? 'NPR'),
@@ -38,11 +58,23 @@ final class IntegrationSiteService
'sso_jwt_secret_encrypted' => encrypt($secrets['sso_jwt_secret']),
'wallet_api_key_encrypted' => encrypt($secrets['wallet_api_key']),
]);
$role = $this->createSiteAdminRole($site);
$adminUser = $this->createSiteAdminUser($site, $role, $adminAccount);
return [
'site' => $site,
'admin_user' => $adminUser,
];
});
$this->configResolver->forgetCache((string) $site->code);
return ['site' => $site->fresh(), 'secrets' => $secrets];
return [
'site' => $site->fresh(),
'secrets' => $secrets,
'admin_user' => $adminUser->fresh(),
];
}
/**
@@ -114,4 +146,53 @@ final class IntegrationSiteService
return $trimmed === '' ? null : $trimmed;
}
private function createSiteAdminRole(AdminSite $site): AdminRole
{
$slug = sprintf('site_admin_%s', (string) $site->code);
$role = AdminRole::query()->create([
'slug' => $slug,
'name' => sprintf('%s 站点后台管理员', (string) $site->name),
'description' => sprintf('自动创建:站点 %s (%s) 后台管理账号专用角色', (string) $site->name, (string) $site->code),
'status' => 1,
'is_system' => true,
'sort_order' => 900,
'scope_type' => AdminRole::SCOPE_SYSTEM,
]);
$role->syncLegacyPermissionSlugs(
AdminPermissionInheritance::expand(self::SITE_ADMIN_PERMISSION_SLUGS),
);
return $role;
}
/**
* @param array{username: string, nickname: string, password: string, email?: string|null} $adminAccount
*/
private function createSiteAdminUser(AdminSite $site, AdminRole $role, array $adminAccount): AdminUser
{
$username = trim((string) ($adminAccount['username'] ?? ''));
$nickname = trim((string) ($adminAccount['nickname'] ?? ''));
$password = (string) ($adminAccount['password'] ?? '');
$email = $this->nullableTrim($adminAccount['email'] ?? null);
if ($username === '' || $nickname === '' || $password === '') {
throw ValidationException::withMessages([
'admin_account' => ['站点后台管理账号信息不完整。'],
]);
}
$user = AdminUser::query()->create([
'username' => $username,
'name' => $nickname,
'email' => $email,
'password' => $password,
'status' => 0,
]);
$user->syncSystemRoleSlugsForSite((int) $site->id, [(string) $role->slug]);
return $user;
}
}

View File

@@ -3,8 +3,8 @@
namespace App\Services\Player;
use App\Models\AgentNode;
use App\Services\Agent\RebateLimitValidator;
use Illuminate\Support\Facades\DB;
use App\Services\Agent\RebateLimitValidator;
final class PlayerRebateProfileService
{
@@ -25,8 +25,8 @@ final class PlayerRebateProfileService
foreach ($profiles as $row) {
$gameType = trim((string) ($row['game_type'] ?? '*')) ?: '*';
$inherit = (bool) ($row['inherit_from_agent'] ?? false);
$rebateRate = (float) ($row['rebate_rate'] ?? 0);
$extraRate = (float) ($row['extra_rebate_rate'] ?? 0);
$rebateRate = (float) ($row['rebate_rate'] ?? 0) / 100;
$extraRate = (float) ($row['extra_rebate_rate'] ?? 0) / 100;
if (! $inherit) {
$this->rebateLimitValidator->assertPlayerRebateWithinAgent($agent, $rebateRate, $extraRate);
@@ -56,8 +56,8 @@ final class PlayerRebateProfileService
->get()
->map(static fn (object $row): array => [
'game_type' => (string) $row->game_type,
'rebate_rate' => (float) $row->rebate_rate,
'extra_rebate_rate' => (float) $row->extra_rebate_rate,
'rebate_rate' => round((float) $row->rebate_rate * 100, 4),
'extra_rebate_rate' => round((float) $row->extra_rebate_rate * 100, 4),
'inherit_from_agent' => (bool) $row->inherit_from_agent,
])
->all();