doge hai 5 días
pai
achega
7db00e2b49

+ 17 - 5
app/Http/Controllers/admin/RechargeChannel.php

@@ -13,6 +13,8 @@ use Illuminate\Validation\ValidationException;
 use Illuminate\Database\Eloquent\ModelNotFoundException;
 use Exception;
 use App\Constants\HttpStatus;
+use App\Constants\Util;
+use App\Services\Payment\RechargeChannelLogoService;
 
 class RechargeChannel extends Controller
 {
@@ -191,14 +193,24 @@ class RechargeChannel extends Controller
             }
             $count = $query->count();
             $companyStatuses = PaymentProviderService::statuses();
-            $list = $query
+            $channels = $query
                 ->forPage($page, $limit)
                 ->orderBy('sort', 'asc')
                 ->orderBy('id', 'asc')
-                ->get()->map(function (RechargeChannelModel $channel) use ($companyStatuses) {
-                    $row = $channel->toArray();
-                    return $row + PaymentProviderCatalog::channelDisplay($row, $companyStatuses);
-                });
+                ->get();
+            $configuredLogos = [];
+            $logoTypes = $channels->filter(fn (RechargeChannelModel $channel) => (int)$channel->data_type === 1)
+                ->pluck('type')->map(fn ($type) => (string)$type)->unique()->values()->all();
+            if ($logoTypes !== []) $configuredLogos = RechargeChannelLogoService::configured($logoTypes);
+            $list = $channels->map(function (RechargeChannelModel $channel) use ($companyStatuses, $configuredLogos) {
+                $row = $channel->toArray();
+                $dataType = (int)$row['data_type'];
+                if (RechargeChannelLogoService::supportsDirection($dataType) && ($row['logo'] ?? '') === ''
+                    && $dataType === 1 && isset($configuredLogos[(string)$row['type']])) {
+                    $row['logo'] = (string)Util::ensureUrl($configuredLogos[(string)$row['type']]);
+                }
+                return $row + PaymentProviderCatalog::channelDisplay($row, $companyStatuses);
+            });
         } catch (Exception $e) {
             return $this->error(HttpStatus::CUSTOM_ERROR,$e->getMessage());
         }

+ 9 - 4
app/Models/RechargeChannel.php

@@ -89,11 +89,14 @@ class RechargeChannel extends BaseModel
         }
         $query = $query->whereIn('type', $types);
         
+        $direction = (int)$data_type;
         $fields = ['id', 'data_type','name','type', 'min','max','fixed','rate'];
-        if ((int)$data_type === 1) $fields[] = 'logo';
+        if (\App\Services\Payment\RechargeChannelLogoService::supportsDirection($direction)) {
+            $fields[] = 'logo';
+        }
         $product = $query->orderBy('sort', 'asc')->orderBy('id')->select($fields)->get()->toArray();
         
-        $logos = (int)$data_type === 1 ? \App\Services\Payment\RechargeChannelLogoService::configured(array_column($product, 'type')) : [];
+        $logos = $direction === 1 ? \App\Services\Payment\RechargeChannelLogoService::configured(array_column($product, 'type')) : [];
         $list = [];
         foreach($product as $key => $pv) {
             if (isset($list[$pv['type']]['config'])) {
@@ -125,8 +128,10 @@ class RechargeChannel extends BaseModel
                 'value' => $pv['type'],
                 'config' => $config ?? [],
             ];
-            if ((int)$data_type === 1) {
-                $logo = array_key_exists($pv['type'], $logos) ? $logos[$pv['type']] : ($config['logo'] ?? '');
+            if (\App\Services\Payment\RechargeChannelLogoService::supportsDirection($direction)) {
+                $logo = $direction === 1 && array_key_exists($pv['type'], $logos)
+                    ? $logos[$pv['type']]
+                    : ($config['logo'] ?? '');
                 $list[$pv['type']]['logo'] = $logo ? (string)Util::ensureUrl($logo) : '';
             }
         }

+ 8 - 6
app/Services/Payment/RechargeChannelConfigurationService.php

@@ -131,15 +131,17 @@ class RechargeChannelConfigurationService
                 if (array_key_exists($field, $params) && $params[$field] === null) unset($params[$field]);
             }
             if (!$channel->exists) $params += ['status' => 1, 'sort' => 0];
-            if ($identityChanged && (int)$params['data_type'] === 1 && !array_key_exists('logo', $params)) {
-                $logos = RechargeChannelLogoService::configured([$params['type']]);
-                $peer = RechargeChannel::where('data_type', 1)->where('type', $params['type'])->orderBy('sort')->orderBy('id')->first();
-                $params['logo'] = array_key_exists($params['type'], $logos) ? $logos[$params['type']] : ($peer ? $peer->getRawOriginal('logo') : null);
+            if ($identityChanged && RechargeChannelLogoService::supportsDirection((int)$params['data_type']) && !array_key_exists('logo', $params)) {
+                $dataType = (int)$params['data_type'];
+                $type = (string)$params['type'];
+                $logos = $dataType === 1 ? RechargeChannelLogoService::configured([$type]) : [];
+                $peer = RechargeChannel::where('data_type', $dataType)->where('type', $type)->orderBy('sort')->orderBy('id')->first();
+                $params['logo'] = array_key_exists($type, $logos) ? $logos[$type] : ($peer ? $peer->getRawOriginal('logo') : null);
             }
             $channel->fill($params);
             $channel->save();
-            if ((int)$channel->data_type === 1 && array_key_exists('logo', $params)) {
-                RechargeChannelLogoService::store((string)$channel->type, $params['logo']);
+            if (RechargeChannelLogoService::supportsDirection((int)$channel->data_type) && array_key_exists('logo', $params)) {
+                RechargeChannelLogoService::store((string)$channel->type, $params['logo'], (int)$channel->data_type);
             }
             return $channel;
         }, 3);

+ 9 - 3
app/Services/Payment/RechargeChannelLogoService.php

@@ -12,6 +12,11 @@ use Illuminate\Validation\ValidationException;
 
 final class RechargeChannelLogoService
 {
+    public static function supportsDirection(int $dataType): bool
+    {
+        return in_array($dataType, [1, 2], true);
+    }
+
     public static function normalize($logo): ?string
     {
         Validator::make(['logo' => $logo], ['logo' => ['nullable', 'string', 'max:2048']])->validate();
@@ -52,14 +57,15 @@ final class RechargeChannelLogoService
         return ['total' => count($rows), 'data' => array_values($rows)];
     }
 
-    public static function store(string $type, ?string $logo): void
+    public static function store(string $type, ?string $logo, int $dataType = 1): void
     {
-        if (Schema::hasTable('recharge_method_logos')) {
+        if (!self::supportsDirection($dataType)) return;
+        if ($dataType === 1 && Schema::hasTable('recharge_method_logos')) {
             $before = DB::table('recharge_method_logos')->where('channel_type', $type)->first();
             DB::table('recharge_method_logos')->updateOrInsert(['channel_type' => $type],
                 ['logo' => $logo, 'created_at' => $before->created_at ?? now(), 'updated_at' => now()]);
         }
-        RechargeChannel::where('data_type', 1)->where('type', $type)->update(['logo' => $logo]);
+        RechargeChannel::where('data_type', $dataType)->where('type', $type)->update(['logo' => $logo]);
     }
 
     public function save(array $input): array

+ 1 - 0
docs/前端交付/bot-28/后台/README.md

@@ -21,6 +21,7 @@
 | 活动列表:到期自动下架、状态筛选与列表展示(2026-11-05) | [活动列表-到期自动下架](活动列表-到期自动下架.md) |
 | 会员列表:默认按注册时间倒序,钱包/三方/冻结/余额宝/投注/打码量列服务端排序(2026-11-05) | [会员列表-默认排序与列排序](会员列表-默认排序与列排序.md) |
 | 菜单角标:列表新增/编辑/删除,三语角标(2026-11-05) | [菜单角标配置-后台接口](菜单角标配置-后台接口.md) |
+| 赛事列表:按是否置顶筛选(`is_rec`,2026-11-05) | [赛事列表-置顶筛选](赛事列表-置顶筛选.md) |
 | 资金流动:区间总计与每日列表 | [资金流动-前端接入](资金流动-前端接入.md) |
 | 用户报表:玩过的游戏大类、盈亏(充值-提现)与充值/提现/盈亏/余额排序(2026-11-04) | [用户报表-游戏分类、盈亏与排序](用户报表-游戏分类与提充差值.md) |
 | 龙虎榜:五类排名与分页 | [龙虎榜-前端接入](龙虎榜-前端接入.md) |

+ 47 - 0
docs/前端交付/bot-28/后台/赛事列表-置顶筛选.md

@@ -0,0 +1,47 @@
+# 赛事列表(EventList):是否置顶筛选
+
+页面路径:`views/orderManage/EventList`(菜单「足球管理 → 赛事列表」)
+
+列表接口:`GET /admin/sport/list`,管理员认证,成功 `code=0`。
+
+## 筛选参数
+
+| 参数 | 类型 | 说明 |
+|---|---|---|
+| `is_rec` | int,可选 | `1` 仅置顶赛事;`0` 仅未置顶;不传或清空筛选项时不带该参数,表示不按置顶过滤 |
+
+与其它整型筛选项(如 `state`、`is_locked`)一致:下拉清空后请求里不要传 `is_rec=""`,避免校验失败。
+
+示例:
+
+```http
+GET /admin/sport/list?page=1&limit=15&is_rec=1
+GET /admin/sport/list?page=1&limit=15&is_rec=0
+```
+
+## 列表字段
+
+| 字段 | 说明 |
+|---|---|
+| `data.data[].is_rec` | `1` 置顶,`0` 未置顶 |
+
+## 批量设置置顶
+
+`POST /admin/sport/setStatus`
+
+```json
+{
+  "id": [1, 2],
+  "is_rec": 1
+}
+```
+
+`is_rec` 传 `0` 取消置顶。可与 `status`、`state`、`is_locked` 等同次提交(仅传需要变更的字段)。
+
+## 前端改造要点
+
+1. 在 `params` 中增加 `is_rec: undefined`(或与现有筛选项相同的初始值)。
+2. 搜索区增加「是否置顶」`el-select`,`clearable`,选项:`{ label: '是', value: 1 }`、`{ label: '否', value: 0 }`,`v-model="params.is_rec"`。
+3. 发起列表请求时沿用现有逻辑;若封装了「去掉空参数」,清空下拉后应自动不传 `is_rec`。
+
+权限 URI 与赛事列表现有一致(`admin/sport/list`)。

+ 24 - 0
tests/Integration/PaymentChannelOptionsTest.php

@@ -200,6 +200,30 @@ class PaymentChannelOptionsTest extends TestCase
         }
     }
 
+    public function test_withdraw_logos_upload_save_share_by_method_and_list_returns_logo(): void
+    {
+        $this->app['config']->set('app.url', 'https://admin.example.test');
+        $this->assertSame(0, $this->saveChannel(['id' => 25, 'data_type' => 2, 'payment_company' => 'jd', 'type' => 'JDpay', 'rate' => '0.01', 'logo' => '/storage/images/jd-withdraw.png'])['code']);
+        $row = \App\Models\RechargeChannel::find(25);
+        $this->assertSame('https://admin.example.test/storage/images/jd-withdraw.png', $row->logo);
+        $deposit = \App\Models\RechargeChannel::find(24);
+        $this->assertSame('', $deposit->logo);
+        $this->assertSame(0, $this->saveChannel($this->newChannel(['data_type' => 2, 'payment_company' => 'jd', 'type' => 'JDpay', 'rate' => '0.02', 'key' => 'withdraw-peer']))['code']);
+        foreach (\App\Models\RechargeChannel::where('data_type', 2)->where('type', 'JDpay')->get() as $peer) {
+            $this->assertSame($row->logo, $peer->logo);
+        }
+        $listRow = $this->channelList(['data_type' => '2', 'payment_company' => 'jd'])['data'][0];
+        $this->assertArrayHasKey('logo', $listRow);
+        $this->assertSame('https://admin.example.test/storage/images/jd-withdraw.png', $listRow['logo']);
+        $formatted = array_column(\App\Models\RechargeChannel::getFormatChannel(2, 1), null, 'value');
+        $this->assertSame('https://admin.example.test/storage/images/jd-withdraw.png', $formatted['JDpay']['logo'] ?? '');
+        $this->assertSame('', array_column(\App\Models\RechargeChannel::getFormatChannel(1, 1), null, 'value')['JDpay']['logo'] ?? 'x');
+        $this->assertSame(0, $this->saveChannel(['id' => 25, 'data_type' => 2, 'payment_company' => 'jd', 'type' => 'JDpay', 'rate' => '0.01', 'logo' => null])['code']);
+        foreach (\App\Models\RechargeChannel::where('data_type', 2)->where('type', 'JDpay')->get() as $peer) {
+            $this->assertSame('', $peer->logo);
+        }
+    }
+
     public function test_company_options_use_all_rows_not_first_page(): void
     {
         $page1 = $this->channelList(['data_type' => '1']);