| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586 |
- <?php
- namespace App\Http\Controllers\admin;
- use App\Constants\HttpStatus;
- use App\Http\Controllers\Controller;
- use App\Models\EgameItem;
- use Exception;
- use Illuminate\Http\JsonResponse;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Validation\Rule;
- use Illuminate\Validation\ValidationException;
- class Egame extends Controller
- {
- private const FALLBACK_GAME_TYPE_NAMES = [
- 1 => '视讯',
- 2 => '电子',
- 3 => '彩票',
- 4 => '体育',
- 5 => '电竞',
- 6 => '捕鱼',
- 7 => '棋牌',
- ];
- public function items(): JsonResponse
- {
- try {
- request()->validate([
- 'item_type' => ['nullable', Rule::in([
- EgameItem::TYPE_CATEGORY,
- EgameItem::TYPE_PLATFORM,
- EgameItem::TYPE_GAME,
- ])],
- 'platform_scope' => ['nullable', Rule::in([
- EgameItem::PLATFORM_SCOPE_GLOBAL,
- EgameItem::PLATFORM_SCOPE_CATEGORY,
- ])],
- 'plat_type' => ['nullable', 'string', 'max:32'],
- 'game_type' => ['nullable', 'integer', 'min:0', 'max:255'],
- 'game_code' => ['nullable', 'string', 'max:128'],
- 'keyword' => ['nullable', 'string', 'max:128'],
- 'status' => ['nullable', 'integer', Rule::in([
- EgameItem::STATUS_DISABLED,
- EgameItem::STATUS_ENABLED,
- ])],
- 'page' => ['nullable', 'integer', 'min:1'],
- 'limit' => ['nullable', 'integer', 'min:1', 'max:200'],
- ]);
- $query = EgameItem::query();
- foreach (['item_type', 'plat_type', 'game_type', 'game_code', 'status'] as $field) {
- $value = request()->input($field);
- if ($value !== null && $value !== '') {
- $query->where($field, $value);
- }
- }
- $platformScope = (string) request()->input('platform_scope', '');
- if ($platformScope !== '') {
- $itemType = (string) request()->input('item_type', '');
- if ($itemType !== '' && $itemType !== EgameItem::TYPE_PLATFORM) {
- throw new Exception('platform_scope 只适用于平台配置', HttpStatus::CUSTOM_ERROR);
- }
- $query->where('item_type', EgameItem::TYPE_PLATFORM);
- if ($platformScope === EgameItem::PLATFORM_SCOPE_GLOBAL) {
- $query->where('game_type', 0);
- } else {
- $query->where('game_type', '>', 0);
- }
- }
- $keyword = trim((string) request()->input('keyword', ''));
- if ($keyword !== '') {
- $query->where(function ($query) use ($keyword) {
- $query->where('name', 'like', "%{$keyword}%")
- ->orWhere('plat_type', 'like', "%{$keyword}%")
- ->orWhere('game_code', 'like', "%{$keyword}%");
- });
- }
- $limit = (int) request()->input('limit', 15);
- $page = (int) request()->input('page', 1);
- $total = (clone $query)->count();
- $states = $this->catalogStates();
- $list = $query->orderByDesc('sort')
- ->orderByDesc('id')
- ->forPage($page, $limit)
- ->get()
- ->map(fn (EgameItem $item) => $this->decorateItem($item, $states))
- ->values();
- } catch (ValidationException $e) {
- return $this->error(HttpStatus::VALIDATION_FAILED, $e->validator->errors()->first());
- } catch (Exception $e) {
- return $this->error(HttpStatus::CUSTOM_ERROR, $e->getMessage());
- }
- return $this->success(['total' => $total, 'data' => $list]);
- }
- public function options(): JsonResponse
- {
- try {
- $states = $this->catalogStates();
- $categoryPlatformCounts = $this->groupedCounts(
- EgameItem::query()
- ->where('item_type', EgameItem::TYPE_PLATFORM)
- ->where('game_type', '>', 0),
- 'game_type',
- 'plat_type'
- );
- $categoryGameCounts = $this->groupedCounts(
- EgameItem::query()->where('item_type', EgameItem::TYPE_GAME),
- 'game_type',
- 'id'
- );
- $platformCategoryCounts = $this->groupedCounts(
- EgameItem::query()
- ->where('item_type', EgameItem::TYPE_PLATFORM)
- ->where('game_type', '>', 0),
- 'plat_type',
- 'game_type'
- );
- $platformGameCounts = $this->groupedCounts(
- EgameItem::query()->where('item_type', EgameItem::TYPE_GAME),
- 'plat_type',
- 'id'
- );
- $categories = EgameItem::query()
- ->where('item_type', EgameItem::TYPE_CATEGORY)
- ->orderByDesc('sort')
- ->orderBy('game_type')
- ->get()
- ->map(function (EgameItem $item) use ($states, $categoryPlatformCounts, $categoryGameCounts) {
- $this->decorateItem($item, $states);
- $key = (string) $item->game_type;
- $item->setAttribute('platform_count', $categoryPlatformCounts[$key] ?? 0);
- $item->setAttribute('game_count', $categoryGameCounts[$key] ?? 0);
- return $item;
- })
- ->values();
- $platforms = EgameItem::query()
- ->where('item_type', EgameItem::TYPE_PLATFORM)
- ->where('game_type', 0)
- ->orderByDesc('sort')
- ->orderBy('name')
- ->orderBy('plat_type')
- ->get()
- ->map(function (EgameItem $item) use ($states, $platformCategoryCounts, $platformGameCounts) {
- $this->decorateItem($item, $states);
- $key = strtolower((string) $item->plat_type);
- $item->setAttribute('category_count', $platformCategoryCounts[$key] ?? 0);
- $item->setAttribute('game_count', $platformGameCounts[$key] ?? 0);
- return $item;
- })
- ->values();
- } catch (Exception $e) {
- return $this->error(HttpStatus::CUSTOM_ERROR, $e->getMessage());
- }
- return $this->success([
- 'categories' => $categories,
- 'platforms' => $platforms,
- 'statuses' => [
- ['value' => EgameItem::STATUS_ENABLED, 'label' => '开启'],
- ['value' => EgameItem::STATUS_DISABLED, 'label' => '关闭'],
- ],
- 'ingress' => [
- ['value' => '1', 'label' => '仅电脑'],
- ['value' => '2', 'label' => '仅手机'],
- ['value' => '3', 'label' => '通用'],
- ],
- ]);
- }
- public function update(): JsonResponse
- {
- try {
- request()->validate([
- 'id' => ['nullable', 'integer', 'min:0'],
- 'item_type' => ['nullable', Rule::in([
- EgameItem::TYPE_CATEGORY,
- EgameItem::TYPE_PLATFORM,
- EgameItem::TYPE_GAME,
- ])],
- 'platform_scope' => ['nullable', Rule::in([
- EgameItem::PLATFORM_SCOPE_GLOBAL,
- EgameItem::PLATFORM_SCOPE_CATEGORY,
- ])],
- 'plat_type' => ['nullable', 'string', 'max:32'],
- 'game_type' => ['nullable', 'integer', 'min:0', 'max:255'],
- 'game_code' => ['nullable', 'string', 'max:128'],
- 'name' => ['nullable', 'string', 'max:128'],
- 'ingress' => ['nullable', Rule::in(['1', '2', '3'])],
- 'lobby_enabled' => ['nullable', 'boolean'],
- 'logo' => ['nullable', 'string', 'max:500'],
- 'logo_langs' => ['nullable', 'array'],
- 'status' => ['nullable', 'integer', Rule::in([
- EgameItem::STATUS_DISABLED,
- EgameItem::STATUS_ENABLED,
- ])],
- 'sort' => ['nullable', 'integer', 'min:-999999', 'max:999999'],
- ]);
- $id = (int) request()->input('id', 0);
- $item = DB::transaction(function () use ($id) {
- if ($id > 0) {
- $item = EgameItem::query()->find($id);
- if (!$item) {
- throw new Exception('配置不存在', HttpStatus::CUSTOM_ERROR);
- }
- $this->assertIdentityUnchanged($item);
- } else {
- $identity = $this->normalizedIdentityForCreate();
- $item = EgameItem::query()->firstOrNew($identity);
- }
- $data = $this->mutableItemData($item, !$item->exists);
- $name = array_key_exists('name', $data) ? $data['name'] : (string) $item->name;
- if ($item->isCategory() && trim($name) === '') {
- throw new Exception('分类名称不能为空', HttpStatus::CUSTOM_ERROR);
- }
- if (!empty($data) || !$item->exists) {
- $item->fill($data)->save();
- }
- return $item->fresh();
- });
- } catch (ValidationException $e) {
- return $this->error(HttpStatus::VALIDATION_FAILED, $e->validator->errors()->first());
- } catch (Exception $e) {
- return $this->error((int) $e->getCode(), $e->getMessage());
- }
- return $this->success($this->decorateItem($item, $this->catalogStates()));
- }
- public function setStatus(): JsonResponse
- {
- try {
- request()->validate([
- 'id' => ['required', 'integer', 'min:1'],
- 'status' => ['required', 'integer', Rule::in([
- EgameItem::STATUS_DISABLED,
- EgameItem::STATUS_ENABLED,
- ])],
- ]);
- $item = EgameItem::query()->find((int) request()->input('id'));
- if (!$item) {
- throw new Exception('配置不存在', HttpStatus::CUSTOM_ERROR);
- }
- $item->status = (int) request()->input('status');
- $item->save();
- $item = $item->fresh();
- } catch (ValidationException $e) {
- return $this->error(HttpStatus::VALIDATION_FAILED, $e->validator->errors()->first());
- } catch (Exception $e) {
- return $this->error((int) $e->getCode(), $e->getMessage());
- }
- return $this->success($this->decorateItem($item, $this->catalogStates()));
- }
- public function delete(): JsonResponse
- {
- try {
- request()->validate([
- 'id' => ['required', 'integer', 'min:1'],
- ]);
- if (!EgameItem::query()->whereKey((int) request()->input('id'))->exists()) {
- throw new Exception('配置不存在', HttpStatus::CUSTOM_ERROR);
- }
- } catch (ValidationException $e) {
- return $this->error(HttpStatus::VALIDATION_FAILED, $e->validator->errors()->first());
- } catch (Exception $e) {
- return $this->error((int) $e->getCode(), $e->getMessage());
- }
- return $this->error(
- HttpStatus::CUSTOM_ERROR,
- '第三方游戏目录由同步任务维护,不能删除,请将状态设为关闭'
- );
- }
- private function normalizedIdentityForCreate(): array
- {
- $input = request()->all();
- if (!array_key_exists('item_type', $input) || !array_key_exists('game_type', $input)) {
- throw new Exception('新增配置必须填写 item_type 和 game_type', HttpStatus::CUSTOM_ERROR);
- }
- $itemType = (string) request()->input('item_type', '');
- $platformScope = (string) request()->input('platform_scope', '');
- if (!in_array($itemType, [
- EgameItem::TYPE_CATEGORY,
- EgameItem::TYPE_PLATFORM,
- EgameItem::TYPE_GAME,
- ], true)) {
- throw new Exception('item_type 参数错误', HttpStatus::CUSTOM_ERROR);
- }
- if ($platformScope !== '' && $itemType !== EgameItem::TYPE_PLATFORM) {
- throw new Exception('platform_scope 只适用于平台配置', HttpStatus::CUSTOM_ERROR);
- }
- $identity = [
- 'item_type' => $itemType,
- 'plat_type' => strtolower(trim((string) request()->input('plat_type', ''))),
- 'game_type' => (int) request()->input('game_type'),
- 'game_code' => trim((string) request()->input('game_code', '')),
- ];
- if ($itemType === EgameItem::TYPE_CATEGORY) {
- if ($identity['game_type'] <= 0) {
- throw new Exception('分类 game_type 必须大于 0', HttpStatus::CUSTOM_ERROR);
- }
- $identity['plat_type'] = '';
- $identity['game_code'] = '';
- return $identity;
- }
- if ($identity['plat_type'] === '') {
- throw new Exception('平台代码不能为空', HttpStatus::CUSTOM_ERROR);
- }
- if ($itemType === EgameItem::TYPE_PLATFORM) {
- if ($platformScope === EgameItem::PLATFORM_SCOPE_GLOBAL) {
- $identity['game_type'] = 0;
- }
- if ($platformScope === EgameItem::PLATFORM_SCOPE_CATEGORY && $identity['game_type'] <= 0) {
- throw new Exception('平台分类关联的 game_type 必须大于 0', HttpStatus::CUSTOM_ERROR);
- }
- $identity['game_code'] = '';
- return $identity;
- }
- if ($identity['game_type'] <= 0) {
- throw new Exception('游戏 game_type 必须大于 0', HttpStatus::CUSTOM_ERROR);
- }
- if ($identity['game_code'] === '') {
- throw new Exception('游戏配置必须填写 game_code', HttpStatus::CUSTOM_ERROR);
- }
- return $identity;
- }
- private function assertIdentityUnchanged(EgameItem $item): void
- {
- $input = request()->all();
- $checks = [
- 'item_type' => (string) $item->item_type,
- 'plat_type' => strtolower((string) $item->plat_type),
- 'game_type' => (int) $item->game_type,
- 'game_code' => (string) $item->game_code,
- ];
- foreach ($checks as $field => $expected) {
- if (!array_key_exists($field, $input) || $input[$field] === null) {
- continue;
- }
- $actual = $input[$field];
- if ($field === 'plat_type') {
- $actual = strtolower(trim((string) $actual));
- } elseif ($field === 'game_type') {
- $actual = (int) $actual;
- } else {
- $actual = trim((string) $actual);
- }
- if ($actual !== $expected) {
- throw new Exception('目录标识不允许修改:' . $field, HttpStatus::CUSTOM_ERROR);
- }
- }
- if (
- array_key_exists('platform_scope', $input)
- && $input['platform_scope'] !== null
- && $input['platform_scope'] !== ''
- ) {
- $expectedScope = '';
- if ($item->isGlobalPlatform()) {
- $expectedScope = EgameItem::PLATFORM_SCOPE_GLOBAL;
- } elseif ($item->isPlatformRelation()) {
- $expectedScope = EgameItem::PLATFORM_SCOPE_CATEGORY;
- }
- if ((string) $input['platform_scope'] !== $expectedScope) {
- throw new Exception('目录标识不允许修改:platform_scope', HttpStatus::CUSTOM_ERROR);
- }
- }
- }
- private function mutableItemData(EgameItem $item, bool $isNew): array
- {
- $input = request()->all();
- $data = $isNew ? [
- 'name' => '',
- 'ingress' => '3',
- 'lobby_enabled' => 0,
- 'logo' => '',
- 'logo_langs' => null,
- 'status' => EgameItem::STATUS_ENABLED,
- 'sort' => 0,
- ] : [];
- foreach (['name', 'ingress', 'logo'] as $field) {
- if (array_key_exists($field, $input)) {
- $data[$field] = trim((string) $input[$field]);
- }
- }
- if (array_key_exists('logo_langs', $input)) {
- $data['logo_langs'] = $item->item_type === EgameItem::TYPE_GAME
- ? $input['logo_langs']
- : null;
- }
- if (array_key_exists('status', $input)) {
- $data['status'] = (int) $input['status'];
- }
- if (array_key_exists('sort', $input)) {
- $data['sort'] = (int) $input['sort'];
- }
- if (array_key_exists('lobby_enabled', $input)) {
- $data['lobby_enabled'] = $item->isPlatformRelation()
- ? (int) request()->boolean('lobby_enabled')
- : 0;
- }
- if ($item->item_type !== EgameItem::TYPE_GAME && array_key_exists('ingress', $data)) {
- $data['ingress'] = '3';
- }
- if ($item->item_type !== EgameItem::TYPE_GAME && array_key_exists('logo_langs', $data)) {
- $data['logo_langs'] = null;
- }
- if (!$item->isPlatformRelation() && array_key_exists('lobby_enabled', $data)) {
- $data['lobby_enabled'] = 0;
- }
- return $data;
- }
- private function catalogStates(): array
- {
- $states = [
- 'categories' => [],
- 'global_platforms' => [],
- 'platform_relations' => [],
- ];
- $rows = EgameItem::query()
- ->whereIn('item_type', [EgameItem::TYPE_CATEGORY, EgameItem::TYPE_PLATFORM])
- ->get();
- foreach ($rows as $row) {
- if ($row->isCategory()) {
- $states['categories'][(string) $row->game_type] = $row;
- continue;
- }
- $platType = strtolower((string) $row->plat_type);
- if ($row->isGlobalPlatform()) {
- $states['global_platforms'][$platType] = $row;
- continue;
- }
- if ($row->isPlatformRelation()) {
- $states['platform_relations'][$platType][(string) $row->game_type] = $row;
- }
- }
- return $states;
- }
- private function decorateItem(EgameItem $item, array $states): EgameItem
- {
- $platType = strtolower((string) $item->plat_type);
- $gameType = (string) $item->game_type;
- $disabledBy = null;
- $category = $states['categories'][$gameType] ?? null;
- $globalPlatform = $states['global_platforms'][$platType] ?? null;
- $platformRelation = $states['platform_relations'][$platType][$gameType] ?? null;
- if ($item->isCategory()) {
- if (!$this->enabled($item)) {
- $disabledBy = 'category';
- }
- } elseif ($item->isGlobalPlatform()) {
- if (!$this->enabled($item)) {
- $disabledBy = 'platform';
- }
- } elseif ($item->isPlatformRelation()) {
- if (!$this->enabled($category)) {
- $disabledBy = 'category';
- } elseif (!$this->enabled($globalPlatform)) {
- $disabledBy = 'platform';
- } elseif (!$this->enabled($item)) {
- $disabledBy = 'platform_category';
- }
- } else {
- if (!$this->enabled($category)) {
- $disabledBy = 'category';
- } elseif (!$this->enabled($globalPlatform)) {
- $disabledBy = 'platform';
- } elseif (!$this->enabled($platformRelation)) {
- $disabledBy = 'platform_category';
- } elseif (!$this->enabled($item)) {
- $disabledBy = 'game';
- }
- }
- $item->setAttribute('game_type_name', $this->gameTypeName((int) $item->game_type, $states));
- $item->setAttribute(
- 'platform_name',
- $this->platformName($item, $globalPlatform, $platformRelation)
- );
- if ($item->item_type === EgameItem::TYPE_PLATFORM) {
- $item->setAttribute(
- 'platform_scope',
- $item->isGlobalPlatform()
- ? EgameItem::PLATFORM_SCOPE_GLOBAL
- : EgameItem::PLATFORM_SCOPE_CATEGORY
- );
- } else {
- $item->setAttribute('platform_scope', null);
- }
- $item->setAttribute('effective_status', $disabledBy === null ? 1 : 0);
- $item->setAttribute('disabled_by', $disabledBy);
- return $item;
- }
- private function enabled(?EgameItem $item): bool
- {
- return $item !== null && $item->status === EgameItem::STATUS_ENABLED;
- }
- private function platformName(
- EgameItem $item,
- ?EgameItem $globalPlatform,
- ?EgameItem $platformRelation
- ): string {
- if ($item->isCategory()) {
- return '';
- }
- if ($item->isGlobalPlatform() && trim((string) $item->name) !== '') {
- return (string) $item->name;
- }
- if ($globalPlatform && trim((string) $globalPlatform->name) !== '') {
- return (string) $globalPlatform->name;
- }
- if ($platformRelation && trim((string) $platformRelation->name) !== '') {
- return (string) $platformRelation->name;
- }
- if ($item->item_type === EgameItem::TYPE_PLATFORM && trim((string) $item->name) !== '') {
- return (string) $item->name;
- }
- return (string) $item->plat_type;
- }
- private function gameTypeName(int $gameType, array $states): string
- {
- if ($gameType <= 0) {
- return '';
- }
- $category = $states['categories'][(string) $gameType] ?? null;
- if ($category && trim((string) $category->name) !== '') {
- return (string) $category->name;
- }
- return self::FALLBACK_GAME_TYPE_NAMES[$gameType] ?? ('分类' . $gameType);
- }
- private function groupedCounts($query, string $groupColumn, string $countColumn): array
- {
- return $query
- ->select($groupColumn)
- ->selectRaw("COUNT(DISTINCT {$countColumn}) AS aggregate")
- ->groupBy($groupColumn)
- ->pluck('aggregate', $groupColumn)
- ->map(fn ($count) => (int) $count)
- ->all();
- }
- }
|