[列表接口, 菜单按钮权限 uri, 行主键字段] */ public const CHANNELS = [ 'admin.orders.sport' => ['/admin/order/list', 'admin/order/list', 'id'], 'admin.orders.lhc' => ['/admin/lhcOrder/list', 'admin/lhcOrder/list', 'id'], 'admin.orders.sport_game' => ['/admin/sportGameOrder/list', 'admin/sportGameOrder/list', 'id'], 'admin.orders.jisu_game' => ['/admin/jisuGameOrder/list', 'admin/jisuGameOrder/list', 'id'], 'admin.orders.third_game' => ['/admin/thirdGame/orders', 'admin/thirdGame/orders', 'id'], 'admin.orders.bet' => ['/admin/bet', 'admin/bet', 'id'], 'admin.orders.recharge' => ['/admin/wallet', 'admin/wallet', 'id'], 'admin.orders.payment' => ['/admin/paymentOrder', 'admin/paymentOrder', 'id'], 'admin.orders.third_party_deposit' => ['/admin/thirdPartyDeposit', 'admin/thirdPartyDeposit', 'id'], 'admin.orders.payment_withdraw' => ['/admin/withdraw/rmb', 'admin/withdraw/rmb', 'id'], 'admin.orders.withdraw' => ['/admin/withdraw', 'admin/withdraw', 'id'], 'admin.orders.manual_audit' => ['/admin/manualAudit', 'admin/manualAudit', 'id'], 'admin.users' => ['/admin/user', 'admin/user', 'id'], 'admin.users.online' => ['/admin/online/users', 'admin/online/users', 'member_id'], ]; /** 表 => 默认频道(payment_orders 按 type 分流,见 paymentOrders) */ private const RESOURCE_CHANNELS = [ 'sport_order' => 'admin.orders.sport', 'lhc_order' => 'admin.orders.lhc', 'sport_game_order' => 'admin.orders.sport_game', 'jisu_game_order' => 'admin.orders.jisu_game', 'third_game_orders' => 'admin.orders.third_game', 'bets' => 'admin.orders.bet', 'recharges' => 'admin.orders.recharge', 'withdraws' => 'admin.orders.withdraw', 'manual_audits' => 'admin.orders.manual_audit', 'users' => 'admin.users', AdminRealtime::USER_ONLINE => 'admin.users.online', ]; /** * @return list 解析不到(已删除或不在列表口径内)的行不返回,deleted / offline 只带 key */ public static function resolve(string $resource, string $id, string $action): array { if ($resource === 'payment_orders') { return self::paymentOrders($id, $action); } $channel = self::RESOURCE_CHANNELS[$resource] ?? null; if ($channel === null) { return []; } if ($resource === AdminRealtime::USER_ONLINE) { return self::online($id, $action); } if ($action === 'deleted') { return [self::message($channel, ['id' => self::castId($id)], null)]; } $rows = match ($resource) { 'sport_order' => self::viaController(Order::class, 'list', ['id' => $id]), 'lhc_order' => self::viaController(LhcOrder::class, 'list', ['id' => $id]), 'sport_game_order' => self::viaController(SportGameOrder::class, 'list', ['id' => $id]), 'jisu_game_order' => self::viaController(JisuGameOrder::class, 'list', ['id' => $id]), 'bets' => self::viaController(Bet::class, 'index', ['id' => $id]), 'recharges' => self::viaController(Wallet::class, 'index', ['id' => $id]), 'withdraws' => self::viaController(Withdraw::class, 'index', ['id' => $id]), 'third_game_orders' => app(ThirdGameOrderService::class)->paginate(['id' => $id])['list'] ?? [], 'manual_audits' => ManualAuditService::list(['id' => $id])['data'] ?? [], 'users' => UserService::paginate(['id' => $id])['data'] ?? [], }; $row = self::pick($rows, 'id', $id); return $row === null ? [] : [self::message($channel, ['id' => self::castId($id)], $row)]; } /** 代收(type=1)进「代收订单」与「三方充值」两张列表,代付(type=2/4)进「人民币提现」 */ private static function paymentOrders(string $id, string $action): array { $key = ['id' => self::castId($id)]; if ($action === 'deleted') { return array_map(static fn(string $channel): array => self::message($channel, $key, null), ['admin.orders.payment', 'admin.orders.third_party_deposit', 'admin.orders.payment_withdraw']); } $type = (int) DB::table('payment_orders')->where('id', $id)->value('type'); $messages = []; if ($type === PaymentOrderService::TYPE_PAY) { $row = self::pick(PaymentOrderService::paginate(['type' => PaymentOrderService::TYPE_PAY, 'id' => $id])['data'] ?? [], 'id', $id); if ($row !== null) $messages[] = self::message('admin.orders.payment', $key, $row); $row = self::pick(ThirdPartyDepositService::list(['id' => $id])['data'] ?? [], 'id', $id); if ($row !== null) $messages[] = self::message('admin.orders.third_party_deposit', $key, $row); } elseif (in_array($type, [PaymentOrderService::TYPE_PAYOUT, PaymentOrderService::TYPE_SELF_PAYOUT], true)) { $row = self::pick(self::viaController(Withdraw::class, 'rmb', ['id' => $id, 'type' => $type]), 'id', $id); if ($row !== null) $messages[] = self::message('admin.orders.payment_withdraw', $key, $row); } return $messages; } private static function online(string $userId, string $action): array { $user = DB::table('users')->where('id', $userId)->first(['member_id', 'user_id']); if (!$user) { return []; } $key = ['member_id' => (string) ($user->member_id ?: $user->user_id)]; if ($action === 'offline') { return [self::message('admin.users.online', $key, null)]; } $row = OnlineUserService::list(['user_ids' => [(int) $userId], 'limit' => 1])['data'][0] ?? null; return $row === null ? [] : [self::message('admin.users.online', $key, self::normalize($row))]; } /** * 以内部子请求调用后台列表控制器(与前端请求列表接口走同一段查询与格式化)。 * 列表接口必须支持 id 筛选,pick() 再按 id 校验一次,防止筛选失效时推错行。 */ private static function viaController(string $class, string $method, array $query): array { $app = app(); $original = $app->make('request'); $request = Request::create('/', 'GET', $query + ['page' => 1, 'limit' => 20]); $request->user = $original->user ?? null; $app->instance('request', $request); Facade::clearResolvedInstance('request'); try { $response = $app->call([$app->make($class), $method]); } finally { $app->instance('request', $original); Facade::clearResolvedInstance('request'); } $body = $response instanceof JsonResponse ? $response->getData(true) : []; if ((int) ($body['code'] ?? -1) !== HttpStatus::OK) { throw new RuntimeException($class . '@' . $method . ' failed: ' . ($body['msg'] ?? 'non-json response')); } return $body['data']['data'] ?? []; } private static function pick($rows, string $field, string $id): ?array { foreach (self::normalize($rows) as $row) { if (is_array($row) && (string) ($row[$field] ?? '') === $id) { return $row; } } return null; } /** 与 HTTP 响应相同的序列化(模型的 appends / hidden / casts 均生效) */ private static function normalize($value): array { return json_decode(json_encode($value), true) ?: []; } private static function message(string $channel, array $key, ?array $data): array { return ['channel' => $channel, 'key' => $key, 'data' => $data]; } private static function castId(string $id) { return ctype_digit($id) ? (int) $id : $id; } }