新增 UsdtC2C Resource 与页面

This commit is contained in:
Winchen 2025-06-10 08:26:54 +07:00
parent 4630421e08
commit 827a0e8133
38 changed files with 3532 additions and 31 deletions

View File

@ -3,6 +3,7 @@
<component name="BladeInjectionConfiguration" default="false">
<directives>
<data directive="@append" />
<data directive="@assets" />
<data directive="@auth" injection="true" prefix="&lt;?php&#10;if(auth()-&gt;guard(" suffix=")-&gt;check()): ?&gt;" />
<data directive="@aware" injection="true" prefix="&lt;?php $attributes = $attributes-&gt;exceptProps(" suffix="); ?&gt;" />
<data directive="@break" injection="true" prefix="&lt;?php&#10;if(" suffix=") break; ?&gt;" />
@ -30,6 +31,7 @@
<data directive="@elseguest" injection="true" prefix="&lt;?php&#10;elseif(auth()-&gt;guard(" suffix=")-&gt;guest()): ?&gt;" />
<data directive="@elseif" injection="true" prefix="&lt;?php&#10;elseif(" suffix="): ?&gt;" />
<data directive="@empty" injection="true" prefix="&lt;?php&#10;if(empty(" suffix=")): ?&gt;" />
<data directive="@endassets" />
<data directive="@endauth" />
<data directive="@endcan" />
<data directive="@endcanany" />
@ -49,6 +51,7 @@
<data directive="@endisset" />
<data directive="@endlang" />
<data directive="@endonce" />
<data directive="@endpersist" />
<data directive="@endphp" />
<data directive="@endprepend" />
<data directive="@endprependonce" />
@ -56,13 +59,16 @@
<data directive="@endpush" />
<data directive="@endpushif" />
<data directive="@endpushonce" />
<data directive="@endscript" />
<data directive="@endsection" />
<data directive="@endsession" />
<data directive="@endslot" />
<data directive="@endswitch" />
<data directive="@endteleport" />
<data directive="@endunless" />
<data directive="@endverbatim" />
<data directive="@endwhile" />
<data directive="@entangle" injection="true" prefix="&lt;?php \print_r(" suffix="); ?&gt;" />
<data directive="@env" injection="true" prefix="&lt;?php&#10;if(app()-&gt;environment(" suffix=")): ?&gt;" />
<data directive="@error" injection="true" prefix="&lt;?php&#10;$__errorArgs = [" suffix="];&#10;$__bag = $errors-&gt;getBag($__errorArgs[1] ?? 'default');&#10;if ($__bag-&gt;has($__errorArgs[0])) :&#10;if (isset($message)) { $__messageOriginal = $message; }&#10;$message = $__bag-&gt;first($__errorArgs[0]); ?&gt;" />
<data directive="@extends" injection="true" prefix="&lt;?php&#10;echo $__env-&gt;make(" suffix=", \Illuminate\Support\Arr::except(get_defined_vars(), ['__data', '__path']))-&gt;render(); ?&gt;" />
@ -82,10 +88,15 @@
<data directive="@js" injection="true" prefix="&lt;?php _bladeDirective(" suffix="); ?&gt;" />
<data directive="@json" injection="true" prefix="&lt;?php&#10;echo json_encode(" suffix=") ?&gt;" />
<data directive="@lang" injection="true" prefix="&lt;?php&#10;echo app('translator')-&gt;get(" suffix="); ?&gt;" />
<data directive="@livewire" injection="true" prefix="&lt;?php \Livewire\Livewire::mount(" suffix="); ?&gt;" />
<data directive="@livewireScriptConfig" />
<data directive="@livewireScripts" />
<data directive="@livewireStyles" />
<data directive="@method" injection="true" prefix="&lt;?php&#10;echo method_field(" suffix="); ?&gt;" />
<data directive="@once" />
<data directive="@overwrite" />
<data directive="@parent" />
<data directive="@persist" injection="true" prefix="&lt;?php echo e(" suffix="); ?&gt;" />
<data directive="@php" injection="true" prefix="&lt;?php&#10;(" suffix="); ?&gt;" />
<data directive="@prepend" injection="true" prefix="&lt;?php&#10;$__env-&gt;startPrepend(" suffix="); ?&gt;" />
<data directive="@prependonce" injection="true" prefix="&lt;?php&#10;$___phpstorm_prepend_once_params = [" suffix="]; if (! $__env-&gt;hasRenderedOnce($___phpstorm_prepend_once_params[1])): $__env-&gt;markAsRenderedOnce($___phpstorm_prepend_once_params); $__env-&gt;startPrepend($___phpstorm_prepend_once_params[0]); ?&gt;" />
@ -96,6 +107,7 @@
<data directive="@pushonce" injection="true" prefix="&lt;?php&#10;$__php_storm_push_once_params = [" suffix="]; if (!$__env-&gt;hasRenderedOnce($__php_storm_push_once_params[1])): $__env-&gt;markAsRenderedOnce($__php_storm_push_once_params[1]); $__env-&gt;startPush($__php_storm_push_once_params[0]); ?&gt;" />
<data directive="@readonly" injection="true" prefix="&lt;?php&#10;if(" suffix="): echo 'readonly'; endif; ?&gt;" />
<data directive="@required" injection="true" prefix="&lt;?php&#10;if(" suffix="): echo 'required'; endif; ?&gt;" />
<data directive="@script" />
<data directive="@section" injection="true" prefix="&lt;?php&#10;$__env-&gt;startSection(" suffix="); ?&gt;" />
<data directive="@sectionMissing" injection="true" prefix="&lt;?php&#10;if (empty(trim($__env-&gt;yieldContent(" suffix=")))): ?&gt;" />
<data directive="@selected" injection="true" prefix="&lt;?php if(" suffix=") {echo 'selected';}?&gt;" />
@ -105,7 +117,10 @@
<data directive="@stack" injection="true" prefix="&lt;?php&#10;echo $__env-&gt;yieldPushContent(" suffix="); ?&gt;" />
<data directive="@stop" />
<data directive="@style" injection="true" prefix="class=&quot;&lt;?php echo \Illuminate\Support\Arr::toCssStyles(" suffix=")?&gt;&quot;" />
<data directive="@svg" injection="true" prefix="&lt;?php echo e(svg(" suffix=")); ?&gt;" />
<data directive="@switch" injection="true" prefix="&lt;?php&#10;switch(" suffix="): ?&gt;" />
<data directive="@teleport" injection="true" prefix="&lt;?php echo e(" suffix="); ?&gt;" />
<data directive="@this" />
<data directive="@unless" injection="true" prefix="&lt;?php&#10;if (! (" suffix=")): ?&gt;" />
<data directive="@unset" injection="true" prefix="&lt;?php&#10;unset(" suffix="); ?&gt;" />
<data directive="@verbatim" />

View File

@ -17,6 +17,11 @@
</LaravelCodeGeneration>
</option>
<option name="frameworkFound" value="true" />
<option name="translation">
<LaravelTranslation>
<option name="defaultLanguageFromConfig" value="zh_CN" />
</LaravelTranslation>
</option>
<option name="userClassName" value="\App\Models\User" />
</component>
</project>

View File

@ -130,6 +130,33 @@
<path value="$PROJECT_DIR$/vendor/league/flysystem-local" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-uuid" />
<path value="$PROJECT_DIR$/vendor/nunomaduro/collision" />
<path value="$PROJECT_DIR$/vendor/livewire/livewire" />
<path value="$PROJECT_DIR$/vendor/spatie/invade" />
<path value="$PROJECT_DIR$/vendor/spatie/color" />
<path value="$PROJECT_DIR$/vendor/spatie/laravel-package-tools" />
<path value="$PROJECT_DIR$/vendor/masterminds/html5" />
<path value="$PROJECT_DIR$/vendor/ryangjchandler/blade-capture-directive" />
<path value="$PROJECT_DIR$/vendor/psr/cache" />
<path value="$PROJECT_DIR$/vendor/filament/filament" />
<path value="$PROJECT_DIR$/vendor/filament/actions" />
<path value="$PROJECT_DIR$/vendor/filament/infolists" />
<path value="$PROJECT_DIR$/vendor/filament/forms" />
<path value="$PROJECT_DIR$/vendor/symfony/html-sanitizer" />
<path value="$PROJECT_DIR$/vendor/filament/support" />
<path value="$PROJECT_DIR$/vendor/filament/notifications" />
<path value="$PROJECT_DIR$/vendor/filament/widgets" />
<path value="$PROJECT_DIR$/vendor/filament/tables" />
<path value="$PROJECT_DIR$/vendor/anourvalar/eloquent-serialize" />
<path value="$PROJECT_DIR$/vendor/danharrin/date-format-converter" />
<path value="$PROJECT_DIR$/vendor/danharrin/livewire-rate-limiting" />
<path value="$PROJECT_DIR$/vendor/doctrine/dbal" />
<path value="$PROJECT_DIR$/vendor/doctrine/deprecations" />
<path value="$PROJECT_DIR$/vendor/openspout/openspout" />
<path value="$PROJECT_DIR$/vendor/laravel-lang/lang" />
<path value="$PROJECT_DIR$/vendor/kirschbaum-development/eloquent-power-joins" />
<path value="$PROJECT_DIR$/vendor/blade-ui-kit/blade-icons" />
<path value="$PROJECT_DIR$/vendor/blade-ui-kit/blade-heroicons" />
<path value="$PROJECT_DIR$/vendor/league/csv" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.2" />

0
Expin.session.sql Normal file
View File

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Pages;
use Filament\Pages\Page;
class Dashboard extends Page
{
protected static ?string $navigationLabel = '一路发收支记账后台'; // 左侧菜单显示
protected static ?string $title = '控制台'; // 页面标题显示
protected static ?string $slug = 'dashboard'; // URL 路径
protected static string $view = 'filament.pages.dashboard'; // 对应 Blade 文件
public static function getNavigationIcon(): string
{
return 'heroicon-o-home'; // 你可以换成你想要的图标
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Filament\Pages;
use Filament\Pages\Page;
class OtherFunctions extends Page
{
protected static ?int $navigationSort = 20;
protected static ?string $title = '系统设置';
protected static ?string $navigationLabel = '系统设置';
protected static string $view = 'filament.pages.other-functions';
public static function getNavigationIcon(): string
{
return 'heroicon-o-cog'; // ✅ 自订你喜欢的图标,例如齿轮
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\LedgerResource\Pages;
use App\Filament\Resources\LedgerResource\RelationManagers;
use App\Models\Ledger;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class LedgerResource extends Resource
{
protected static ?string $model = Ledger::class;
protected static ?string $navigationLabel = '财务账本';
protected static ?string $pluralLabel = '财务账本';
protected static ?string $label = '账本';
protected static ?string $navigationGroup = '财务管理';
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
public static function form(Form $form): Form
{
return $form
->schema([
//
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('id')->label('ID')->sortable(),
Tables\Columns\TextColumn::make('chat_id')->label('群ID')->searchable(),
Tables\Columns\TextColumn::make('user_id')->label('用户ID')->searchable(),
Tables\Columns\TextColumn::make('type')->label('类型')->formatStateUsing(fn ($state) => $state === 0 ? '收入' : '支出')->sortable(),
Tables\Columns\TextColumn::make('date')->label('记账时间')->dateTime()->sortable(),
Tables\Columns\TextColumn::make('amount')->label('金额')->money('LAK')->sortable(),
Tables\Columns\TextColumn::make('created_at')->label('创建时间')->dateTime()->since(),
Tables\Columns\TextColumn::make('updated_at')->label('更新时间')->dateTime()->since(),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListLedgers::route('/'),
'create' => Pages\CreateLedger::route('/create'),
'edit' => Pages\EditLedger::route('/{record}/edit'),
];
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Filament\Resources\LedgerResource\Pages;
use App\Filament\Resources\LedgerResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
class CreateLedger extends CreateRecord
{
protected static string $resource = LedgerResource::class;
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\LedgerResource\Pages;
use App\Filament\Resources\LedgerResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditLedger extends EditRecord
{
protected static string $resource = LedgerResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\LedgerResource\Pages;
use App\Filament\Resources\LedgerResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListLedgers extends ListRecords
{
protected static string $resource = LedgerResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@ -0,0 +1,98 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\TgCommResource\Pages;
use App\Filament\Resources\TgCommResource\RelationManagers;
use App\Models\TgComm;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Tables\Columns\TextColumn;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
// use Filament\Forms\Components\TextInput;
// use Filament\Forms\Components\Toggle;
use Filament\Forms\Components\{TextInput, Toggle, Section, Grid};
class TgCommResource extends Resource
{
protected static ?string $model = TgComm::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
protected static ?string $navigationLabel = '指令列表'; // 左侧菜单文字
protected static ?int $navigationSort = 10;
protected static ?string $pluralModelLabel = '指令列表'; // 页面标题
protected static ?string $modelLabel = '指令';
public static function form(Form $form): Form
{
return $form->schema([
Section::make('基本信息')->schema([
Grid::make(2)->schema([
TextInput::make('command')
->label('指令内容')
->required(),
TextInput::make('description')
->label('指令用途'),
]),
TextInput::make('action')
->label('使用说明')
->columnSpanFull(),
]),
Section::make('设置')->schema([
Grid::make(3)->schema([
Toggle::make('display')->label('显示在指令列表'),
Toggle::make('manager')->label('管理员权限'),
Toggle::make('valid')->label('启用'),
]),
]),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('id')->label('ID'),
TextColumn::make('command')->label('指令内容'),
TextColumn::make('instructions')->label('指令说明'),
TextColumn::make('memo')->label('使用说明'),
TextColumn::make('dis_play')->label('列表显示'),
TextColumn::make('manager')->label('管理权限'),
TextColumn::make('valid')->label('停/启用'),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListTgComms::route('/'),
'create' => Pages\CreateTgComm::route('/create'),
'edit' => Pages\EditTgComm::route('/{record}/edit'),
];
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Filament\Resources\TgCommResource\Pages;
use App\Filament\Resources\TgCommResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
class CreateTgComm extends CreateRecord
{
protected static string $resource = TgCommResource::class;
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\TgCommResource\Pages;
use App\Filament\Resources\TgCommResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditTgComm extends EditRecord
{
protected static string $resource = TgCommResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\TgCommResource\Pages;
use App\Filament\Resources\TgCommResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListTgComms extends ListRecords
{
protected static string $resource = TgCommResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\UsdtC2cResource\Pages;
use App\Filament\Resources\UsdtC2cResource\RelationManagers;
use App\Models\UsdtC2c;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\Filter;
use Filament\Forms\Components\DatePicker;
class UsdtC2cResource extends Resource
{
protected static ?string $model = UsdtC2c::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
public static function getNavigationGroup(): ?string
{
return '财务管理'; // ⚠️ 注意这要与其他项的 group 完全一致
}
public static function getNavigationIcon(): string
{
return 'heroicon-o-currency-bangladeshi'; // 或自定义你喜欢的图示
}
public static function getNavigationLabel(): string
{
return 'USDT-LAK 记录';
}
public static function getPluralLabel(): string
{
return 'USDT-LAK 记录';
}
public static function form(Form $form): Form
{
return $form
->schema([
//
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('Highest')->label('最高价'),
TextColumn::make('Million')->label('200万成交价'),
TextColumn::make('created_at')->label('时间')->dateTime(),
])
->filters([
Filter::make('created_at')
->form([
DatePicker::make('from'),
DatePicker::make('until'),
])
->query(function ($query, array $data) {
return $query
->when($data['from'], fn ($q) => $q->where('created_at', '>=', $data['from']))
->when($data['until'], fn ($q) => $q->where('created_at', '<=', $data['until']));
}),
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
])
->defaultSort('Highest', 'desc'); // ✅ 放在这里才对
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListUsdtC2cs::route('/'),
'create' => Pages\CreateUsdtC2c::route('/create'),
'edit' => Pages\EditUsdtC2c::route('/{record}/edit'),
];
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Filament\Resources\UsdtC2cResource\Pages;
use App\Filament\Resources\UsdtC2cResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
class CreateUsdtC2c extends CreateRecord
{
protected static string $resource = UsdtC2cResource::class;
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\UsdtC2cResource\Pages;
use App\Filament\Resources\UsdtC2cResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditUsdtC2c extends EditRecord
{
protected static string $resource = UsdtC2cResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\UsdtC2cResource\Pages;
use App\Filament\Resources\UsdtC2cResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListUsdtC2cs extends ListRecords
{
protected static string $resource = UsdtC2cResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@ -0,0 +1,126 @@
<?php
namespace App\Http\Controllers;
use App\Models\UsdtC2c;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
class BinanceController extends Controller
{
public function getMaxUsdtLak()
{
$url = 'https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search';
$response = Http::post($url, [
'asset' => 'USDT',
'fiat' => 'LAK',
'tradeType' => 'SELL',
'page' => 1,
'rows' => 20,
'merchantCheck' => true
]);
if (!$response->ok()) {
return response()->json(['error' => '请求失败'], 500);
}
$ads = $response->json('data');
$maxPrice = collect($ads)->max(fn ($item) => (float) $item['adv']['price']);
return response()->json([
'max_price' => $maxPrice
]);
}
public function getUsdtLakForAmount()
{
$url = 'https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search';
$amount = 20000000; // 查询200万LAK的卖价
$response = Http::post($url, [
'asset' => 'USDT',
'fiat' => 'LAK',
'tradeType' => 'SELL',
'page' => 1,
'rows' => 20,
'merchantCheck' => true,
'transAmount' => $amount
]);
if (!$response->ok()) {
return response()->json(['error' => '请求失败'], 500);
}
$ads = $response->json('data');
if (empty($ads)) {
return response()->json(['message' => '找不到符合条件的卖单']);
}
// 按照价格升序排列,取最便宜的那个
// $sorted = collect($ads)->sortBy(fn ($item) => (float) $item['adv']['price']);
$sorted = collect($ads)->sortByDesc(fn ($item) => (float) $item['adv']['price']);
$best = $sorted->first();
// dd($best);
return response()->json([
'price' => $best['adv']['price'],
'availableQuantity' => $best['adv']['surplusAmount'],
'minAmount' => $best['adv']['minSingleTransAmount'],
'maxAmount' => $best['adv']['maxSingleTransAmount'],
'nickName' => $best['advertiser']['nickName']
]);
}
public function savePrice()
{
$url = 'https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search';
// 1. 获取最高价(不限制金额)
$respAll = Http::post($url, [
'asset' => 'USDT',
'fiat' => 'LAK',
'tradeType' => 'SELL',
'page' => 1,
'rows' => 20,
'merchantCheck' => true
]);
// 2. 获取 200 万成交价
$resp200 = Http::post($url, [
'asset' => 'USDT',
'fiat' => 'LAK',
'tradeType' => 'SELL',
'page' => 1,
'rows' => 20,
'merchantCheck' => true,
'transAmount' => 2000000
]);
if (!$respAll->ok() || !$resp200->ok()) {
return response()->json(['error' => 'Binance 请求失败'], 500);
}
$adsAll = $respAll->json('data');
$ads200 = $resp200->json('data');
$highestPrice = collect($adsAll)->max(fn($item) => (float) $item['adv']['price']);
$priceFor200M = collect($ads200)->sortByDesc(fn($item) => (float) $item['adv']['price'])->first()['adv']['price'] ?? null;
// 写入一行资料
UsdtC2c::create([
'Highest' => $highestPrice,
'Million' => $priceFor200M,
]);
return response()->json([
'status' => '写入成功',
'Highest' => $highestPrice,
'Million' => $priceFor200M
]);
}
}

View File

@ -40,4 +40,5 @@ public function Msg($token,Request $request)
// }
}
}
}

12
app/Models/UsdtC2c.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UsdtC2c extends Model
{
protected $table = 'UsdtC2c';
public $timestamps = true; // 我们只用 created_at 字段
protected $fillable = ['Highest', 'Million', 'created_at'];
}

View File

@ -3,7 +3,9 @@
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Filament\Facades\Filament;
use Illuminate\Support\Facades\Schema;
class AppServiceProvider extends ServiceProvider
{
/**
@ -21,6 +23,13 @@ public function boot(): void
{
Schema::defaultStringLength(191); // 防止旧 MySQL 出现索引超长报错
Filament::serving(function () {
Filament::registerRenderHook(
'panels::head.end',
fn () => '<style>.filament-tables-text-column { user-select: text !important; }</style>'
);
});
// 强制所有表使用 utf8mb4_0900_ai_ci
// Schema::defaultCharset('utf8mb4');
// Schema::defaultCollation('utf8mb4_0900_ai_ci');

View File

@ -17,12 +17,22 @@
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Filament\Navigation\NavigationGroup;
use Filament\Navigation\NavigationItem;
use Filament\Navigation\NavigationBuilder;
use App\Filament\Pages\Dashboard;
use App\Filament\Widgets\AccountWidget;
use App\Filament\Widgets\FilamentInfoWidget;
class AdminPanelProvider extends PanelProvider
{
public function hasDashboard(): bool
{
return false;
}
public function panel(Panel $panel): Panel
{
return $panel
return Panel::make()
->default()
->id('admin')
->path('admin')
@ -33,13 +43,13 @@ public function panel(Panel $panel): Panel
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([
Pages\Dashboard::class,
Dashboard::class,
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([
Widgets\AccountWidget::class,
Widgets\FilamentInfoWidget::class,
])
// ->widgets([
// AccountWidget::class,
// FilamentInfoWidget::class,
// ])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
@ -55,4 +65,24 @@ public function panel(Panel $panel): Panel
Authenticate::class,
]);
}
public function navigation(NavigationBuilder $builder): NavigationBuilder
{
return $builder
->groups([
NavigationGroup::make('Telegram 管理') // 父级群组
->items([
NavigationItem::make('指令列表') // 子菜单项
->icon('heroicon-o-command-line')
->url(route('filament.admin.resources.tg-comm.index'))
->isActiveWhen(fn (): bool => request()->routeIs('filament.admin.resources.tg-comm.*')),
NavigationItem::make('其它功能')
->icon('heroicon-o-cog')
->url(route('filament.admin.pages.other-functions'))
->isActiveWhen(fn (): bool => request()->routeIs('filament.admin.pages.other-functions')),
]),
]);
}
}

View File

@ -14,6 +14,7 @@
"require-dev": {
"barryvdh/laravel-ide-helper": "^3.5",
"fakerphp/faker": "^1.23",
"laravel-lang/lang": "*",
"laravel/pail": "^1.2.2",
"laravel/pint": "^1.13",
"laravel/sail": "^1.41",

77
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a21399f8c690e2a2b52dd1d1007b110b",
"content-hash": "de41a71c12f31874afa042ac2754bd18",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@ -7942,6 +7942,81 @@
},
"time": "2025-04-29T18:09:42+00:00"
},
{
"name": "laravel-lang/lang",
"version": "12.17.1",
"source": {
"type": "git",
"url": "https://github.com/Laravel-Lang/lang.git",
"reference": "b07184103fb64131d514667b8fd1d74c71105409"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Laravel-Lang/lang/zipball/b07184103fb64131d514667b8fd1d74c71105409",
"reference": "b07184103fb64131d514667b8fd1d74c71105409",
"shasum": ""
},
"require": {
"ext-json": "*"
},
"conflict": {
"laravel-lang/publisher": "<14.0"
},
"require-dev": {
"laravel-lang/publisher": "^14.0",
"laravel-lang/status-generator": "^1.13",
"php": "^8.1",
"phpunit/phpunit": "^9.6",
"symfony/var-dumper": "^5.0 || ^6.0"
},
"suggest": {
"arcanedev/laravel-lang": "Translations manager and checker for Laravel",
"laravel-lang/attributes": "Translations for field names of the forms",
"laravel-lang/http-statuses": "Translations for HTTP statuses",
"laravel-lang/publisher": "Easy installation and update of translation files for your project"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"LaravelLang\\Lang\\ServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"LaravelLang\\Lang\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laravel-Lang Team",
"homepage": "https://github.com/Laravel-Lang"
}
],
"description": "Languages for Laravel",
"keywords": [
"lang",
"languages",
"laravel",
"lpm"
],
"support": {
"issues": "https://github.com/Laravel-Lang/lang/issues",
"source": "https://github.com/Laravel-Lang/lang"
},
"funding": [
{
"url": "https://opencollective.com/laravel-lang",
"type": "open_collective"
}
],
"time": "2023-02-19T13:40:37+00:00"
},
{
"name": "laravel/pail",
"version": "v1.2.2",

View File

@ -10,10 +10,10 @@ public function up(): void
Schema::create('tg_comm', function (Blueprint $table) {
$table->increments('id'); // PRIMARY KEY + AUTO_INCREMENT
$table->string('command')->nullable()->comment('指令名称');
$table->string('Instructions')->nullable()->comment('说明');
$table->string('instructions')->nullable()->comment('指令用途');
$table->string('action', )->nullable()->comment('对应动作指令');
$table->longText('memo')->nullable()->comment('使用说明');
$table->integer('dis_play')->default(1)->comment('指令列表 0不显示 1显示');
$table->integer('display')->default(1)->comment('指令列表 0不显示 1显示');
$table->integer('manager')->default(0)->comment('管理权限0需要1不需要');
$table->integer('valid')->default(1)->comment('0停用1启用');
$table->dateTime('created_at')->useCurrent()->nullable();

View File

@ -0,0 +1,24 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('UsdtC2c', function (Blueprint $table) {
$table->id();
$table->string('Currency')->nullable();
$table->decimal('Highest', 20, 8)->nullable();
$table->decimal('Million', 20, 8)->nullable();
$table->dateTime('created_at')->useCurrent()->nullable();
$table->dateTime('updated_at')->nullable()->useCurrentOnUpdate();
});
}
public function down(): void
{
Schema::dropIfExists('UsdtC2c');
}
};

View File

@ -8,16 +8,22 @@
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call(UsdtC2cSeeder::class);
}
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
// public function run(): void
// {
// // User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
}
// User::factory()->create([
// 'name' => 'Test User',
// 'email' => 'test@example.com',
// ]);
// }
}

View File

@ -0,0 +1,22 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\UsdtC2c;
class UsdtC2cSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
UsdtC2c::create([
'Currency' => 'LAK',
'Highest' => 27000.12345678,
'Million' => 270000000.12345678,
]);
}
}

20
lang/en/auth.php Normal file
View File

@ -0,0 +1,20 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'These credentials do not match our records.',
'password' => 'The provided password is incorrect.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
];

19
lang/en/pagination.php Normal file
View File

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; Previous',
'next' => 'Next &raquo;',
];

22
lang/en/passwords.php Normal file
View File

@ -0,0 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| outcome such as failure due to an invalid password / reset token.
|
*/
'reset' => 'Your password has been reset.',
'sent' => 'We have emailed your password reset link.',
'throttled' => 'Please wait before retrying.',
'token' => 'This password reset token is invalid.',
'user' => "We can't find a user with that email address.",
];

197
lang/en/validation.php Normal file
View File

@ -0,0 +1,197 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'The :attribute field must be accepted.',
'accepted_if' => 'The :attribute field must be accepted when :other is :value.',
'active_url' => 'The :attribute field must be a valid URL.',
'after' => 'The :attribute field must be a date after :date.',
'after_or_equal' => 'The :attribute field must be a date after or equal to :date.',
'alpha' => 'The :attribute field must only contain letters.',
'alpha_dash' => 'The :attribute field must only contain letters, numbers, dashes, and underscores.',
'alpha_num' => 'The :attribute field must only contain letters and numbers.',
'any_of' => 'The :attribute field is invalid.',
'array' => 'The :attribute field must be an array.',
'ascii' => 'The :attribute field must only contain single-byte alphanumeric characters and symbols.',
'before' => 'The :attribute field must be a date before :date.',
'before_or_equal' => 'The :attribute field must be a date before or equal to :date.',
'between' => [
'array' => 'The :attribute field must have between :min and :max items.',
'file' => 'The :attribute field must be between :min and :max kilobytes.',
'numeric' => 'The :attribute field must be between :min and :max.',
'string' => 'The :attribute field must be between :min and :max characters.',
],
'boolean' => 'The :attribute field must be true or false.',
'can' => 'The :attribute field contains an unauthorized value.',
'confirmed' => 'The :attribute field confirmation does not match.',
'contains' => 'The :attribute field is missing a required value.',
'current_password' => 'The password is incorrect.',
'date' => 'The :attribute field must be a valid date.',
'date_equals' => 'The :attribute field must be a date equal to :date.',
'date_format' => 'The :attribute field must match the format :format.',
'decimal' => 'The :attribute field must have :decimal decimal places.',
'declined' => 'The :attribute field must be declined.',
'declined_if' => 'The :attribute field must be declined when :other is :value.',
'different' => 'The :attribute field and :other must be different.',
'digits' => 'The :attribute field must be :digits digits.',
'digits_between' => 'The :attribute field must be between :min and :max digits.',
'dimensions' => 'The :attribute field has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'doesnt_end_with' => 'The :attribute field must not end with one of the following: :values.',
'doesnt_start_with' => 'The :attribute field must not start with one of the following: :values.',
'email' => 'The :attribute field must be a valid email address.',
'ends_with' => 'The :attribute field must end with one of the following: :values.',
'enum' => 'The selected :attribute is invalid.',
'exists' => 'The selected :attribute is invalid.',
'extensions' => 'The :attribute field must have one of the following extensions: :values.',
'file' => 'The :attribute field must be a file.',
'filled' => 'The :attribute field must have a value.',
'gt' => [
'array' => 'The :attribute field must have more than :value items.',
'file' => 'The :attribute field must be greater than :value kilobytes.',
'numeric' => 'The :attribute field must be greater than :value.',
'string' => 'The :attribute field must be greater than :value characters.',
],
'gte' => [
'array' => 'The :attribute field must have :value items or more.',
'file' => 'The :attribute field must be greater than or equal to :value kilobytes.',
'numeric' => 'The :attribute field must be greater than or equal to :value.',
'string' => 'The :attribute field must be greater than or equal to :value characters.',
],
'hex_color' => 'The :attribute field must be a valid hexadecimal color.',
'image' => 'The :attribute field must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field must exist in :other.',
'integer' => 'The :attribute field must be an integer.',
'ip' => 'The :attribute field must be a valid IP address.',
'ipv4' => 'The :attribute field must be a valid IPv4 address.',
'ipv6' => 'The :attribute field must be a valid IPv6 address.',
'json' => 'The :attribute field must be a valid JSON string.',
'list' => 'The :attribute field must be a list.',
'lowercase' => 'The :attribute field must be lowercase.',
'lt' => [
'array' => 'The :attribute field must have less than :value items.',
'file' => 'The :attribute field must be less than :value kilobytes.',
'numeric' => 'The :attribute field must be less than :value.',
'string' => 'The :attribute field must be less than :value characters.',
],
'lte' => [
'array' => 'The :attribute field must not have more than :value items.',
'file' => 'The :attribute field must be less than or equal to :value kilobytes.',
'numeric' => 'The :attribute field must be less than or equal to :value.',
'string' => 'The :attribute field must be less than or equal to :value characters.',
],
'mac_address' => 'The :attribute field must be a valid MAC address.',
'max' => [
'array' => 'The :attribute field must not have more than :max items.',
'file' => 'The :attribute field must not be greater than :max kilobytes.',
'numeric' => 'The :attribute field must not be greater than :max.',
'string' => 'The :attribute field must not be greater than :max characters.',
],
'max_digits' => 'The :attribute field must not have more than :max digits.',
'mimes' => 'The :attribute field must be a file of type: :values.',
'mimetypes' => 'The :attribute field must be a file of type: :values.',
'min' => [
'array' => 'The :attribute field must have at least :min items.',
'file' => 'The :attribute field must be at least :min kilobytes.',
'numeric' => 'The :attribute field must be at least :min.',
'string' => 'The :attribute field must be at least :min characters.',
],
'min_digits' => 'The :attribute field must have at least :min digits.',
'missing' => 'The :attribute field must be missing.',
'missing_if' => 'The :attribute field must be missing when :other is :value.',
'missing_unless' => 'The :attribute field must be missing unless :other is :value.',
'missing_with' => 'The :attribute field must be missing when :values is present.',
'missing_with_all' => 'The :attribute field must be missing when :values are present.',
'multiple_of' => 'The :attribute field must be a multiple of :value.',
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute field format is invalid.',
'numeric' => 'The :attribute field must be a number.',
'password' => [
'letters' => 'The :attribute field must contain at least one letter.',
'mixed' => 'The :attribute field must contain at least one uppercase and one lowercase letter.',
'numbers' => 'The :attribute field must contain at least one number.',
'symbols' => 'The :attribute field must contain at least one symbol.',
'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.',
],
'present' => 'The :attribute field must be present.',
'present_if' => 'The :attribute field must be present when :other is :value.',
'present_unless' => 'The :attribute field must be present unless :other is :value.',
'present_with' => 'The :attribute field must be present when :values is present.',
'present_with_all' => 'The :attribute field must be present when :values are present.',
'prohibited' => 'The :attribute field is prohibited.',
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
'prohibited_if_accepted' => 'The :attribute field is prohibited when :other is accepted.',
'prohibited_if_declined' => 'The :attribute field is prohibited when :other is declined.',
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
'prohibits' => 'The :attribute field prohibits :other from being present.',
'regex' => 'The :attribute field format is invalid.',
'required' => 'The :attribute field is required.',
'required_array_keys' => 'The :attribute field must contain entries for: :values.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_if_accepted' => 'The :attribute field is required when :other is accepted.',
'required_if_declined' => 'The :attribute field is required when :other is declined.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values are present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute field must match :other.',
'size' => [
'array' => 'The :attribute field must contain :size items.',
'file' => 'The :attribute field must be :size kilobytes.',
'numeric' => 'The :attribute field must be :size.',
'string' => 'The :attribute field must be :size characters.',
],
'starts_with' => 'The :attribute field must start with one of the following: :values.',
'string' => 'The :attribute field must be a string.',
'timezone' => 'The :attribute field must be a valid timezone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'uppercase' => 'The :attribute field must be uppercase.',
'url' => 'The :attribute field must be a valid URL.',
'ulid' => 'The :attribute field must be a valid ULID.',
'uuid' => 'The :attribute field must be a valid UUID.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap our attribute placeholder
| with something more reader friendly such as "E-Mail Address" instead
| of "email". This simply helps us make our message more expressive.
|
*/
'attributes' => [],
];

2418
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
<x-filament::page>
<h2 class="text-xl font-bold">歡迎使用 收支记账机器人 控制台</h2>
<p>這裡可以總覽系統狀態與快捷功能。</p>
</x-filament::page>

View File

@ -0,0 +1,3 @@
<x-filament-panels::page>
</x-filament-panels::page>

View File

@ -2,11 +2,17 @@
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TgController;
use App\Http\Controllers\BinanceController;
Route::get('/', function () {
return view('welcome');
});
Route::any('/{token}/Msg', [TgController::class, 'Msg']);
Route::get('/admin-panel', function () {return redirect('/admin');});
Route::get('/binance/usdt-lak-max', [BinanceController::class, 'getMaxUsdtLak']); //最高价
Route::get('/binance/usdt-lak-200', [BinanceController::class, 'getUsdtLakForAmount']); //200万
Route::get('/binance/save-price', [BinanceController::class, 'savePrice']); //最高价+200万写入到资料表