$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, '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(), ]); $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), ]); 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), ]); 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 (! 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 */ 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; } }