250 lines
10 KiB
PHP
250 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Channel;
|
|
use App\Models\User;
|
|
use App\Services\TelegramNotifier;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Validation\Rule;
|
|
|
|
class AgentUserController extends Controller
|
|
{
|
|
public function index(TelegramNotifier $telegram)
|
|
{
|
|
return view('admin.agents.index', [
|
|
'botUsername' => $telegram->cachedBotUsername(),
|
|
'botInviteUrl' => $telegram->inviteUrl(),
|
|
]);
|
|
}
|
|
|
|
public function data(Request $request)
|
|
{
|
|
$q = User::query()->withCount('channels');
|
|
|
|
$username = trim((string) $request->query('username', ''));
|
|
$status = $request->query('status');
|
|
if ($username !== '') {
|
|
$q->where('username', 'like', '%'.$username.'%');
|
|
}
|
|
if ($status !== null && $status !== '') {
|
|
$q->where('status', (int) $status);
|
|
}
|
|
|
|
$sortable = ['id', 'username', 'status', 'created_at', 'updated_at'];
|
|
$field = (string) $request->query('field', 'id');
|
|
$order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc';
|
|
if (! in_array($field, $sortable, true)) {
|
|
$field = 'id';
|
|
}
|
|
$q->orderBy($field, $order);
|
|
|
|
$limit = max(1, min(100, (int) $request->query('limit', 20)));
|
|
$page = max(1, (int) $request->query('page', 1));
|
|
$paginator = $q->paginate($limit, ['*'], 'page', $page);
|
|
|
|
$data = collect($paginator->items())->map(function (User $u) {
|
|
return [
|
|
'id' => $u->id,
|
|
'username' => $u->username,
|
|
'status' => (int) $u->status,
|
|
'comment' => $u->comment ?: '',
|
|
'chat_id' => $u->chat_id ?: '',
|
|
'telegram_ready' => $u->hasTelegramChat(),
|
|
'channels_count' => (int) $u->channels_count,
|
|
'auto_transfer_enabled' => (int) $u->auto_transfer_enabled,
|
|
'auto_transfer_threshold_usdt' => $u->auto_transfer_threshold_usdt !== null ? (string) $u->auto_transfer_threshold_usdt : '',
|
|
'auto_transfer_threshold_trx' => $u->auto_transfer_threshold_trx !== null ? (string) $u->auto_transfer_threshold_trx : '',
|
|
'auto_transfer_threshold_eth' => $u->auto_transfer_threshold_eth !== null ? (string) $u->auto_transfer_threshold_eth : '',
|
|
'auto_transfer_threshold_btc' => $u->auto_transfer_threshold_btc !== null ? (string) $u->auto_transfer_threshold_btc : '',
|
|
'album_storage_default' => $u->albumStorageDefaultEnabled() ? 1 : 0,
|
|
'created_at' => optional($u->created_at)->format('Y-m-d H:i:s'),
|
|
'updated_at' => optional($u->updated_at)->format('Y-m-d H:i:s'),
|
|
];
|
|
})->values();
|
|
|
|
return response()->json([
|
|
'code' => 0,
|
|
'msg' => '',
|
|
'count' => $paginator->total(),
|
|
'data' => $data,
|
|
]);
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
$data = $request->validate([
|
|
'username' => ['required', 'string', 'max:64', 'alpha_dash', Rule::unique('users', 'username')],
|
|
'password' => ['required', 'string', 'min:6', 'max:128'],
|
|
'comment' => ['nullable', 'string', 'max:255'],
|
|
'status' => ['nullable', 'integer', Rule::in([0, 1])],
|
|
'chat_id' => $this->chatIdRules(),
|
|
'auto_transfer_enabled' => ['nullable', 'integer', Rule::in([0, 1])],
|
|
'auto_transfer_threshold_usdt' => ['nullable', 'numeric', 'min:0'],
|
|
'auto_transfer_threshold_trx' => ['nullable', 'numeric', 'min:0'],
|
|
'auto_transfer_threshold_eth' => ['nullable', 'numeric', 'min:0'],
|
|
'auto_transfer_threshold_btc' => ['nullable', 'numeric', 'min:0'],
|
|
'album_storage_default' => ['nullable', 'integer', Rule::in([0, 1])],
|
|
]);
|
|
|
|
$user = User::query()->create([
|
|
'username' => $data['username'],
|
|
'password' => $data['password'],
|
|
'comment' => $data['comment'] ?? null,
|
|
'status' => (int) ($data['status'] ?? 1),
|
|
'chat_id' => $this->nullableTrim($data['chat_id'] ?? null),
|
|
'auto_transfer_enabled' => (bool) ((int) ($data['auto_transfer_enabled'] ?? 0)),
|
|
'auto_transfer_threshold_usdt' => $this->nullableThreshold($data['auto_transfer_threshold_usdt'] ?? null),
|
|
'auto_transfer_threshold_trx' => $this->nullableThreshold($data['auto_transfer_threshold_trx'] ?? null),
|
|
'auto_transfer_threshold_eth' => $this->nullableThreshold($data['auto_transfer_threshold_eth'] ?? null),
|
|
'auto_transfer_threshold_btc' => $this->nullableThreshold($data['auto_transfer_threshold_btc'] ?? null),
|
|
'album_storage_default' => (bool) ((int) ($data['album_storage_default'] ?? 0)),
|
|
]);
|
|
|
|
return response()->json(['code' => 0, 'msg' => 'ok', 'data' => ['id' => $user->id]]);
|
|
}
|
|
|
|
public function update(Request $request, User $agent)
|
|
{
|
|
$data = $request->validate([
|
|
'password' => ['nullable', 'string', 'min:6', 'max:128'],
|
|
'comment' => ['nullable', 'string', 'max:255'],
|
|
'status' => ['nullable', 'integer', Rule::in([0, 1])],
|
|
'chat_id' => $this->chatIdRules($agent->id),
|
|
'auto_transfer_enabled' => ['nullable', 'integer', Rule::in([0, 1])],
|
|
'auto_transfer_threshold_usdt' => ['nullable', 'numeric', 'min:0'],
|
|
'auto_transfer_threshold_trx' => ['nullable', 'numeric', 'min:0'],
|
|
'auto_transfer_threshold_eth' => ['nullable', 'numeric', 'min:0'],
|
|
'auto_transfer_threshold_btc' => ['nullable', 'numeric', 'min:0'],
|
|
'album_storage_default' => ['nullable', 'integer', Rule::in([0, 1])],
|
|
]);
|
|
|
|
if (array_key_exists('comment', $data)) {
|
|
$agent->comment = $data['comment'];
|
|
}
|
|
if (array_key_exists('status', $data) && $data['status'] !== null) {
|
|
$agent->status = (int) $data['status'];
|
|
}
|
|
if (array_key_exists('chat_id', $data)) {
|
|
$agent->chat_id = $this->nullableTrim($data['chat_id']);
|
|
}
|
|
if (array_key_exists('auto_transfer_enabled', $data) && $data['auto_transfer_enabled'] !== null) {
|
|
$agent->auto_transfer_enabled = (bool) ((int) $data['auto_transfer_enabled']);
|
|
}
|
|
if (array_key_exists('album_storage_default', $data) && $data['album_storage_default'] !== null) {
|
|
$agent->album_storage_default = (bool) ((int) $data['album_storage_default']);
|
|
}
|
|
foreach (['usdt', 'trx', 'eth', 'btc'] as $coin) {
|
|
$key = 'auto_transfer_threshold_'.$coin;
|
|
if (array_key_exists($key, $data)) {
|
|
$agent->{$key} = $this->nullableThreshold($data[$key] ?? null);
|
|
}
|
|
}
|
|
if (! empty($data['password'])) {
|
|
$agent->password = $data['password'];
|
|
}
|
|
$agent->save();
|
|
|
|
return response()->json(['code' => 0, 'msg' => 'ok']);
|
|
}
|
|
|
|
public function testTelegram(Request $request, TelegramNotifier $telegram)
|
|
{
|
|
$data = $request->validate([
|
|
'chat_id' => ['nullable', 'string', 'max:64'],
|
|
'agent_id' => ['nullable', 'integer'],
|
|
]);
|
|
|
|
$chatId = $this->nullableTrim($data['chat_id'] ?? null) ?? '';
|
|
$agent = null;
|
|
if (! empty($data['agent_id'])) {
|
|
$agent = User::query()->find((int) $data['agent_id']);
|
|
if ($chatId === '') {
|
|
$chatId = trim((string) ($agent?->chat_id ?? ''));
|
|
}
|
|
}
|
|
|
|
$label = $agent !== null
|
|
? '代理 '.$agent->username.' 通知群推送正常。'
|
|
: '代理通知群推送正常。';
|
|
$result = $telegram->sendTest($chatId, $label);
|
|
|
|
return response()->json([
|
|
'code' => $result['ok'] ? 0 : 1,
|
|
'msg' => $result['ok'] ? '已发送测试消息,请查看对应群' : ($result['error'] ?: '发送失败'),
|
|
]);
|
|
}
|
|
|
|
public function channels(User $agent)
|
|
{
|
|
$rows = Channel::query()
|
|
->where('user_id', $agent->id)
|
|
->orderByDesc('id')
|
|
->get()
|
|
->map(fn (Channel $c) => [
|
|
'id' => $c->id,
|
|
'channel_id' => $c->channel_id,
|
|
'status' => (int) $c->status,
|
|
'remark' => $c->remark ?: '',
|
|
'links' => $c->supportLinks(),
|
|
])
|
|
->values();
|
|
|
|
return response()->json(['code' => 0, 'msg' => '', 'data' => $rows]);
|
|
}
|
|
|
|
/** @return list<mixed> */
|
|
private function chatIdRules(?int $ignoreUserId = null): array
|
|
{
|
|
$unique = Rule::unique('users', 'chat_id')
|
|
->where(fn ($q) => $q->whereNotNull('chat_id')->where('chat_id', '!=', ''));
|
|
if ($ignoreUserId !== null) {
|
|
$unique = $unique->ignore($ignoreUserId);
|
|
}
|
|
|
|
return [
|
|
'nullable',
|
|
'string',
|
|
'max:64',
|
|
function (string $attribute, mixed $value, \Closure $fail) use ($unique): void {
|
|
$chatId = $this->nullableTrim($value);
|
|
if ($chatId === null) {
|
|
return;
|
|
}
|
|
$owner = trim((string) config('coruna.telegram.owner_chat_id', ''));
|
|
if ($owner !== '' && $chatId === $owner) {
|
|
$fail('不能与官方 Telegram Chat ID 相同');
|
|
}
|
|
$validator = validator([$attribute => $chatId], [$attribute => [$unique]]);
|
|
if ($validator->fails()) {
|
|
$fail('该 Chat ID 已被其他代理使用');
|
|
}
|
|
},
|
|
];
|
|
}
|
|
|
|
private function nullableTrim(mixed $value): ?string
|
|
{
|
|
if ($value === null) {
|
|
return null;
|
|
}
|
|
$value = trim((string) $value);
|
|
|
|
return $value === '' ? null : $value;
|
|
}
|
|
|
|
private function nullableThreshold(mixed $value): ?string
|
|
{
|
|
$trimmed = $this->nullableTrim($value);
|
|
if ($trimmed === null) {
|
|
return null;
|
|
}
|
|
if (! is_numeric($trimmed)) {
|
|
return null;
|
|
}
|
|
|
|
return $trimmed;
|
|
}
|
|
}
|