|
|
@@ -14,7 +14,11 @@ use App\Services\Agent\AgentReportService;
|
|
|
use App\Services\Agent\AgentService;
|
|
|
use App\Services\Agent\AgentWalletService;
|
|
|
use App\Services\Agent\AgentWithdrawalService;
|
|
|
-use App\Services\Agent\AgentFlowCommissionProfileService;
|
|
|
+use App\Services\Agent\AgentPlanApplicationService;
|
|
|
+use App\Services\Agent\AgentRechargeService;
|
|
|
+use App\Models\Agent\AgentSetting;
|
|
|
+use App\Models\Agent\AgentRecharge;
|
|
|
+use App\Models\Agent\AgentProfitEntry;
|
|
|
use App\Services\Agent\AgentProfitCommissionProfileService;
|
|
|
use Illuminate\Http\Request;
|
|
|
use Illuminate\Validation\Rule;
|
|
|
@@ -31,15 +35,15 @@ class Agent extends AgentApiController
|
|
|
$data = $request->validate([
|
|
|
'page' => ['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:100'],
|
|
|
'username' => ['nullable', 'string', 'max:64'], 'real_name' => ['nullable', 'string', 'max:128'],
|
|
|
- 'parent_id' => ['nullable', 'integer'], 'status' => ['nullable', 'integer', 'in:0,1'],
|
|
|
+ 'type' => ['nullable', 'integer', 'in:1,2'], 'status' => ['nullable', 'integer', 'in:0,1'],
|
|
|
'start_date' => ['nullable', 'date_format:Y-m-d'], 'end_date' => ['nullable', 'date_format:Y-m-d', 'after_or_equal:start_date'],
|
|
|
]);
|
|
|
$page = max(1, (int) ($data['page'] ?? 1));
|
|
|
$limit = min(100, max(1, (int) ($data['limit'] ?? 20)));
|
|
|
- $query = AgentModel::query()->with(['parent:id,username', 'token:id,agent_id,expires_at,last_used_at', 'flowCommissionProfile', 'profitCommissionProfile'])->withCount(['children', 'members']);
|
|
|
+ $query = AgentModel::query()->with(['token:id,agent_id,expires_at,last_used_at', 'profitCommissionProfile'])->withCount(['members']);
|
|
|
if (!empty($data['username'])) $query->where('username', 'like', '%' . $data['username'] . '%');
|
|
|
if (!empty($data['real_name'])) $query->where('real_name', 'like', '%' . $data['real_name'] . '%');
|
|
|
- if (!empty($data['parent_id'])) $query->where('parent_id', $data['parent_id']);
|
|
|
+ if (!empty($data['type'])) $query->where('type', $data['type']);
|
|
|
if (isset($data['status'])) $query->where('status', $data['status']);
|
|
|
if (!empty($data['start_date'])) $query->where('created_at', '>=', $data['start_date'] . ' 00:00:00');
|
|
|
if (!empty($data['end_date'])) $query->where('created_at', '<=', $data['end_date'] . ' 23:59:59');
|
|
|
@@ -53,7 +57,7 @@ class Agent extends AgentApiController
|
|
|
{
|
|
|
return $this->execute(function () use ($request, $domains) {
|
|
|
$data = $request->validate(['id' => ['required', 'integer', 'exists:agents,id']]);
|
|
|
- $agent = AgentModel::query()->with(['parent:id,username', 'token:id,agent_id,expires_at,last_used_at', 'flowCommissionProfile', 'profitCommissionProfile'])->withCount(['children', 'members'])->findOrFail($data['id']);
|
|
|
+ $agent = AgentModel::query()->with(['token:id,agent_id,expires_at,last_used_at', 'profitCommissionProfile'])->withCount(['members'])->findOrFail($data['id']);
|
|
|
return $this->formatAgent($agent, $domains);
|
|
|
});
|
|
|
}
|
|
|
@@ -64,9 +68,6 @@ class Agent extends AgentApiController
|
|
|
$data = $request->validate($this->rules());
|
|
|
return DB::transaction(function () use ($data, $service) {
|
|
|
$agent = $service->create($data, (int) request()->user->id);
|
|
|
- if (!empty($data['owner_user_id'])) {
|
|
|
- $agent = app(\App\Services\Agent\AgentApplicationService::class)->bindMember($agent, (int) $data['owner_user_id']);
|
|
|
- }
|
|
|
if (bccomp((string) ($data['initial_balance'] ?? 0), '0', 4) > 0) {
|
|
|
app(AgentWalletService::class)->adjust(
|
|
|
(int) $agent->id,
|
|
|
@@ -77,7 +78,7 @@ class Agent extends AgentApiController
|
|
|
'admin',
|
|
|
(int) request()->user->id
|
|
|
);
|
|
|
- $agent = $agent->fresh(['flowCommissionProfile', 'profitCommissionProfile']);
|
|
|
+ $agent = $agent->fresh(['profitCommissionProfile']);
|
|
|
}
|
|
|
return $agent;
|
|
|
});
|
|
|
@@ -89,16 +90,7 @@ class Agent extends AgentApiController
|
|
|
return $this->execute(function () use ($request, $service) {
|
|
|
$data = $request->validate(array_merge(['id' => ['required', 'integer', 'exists:agents,id']], $this->rules(true)));
|
|
|
return DB::transaction(function () use ($service, $data) {
|
|
|
- $agent = $service->update(
|
|
|
- AgentModel::query()->findOrFail($data['id']),
|
|
|
- $data,
|
|
|
- null,
|
|
|
- (int) request()->user->id
|
|
|
- );
|
|
|
- if (!empty($data['owner_user_id'])) {
|
|
|
- $agent = app(\App\Services\Agent\AgentApplicationService::class)->bindMember($agent, (int) $data['owner_user_id']);
|
|
|
- }
|
|
|
- return $agent;
|
|
|
+ return $service->update(AgentModel::query()->findOrFail($data['id']), $data);
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
@@ -118,7 +110,7 @@ class Agent extends AgentApiController
|
|
|
{
|
|
|
return $this->execute(function () use ($request, $wallets) {
|
|
|
$data = $request->validate([
|
|
|
- 'agent_id' => ['required', 'integer', 'exists:agents,id'], 'type' => ['required', 'in:credit,debit'],
|
|
|
+ 'agent_id' => ['required', 'integer', 'exists:agents,id'], 'type' => ['required', 'in:credit,debit,margin_credit,margin_refund'],
|
|
|
'amount' => ['required', 'numeric', 'min:0.0001', new DecimalNumber()], 'remark' => ['required', 'string', 'max:500'],
|
|
|
'request_id' => ['required', 'string', 'max:64'],
|
|
|
]);
|
|
|
@@ -239,13 +231,14 @@ class Agent extends AgentApiController
|
|
|
'agent_id' => ['nullable', 'integer'], 'order_no' => ['nullable', 'string', 'max:48'],
|
|
|
'username' => ['nullable', 'string', 'max:64'],
|
|
|
'platform' => ['nullable', 'string', 'max:32'],
|
|
|
- 'flow_status' => ['nullable', 'in:pending,credited'],
|
|
|
+ 'category' => ['nullable', 'string', 'max:32'],
|
|
|
'profit_status' => ['nullable', 'in:pending,credited'],
|
|
|
'settlement_date' => ['nullable', 'date_format:Y-m-d'],
|
|
|
'start_date' => ['nullable', 'date_format:Y-m-d'], 'end_date' => ['nullable', 'date_format:Y-m-d', 'after_or_equal:start_date'],
|
|
|
]);
|
|
|
$query = AgentRebateRecord::query()->with('agent:id,username');
|
|
|
- foreach (['agent_id', 'order_no', 'platform', 'flow_status', 'profit_status'] as $field) {
|
|
|
+ if (!empty($data['category'])) $query->where('platform', $data['category']);
|
|
|
+ foreach (['agent_id', 'order_no', 'platform', 'profit_status'] as $field) {
|
|
|
if (!empty($data[$field])) $query->where($field, $data[$field]);
|
|
|
}
|
|
|
if (!empty($data['username'])) {
|
|
|
@@ -269,72 +262,128 @@ class Agent extends AgentApiController
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- public function flowCommissionProfiles(Request $request, AgentFlowCommissionProfileService $service)
|
|
|
- {
|
|
|
- return $this->execute(function () use ($request, $service) {
|
|
|
- $data = $request->validate([
|
|
|
- 'page' => ['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:100'],
|
|
|
- 'name' => ['nullable', 'string', 'max:128'],
|
|
|
- ]);
|
|
|
- return $service->paginate($data);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- public function saveFlowCommissionProfile(Request $request, AgentFlowCommissionProfileService $service)
|
|
|
+ public function saveProfitCommissionProfile(Request $request, AgentProfitCommissionProfileService $service)
|
|
|
{
|
|
|
return $this->execute(function () use ($request, $service) {
|
|
|
- $id = (int)$request->input('id', 0);
|
|
|
$data = $request->validate([
|
|
|
- 'id' => ['nullable', 'integer', 'exists:agent_flow_commission_profiles,id'],
|
|
|
- 'name' => ['required', 'string', 'max:128', Rule::unique('agent_flow_commission_profiles', 'name')->whereNull('owner_agent_id')->ignore($id)],
|
|
|
+ 'id' => ['nullable', 'integer', 'exists:agent_profit_commission_profiles,id'],
|
|
|
+ 'name' => ['required', 'string', 'max:128'],
|
|
|
+ 'margin_amount' => ['required', 'numeric', 'min:0', new DecimalNumber()],
|
|
|
+ 'f1_lottery_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
+ 'f1_sport_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
'live_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
'slot_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'lottery_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
'sport_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
'esports_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
'fishing_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
'chess_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'is_default' => ['required', 'boolean'],
|
|
|
]);
|
|
|
- return $service->save($data, (int)request()->user->id);
|
|
|
+ return $service->save($data, (int) request()->user->id);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- public function deleteFlowCommissionProfile(Request $request, AgentFlowCommissionProfileService $service)
|
|
|
+ public function deleteProfitCommissionProfile(Request $request, AgentProfitCommissionProfileService $service)
|
|
|
{
|
|
|
return $this->execute(function () use ($request, $service) {
|
|
|
- $data = $request->validate(['id' => ['required', 'integer', 'exists:agent_flow_commission_profiles,id']]);
|
|
|
- $service->delete((int)$data['id'], (int)request()->user->id);
|
|
|
+ $data = $request->validate(['id' => ['required', 'integer', 'exists:agent_profit_commission_profiles,id']]);
|
|
|
+ $service->delete((int) $data['id']);
|
|
|
return [];
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- public function saveProfitCommissionProfile(Request $request, AgentProfitCommissionProfileService $service)
|
|
|
+ /** 代理全局配置:停止分成阈值(方案保证金的百分比)、入股价格与可购买区间。 */
|
|
|
+ public function settings()
|
|
|
+ {
|
|
|
+ return $this->execute(fn () => AgentSetting::current());
|
|
|
+ }
|
|
|
+
|
|
|
+ public function saveSettings(Request $request)
|
|
|
+ {
|
|
|
+ return $this->execute(function () use ($request) {
|
|
|
+ $data = $request->validate([
|
|
|
+ 'margin_stop_percent' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
+ 'equity_enabled' => ['required', 'boolean'],
|
|
|
+ 'equity_price_per_percent' => ['required', 'numeric', 'min:0', new DecimalNumber()],
|
|
|
+ 'equity_min_percent' => ['required', 'numeric', 'gt:0', 'max:100', new DecimalNumber(3, 4)],
|
|
|
+ 'equity_max_percent' => ['required', 'numeric', 'gte:equity_min_percent', 'max:100', new DecimalNumber(3, 4)],
|
|
|
+ ]);
|
|
|
+ $setting = AgentSetting::current();
|
|
|
+ $setting->fill($data + ['updated_by' => (int) request()->user->id]);
|
|
|
+ $setting->equity_enabled = $request->boolean('equity_enabled') ? 1 : 0;
|
|
|
+ $setting->save();
|
|
|
+ return $setting->fresh();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public function planApplications(Request $request, AgentPlanApplicationService $service)
|
|
|
{
|
|
|
return $this->execute(function () use ($request, $service) {
|
|
|
- $id = (int) $request->input('id', 0);
|
|
|
$data = $request->validate([
|
|
|
- 'id' => ['nullable', 'integer', 'exists:agent_profit_commission_profiles,id'],
|
|
|
- 'name' => ['required', 'string', 'max:128', Rule::unique('agent_profit_commission_profiles', 'name')->whereNull('owner_agent_id')->ignore($id)],
|
|
|
- 'live_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'slot_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'lottery_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'sport_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'esports_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'fishing_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'chess_rate' => ['required', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'is_default' => ['required', 'boolean'],
|
|
|
+ 'page' => ['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:100'],
|
|
|
+ 'agent_id' => ['nullable', 'integer'], 'username' => ['nullable', 'string', 'max:64'],
|
|
|
+ 'status' => ['nullable', 'in:pending,approved,rejected'],
|
|
|
]);
|
|
|
- return $service->save($data, (int) request()->user->id);
|
|
|
+ return $service->paginate($data);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- public function deleteProfitCommissionProfile(Request $request, AgentProfitCommissionProfileService $service)
|
|
|
+ public function auditPlanApplication(Request $request, AgentPlanApplicationService $service)
|
|
|
{
|
|
|
return $this->execute(function () use ($request, $service) {
|
|
|
- $data = $request->validate(['id' => ['required', 'integer', 'exists:agent_profit_commission_profiles,id']]);
|
|
|
- $service->delete((int) $data['id'], (int) request()->user->id);
|
|
|
- return [];
|
|
|
+ $data = $request->validate([
|
|
|
+ 'id' => ['required', 'integer'], 'status' => ['required', 'in:approved,rejected'],
|
|
|
+ 'reject_reason' => ['required_if:status,rejected', 'nullable', 'string', 'max:500'],
|
|
|
+ ]);
|
|
|
+ return $service->audit((int) $data['id'], $data['status'], (int) request()->user->id, (string) ($data['reject_reason'] ?? ''));
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public function recharges(Request $request)
|
|
|
+ {
|
|
|
+ return $this->execute(function () use ($request) {
|
|
|
+ $data = $request->validate([
|
|
|
+ 'page' => ['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:100'],
|
|
|
+ 'agent_id' => ['nullable', 'integer'], 'order_no' => ['nullable', 'string', 'max:40'],
|
|
|
+ 'txid' => ['nullable', 'string', 'max:100'], 'type' => ['nullable', 'integer', 'in:1,2'],
|
|
|
+ 'status' => ['nullable', 'integer', 'in:0,1,2,3'],
|
|
|
+ 'start_date' => ['nullable', 'date_format:Y-m-d'], 'end_date' => ['nullable', 'date_format:Y-m-d', 'after_or_equal:start_date'],
|
|
|
+ ]);
|
|
|
+ $query = AgentRecharge::query()->with('agent:id,username,real_name,type');
|
|
|
+ foreach (['agent_id', 'order_no', 'txid', 'type'] as $field) if (!empty($data[$field])) $query->where($field, $data[$field]);
|
|
|
+ if (isset($data['status'])) $query->where('status', (int) $data['status']);
|
|
|
+ return $this->paginate($query, $data);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public function auditRecharge(Request $request, AgentRechargeService $service)
|
|
|
+ {
|
|
|
+ return $this->execute(function () use ($request, $service) {
|
|
|
+ $data = $request->validate([
|
|
|
+ 'id' => ['required', 'integer'], 'status' => ['required', 'integer', 'in:1,2'],
|
|
|
+ 'remark' => ['required_if:status,2', 'nullable', 'string', 'max:500'],
|
|
|
+ ]);
|
|
|
+ return $service->audit((int) $data['id'], (int) $data['status'], (int) request()->user->id, (string) ($data['remark'] ?? ''));
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public function profitEntries(Request $request)
|
|
|
+ {
|
|
|
+ return $this->execute(function () use ($request) {
|
|
|
+ $data = $request->validate([
|
|
|
+ 'page' => ['nullable', 'integer', 'min:1'], 'limit' => ['nullable', 'integer', 'min:1', 'max:100'],
|
|
|
+ 'agent_id' => ['nullable', 'integer'], 'user_id' => ['nullable', 'integer'], 'category' => ['nullable', 'string', 'max:32'],
|
|
|
+ 'status' => ['nullable', 'in:counted,skipped_inactive,skipped_no_plan,skipped_category'],
|
|
|
+ 'start_date' => ['nullable', 'date_format:Y-m-d'], 'end_date' => ['nullable', 'date_format:Y-m-d', 'after_or_equal:start_date'],
|
|
|
+ ]);
|
|
|
+ $query = AgentProfitEntry::query();
|
|
|
+ foreach (['agent_id', 'user_id', 'category', 'status'] as $field) if (!empty($data[$field])) $query->where($field, $data[$field]);
|
|
|
+ if (!empty($data['start_date'])) $query->where('settle_date', '>=', $data['start_date']);
|
|
|
+ if (!empty($data['end_date'])) $query->where('settle_date', '<=', $data['end_date']);
|
|
|
+ $page = max(1, (int) ($data['page'] ?? 1));
|
|
|
+ $limit = min(100, max(1, (int) ($data['limit'] ?? 20)));
|
|
|
+ $total = (clone $query)->count();
|
|
|
+ $list = $query->orderByDesc('id')->forPage($page, $limit)->get()->each->makeVisible('realtime_balance_after');
|
|
|
+ return compact('total', 'page', 'limit', 'list');
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -342,21 +391,14 @@ class Agent extends AgentApiController
|
|
|
{
|
|
|
return [
|
|
|
'language' => ['sometimes', 'required', 'in:zh,en,vi'],
|
|
|
- 'owner_user_id' => ['nullable', 'integer', 'min:1', 'exists:users,id'],
|
|
|
+ 'type' => [$update ? 'nullable' : 'required', 'integer', 'in:1,2'],
|
|
|
'username' => $update
|
|
|
? ['sometimes', 'string', 'min:4', 'max:64', 'alpha_dash', Rule::unique('agents', 'username')->ignore((int) request()->input('id'))]
|
|
|
: ['required', 'string', 'min:4', 'max:64', 'alpha_dash', 'unique:agents,username'],
|
|
|
'password' => [$update ? 'nullable' : 'required', 'string', 'min:6', 'max:100'],
|
|
|
'withdraw_password' => ['nullable', 'string', 'min:6', 'max:100'], 'real_name' => ['nullable', 'string', 'max:128'],
|
|
|
- 'parent_id' => $update ? ['nullable', 'integer', 'exists:agents,id'] : ['prohibited'],
|
|
|
'deposit_fee_rate' => ['nullable', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
'withdraw_fee_rate' => ['nullable', 'numeric', 'between:0,100', new DecimalNumber(3, 4)],
|
|
|
- 'flow_commission_rate' => ['prohibited'],
|
|
|
- 'profit_commission_rate' => ['prohibited'],
|
|
|
- 'flow_commission_profile_id' => $update
|
|
|
- ? ['nullable', 'integer', Rule::exists('agent_flow_commission_profiles', 'id')->whereNull('owner_agent_id')]
|
|
|
- : ['required', 'integer', Rule::exists('agent_flow_commission_profiles', 'id')->whereNull('owner_agent_id')],
|
|
|
- 'profit_commission_profile_id' => ['nullable', 'integer', Rule::exists('agent_profit_commission_profiles', 'id')->whereNull('owner_agent_id')],
|
|
|
'status' => ['nullable', 'integer', 'in:0,1'],
|
|
|
'initial_balance' => $update ? ['prohibited'] : ['nullable', 'numeric', 'min:0', new DecimalNumber()],
|
|
|
];
|
|
|
@@ -382,7 +424,11 @@ class Agent extends AgentApiController
|
|
|
&& $token->last_used_at->gte(now()->subMinutes(config('agent.online_minutes', 5)));
|
|
|
$data = $agent->toArray();
|
|
|
unset($data['token']);
|
|
|
- return array_merge($data, $domains->format($agent), [
|
|
|
+ $wallets = app(AgentWalletService::class);
|
|
|
+ return array_merge($data, $domains->format($agent), $wallets->funds($agent), [
|
|
|
+ // 总后台可以看到实时保证金;代理端不返回。
|
|
|
+ 'realtime_balance' => (string) $agent->realtime_balance,
|
|
|
+ 'realtime_margin' => $agent->isJoint() ? $wallets->effectiveMargin($agent) : null,
|
|
|
'login_status' => $online ? 1 : 0,
|
|
|
'login_status_text' => $online ? '在线' : '离线',
|
|
|
'last_active_at' => $token?->last_used_at?->toDateTimeString(),
|