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

@@ -0,0 +1,38 @@
<?php
namespace App\Http\Controllers\Api\V1\Admin\Agent;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;
use App\Support\AdminAgentScope;
use App\Support\AgentNodePresenter;
use App\Models\AgentNode;
final class AgentNodeIndexController extends Controller
{
public function __invoke(Request $request): JsonResponse
{
$admin = $request->lotteryAdmin();
abort_if($admin === null, 401);
$query = AgentNode::query()->orderBy('admin_site_id')->orderBy('path');
if (! $admin->isSuperAdmin()) {
$actor = AdminAgentScope::primaryAgentNode($admin);
if ($actor === null) {
$query->whereRaw('0 = 1');
} else {
$query
->where('admin_site_id', (int) $actor->admin_site_id)
->where('path', 'like', $actor->path.'%');
}
}
return ApiResponse::success([
'admin_site_id' => null,
'items' => AgentNodePresenter::list($query->get()),
]);
}
}

View File

@@ -27,6 +27,12 @@ final class AdminIntegrationSiteStoreController extends Controller
AdminIntegrationSitePresenter::detail($site),
$result['secrets'],
);
$payload['admin_user'] = [
'id' => (int) $result['admin_user']->id,
'username' => (string) $result['admin_user']->username,
'nickname' => (string) $result['admin_user']->name,
'email' => $result['admin_user']->email,
];
AuditLogger::recordForAdmin(
$admin,

View File

@@ -3,23 +3,25 @@
namespace App\Http\Controllers\Api\V1\Admin\Player;
use App\Models\Player;
use App\Models\AdminSite;
use App\Models\AgentNode;
use App\Lottery\ErrorCode;
use App\Support\ApiMessage;
use App\Support\ApiResponse;
use Illuminate\Http\JsonResponse;
use App\Support\AdminAgentScope;
use App\Support\AdminSiteScope;
use App\Support\AdminAgentScope;
use App\Support\PlayerAuthSource;
use Illuminate\Http\JsonResponse;
use App\Support\PlayerFundingMode;
use Illuminate\Support\Facades\DB;
use App\Support\PlayerApiPresenter;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AdminPlayerStoreRequest;
use App\Models\AgentNode;
use Illuminate\Support\Facades\Hash;
use App\Services\Agent\AgentProfileService;
use App\Services\Agent\RebateLimitValidator;
use App\Services\Player\PlayerCreditService;
use App\Support\PlayerAuthSource;
use App\Support\PlayerFundingMode;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use App\Http\Requests\Admin\AdminPlayerStoreRequest;
/** POST /api/v1/admin/players */
final class AdminPlayerStoreController extends Controller
@@ -35,7 +37,7 @@ final class AdminPlayerStoreController extends Controller
try {
$agentProfileService->assertActorMayCreatePlayer($admin);
} catch (\Illuminate\Validation\ValidationException $e) {
} catch (ValidationException $e) {
return ApiMessage::errorResponse(
$request,
'admin.player_create_capability_forbidden',
@@ -110,11 +112,15 @@ final class AdminPlayerStoreController extends Controller
}
$agent = AgentNode::query()->findOrFail($agentNodeId);
$rebateRate = 0.0;
$extraRebateRate = 0.0;
if ($request->has('rebate_rate')) {
$rebateRate = (float) $request->input('rebate_rate', 0) / 100;
$extraRebateRate = (float) $request->input('extra_rebate_rate', 0) / 100;
$rebateLimitValidator->assertPlayerRebateWithinAgent(
$agent,
(float) $request->input('rebate_rate', 0),
(float) $request->input('extra_rebate_rate', 0),
$rebateRate,
$extraRebateRate,
);
}
@@ -122,7 +128,7 @@ final class AdminPlayerStoreController extends Controller
? (int) $request->input('credit_limit', 0)
: ($isNative ? 0 : 0);
$player = \Illuminate\Support\Facades\DB::transaction(function () use (
$player = DB::transaction(function () use (
$agent,
$agentProfileService,
$playerCreditService,
@@ -132,6 +138,8 @@ final class AdminPlayerStoreController extends Controller
$agentNodeId,
$sitePlayerId,
$creditLimit,
$rebateRate,
$extraRebateRate,
): Player {
$agentProfileService->assertMayIncreasePlayerCredit($agent, $creditLimit);
@@ -157,12 +165,12 @@ final class AdminPlayerStoreController extends Controller
$agentProfileService->refreshAllocatedCredit($agent);
if ($request->has('rebate_rate')) {
\Illuminate\Support\Facades\DB::table('player_rebate_profiles')->insert([
DB::table('player_rebate_profiles')->insert([
'player_id' => $player->id,
'game_type' => '*',
'inherit_from_agent' => false,
'rebate_rate' => (float) $request->input('rebate_rate', 0),
'extra_rebate_rate' => (float) $request->input('extra_rebate_rate', 0),
'rebate_rate' => $rebateRate,
'extra_rebate_rate' => $extraRebateRate,
'created_at' => now(),
'updated_at' => now(),
]);
@@ -180,12 +188,12 @@ final class AdminPlayerStoreController extends Controller
return (int) $requested;
}
$siteId = \App\Models\AdminSite::query()->where('code', $siteCode)->value('id');
$siteId = AdminSite::query()->where('code', $siteCode)->value('id');
if ($siteId === null) {
return null;
}
$rootId = \App\Models\AgentNode::query()
$rootId = AgentNode::query()
->where('admin_site_id', (int) $siteId)
->where('depth', 0)
->value('id');

View File

@@ -2,19 +2,19 @@
namespace App\Http\Controllers\Api\V1\Admin\Player;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AdminPlayerUpdateRequest;
use App\Models\AgentNode;
use App\Models\Player;
use App\Models\AgentNode;
use App\Support\ApiResponse;
use App\Support\AdminSiteScope;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use App\Support\PlayerApiPresenter;
use App\Http\Controllers\Controller;
use App\Services\Agent\AgentProfileService;
use App\Services\Agent\RebateLimitValidator;
use App\Services\Player\PlayerCreditService;
use App\Services\Player\PlayerRebateProfileService;
use App\Support\AdminSiteScope;
use App\Support\ApiResponse;
use App\Support\PlayerApiPresenter;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use App\Http\Requests\Admin\AdminPlayerUpdateRequest;
/** PUT /api/v1/admin/players/{player} */
final class AdminPlayerUpdateController extends Controller
@@ -44,11 +44,15 @@ final class AdminPlayerUpdateController extends Controller
? AgentNode::query()->find((int) $player->agent_node_id)
: null;
$rebateRate = 0.0;
$extraRebateRate = 0.0;
if ($agent !== null && $request->has('rebate_rate')) {
$rebateRate = (float) $request->input('rebate_rate', 0) / 100;
$extraRebateRate = (float) $request->input('extra_rebate_rate', 0) / 100;
$rebateLimitValidator->assertPlayerRebateWithinAgent(
$agent,
(float) $request->input('rebate_rate', 0),
(float) $request->input('extra_rebate_rate', 0),
$rebateRate,
$extraRebateRate,
);
}
@@ -68,8 +72,8 @@ final class AdminPlayerUpdateController extends Controller
['player_id' => $player->id, 'game_type' => '*'],
[
'inherit_from_agent' => false,
'rebate_rate' => (float) $request->input('rebate_rate', 0),
'extra_rebate_rate' => (float) $request->input('extra_rebate_rate', 0),
'rebate_rate' => $rebateRate,
'extra_rebate_rate' => $extraRebateRate,
'updated_at' => now(),
'created_at' => now(),
],

View File

@@ -30,6 +30,11 @@ final class AdminIntegrationSiteStoreRequest extends ApiFormRequest
'iframe_allowed_origins.*' => ['string', 'max:512'],
'lottery_h5_base_url' => ['nullable', 'string', 'max:512'],
'notes' => ['nullable', 'string', 'max:5000'],
'admin_account' => ['required', 'array'],
'admin_account.username' => ['required', 'string', 'max:64', Rule::unique('admin_users', 'username')],
'admin_account.nickname' => ['required', 'string', 'max:64'],
'admin_account.password' => ['required', 'string', 'min:8', 'max:255'],
'admin_account.email' => ['nullable', 'email', 'max:255'],
];
}
}

View File

@@ -29,8 +29,8 @@ final class AdminPlayerStoreRequest extends ApiFormRequest
'status' => ['sometimes', 'integer', 'in:0,1,2'],
'agent_node_id' => ['sometimes', 'nullable', 'integer', 'min:1'],
'credit_limit' => ['sometimes', 'integer', 'min:0'],
'rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:1'],
'extra_rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:1'],
'rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:100'],
'extra_rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:100'],
];
}

View File

@@ -25,12 +25,12 @@ final class AdminPlayerUpdateRequest extends ApiFormRequest
'default_currency' => ['sometimes', 'string', 'max:16', Rule::exists('currencies', 'code')],
'status' => ['sometimes', 'integer', Rule::in([0, 1, 2])],
'credit_limit' => ['sometimes', 'integer', 'min:0'],
'rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:1'],
'extra_rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:1'],
'rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:100'],
'extra_rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:100'],
'rebate_profiles' => ['sometimes', 'array'],
'rebate_profiles.*.game_type' => ['required_with:rebate_profiles', 'string', 'max:32'],
'rebate_profiles.*.rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:1'],
'rebate_profiles.*.extra_rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:1'],
'rebate_profiles.*.rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:100'],
'rebate_profiles.*.extra_rebate_rate' => ['sometimes', 'numeric', 'min:0', 'max:100'],
'rebate_profiles.*.inherit_from_agent' => ['sometimes', 'boolean'],
'risk_tags' => ['sometimes', 'array'],
'risk_tags.*' => ['string', 'max:64'],

View File

@@ -2,8 +2,6 @@
namespace App\Http\Requests\Admin\Concerns;
use App\Support\AgentSettlementCycle;
trait AgentProfileFieldRules
{
/** @return array<string, mixed> */
@@ -15,7 +13,6 @@ trait AgentProfileFieldRules
'credit_limit' => ['sometimes', 'integer', 'min:0'],
'rebate_limit' => ['sometimes', 'numeric', 'min:0', 'max:100'],
'default_player_rebate' => ['sometimes', 'numeric', 'min:0', 'max:100'],
'settlement_cycle' => ['sometimes', 'string', 'in:'.implode(',', AgentSettlementCycle::VALUES)],
'can_grant_extra_rebate' => ['sometimes', 'boolean'],
'can_create_child_agent' => ['sometimes', 'boolean'],
'can_create_player' => ['sometimes', 'boolean'],
@@ -26,12 +23,6 @@ trait AgentProfileFieldRules
protected function prepareAgentProfileFieldsForValidation(): void
{
if (! $this->has('settlement_cycle')) {
return;
}
$this->merge([
'settlement_cycle' => AgentSettlementCycle::normalize($this->input('settlement_cycle')),
]);
// 预处理字段(如需要)
}
}