Initial commit

This commit is contained in:
xboard
2023-11-17 14:44:01 +08:00
commit 65fe7682ff
460 changed files with 63554 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
<?php
namespace App\Console\Commands;
use App\Models\CommissionLog;
use Illuminate\Console\Command;
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class CheckCommission extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:commission';
/**
* The console command description.
*
* @var string
*/
protected $description = '返佣服务';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->autoCheck();
$this->autoPayCommission();
}
public function autoCheck()
{
if ((int)admin_setting('commission_auto_check_enable', 1)) {
Order::where('commission_status', 0)
->where('invite_user_id', '!=', NULL)
->where('status', 3)
->where('updated_at', '<=', strtotime('-3 day', time()))
->update([
'commission_status' => 1
]);
}
}
public function autoPayCommission()
{
$orders = Order::where('commission_status', 1)
->where('invite_user_id', '!=', NULL)
->get();
foreach ($orders as $order) {
DB::beginTransaction();
if (!$this->payHandle($order->invite_user_id, $order)) {
DB::rollBack();
continue;
}
$order->commission_status = 2;
if (!$order->save()) {
DB::rollBack();
continue;
}
DB::commit();
}
}
public function payHandle($inviteUserId, Order $order)
{
$level = 3;
if ((int)admin_setting('commission_distribution_enable', 0)) {
$commissionShareLevels = [
0 => (int)admin_setting('commission_distribution_l1'),
1 => (int)admin_setting('commission_distribution_l2'),
2 => (int)admin_setting('commission_distribution_l3')
];
} else {
$commissionShareLevels = [
0 => 100
];
}
for ($l = 0; $l < $level; $l++) {
$inviter = User::find($inviteUserId);
if (!$inviter) continue;
if (!isset($commissionShareLevels[$l])) continue;
$commissionBalance = $order->commission_balance * ($commissionShareLevels[$l] / 100);
if (!$commissionBalance) continue;
if ((int)admin_setting('withdraw_close_enable', 0)) {
$inviter->balance = $inviter->balance + $commissionBalance;
} else {
$inviter->commission_balance = $inviter->commission_balance + $commissionBalance;
}
if (!$inviter->save()) {
DB::rollBack();
return false;
}
if (!CommissionLog::create([
'invite_user_id' => $inviteUserId,
'user_id' => $order->user_id,
'trade_no' => $order->trade_no,
'order_amount' => $order->total_amount,
'get_amount' => $commissionBalance
])) {
DB::rollBack();
return false;
}
$inviteUserId = $inviter->invite_user_id;
// update order actual commission balance
$order->actual_commission_balance = $order->actual_commission_balance + $commissionBalance;
}
return true;
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Console\Commands;
use App\Jobs\OrderHandleJob;
use App\Services\OrderService;
use Illuminate\Console\Command;
use App\Models\Order;
use App\Models\User;
use App\Models\Plan;
use Illuminate\Support\Facades\DB;
class CheckOrder extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:order';
/**
* The console command description.
*
* @var string
*/
protected $description = '订单检查任务';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
ini_set('memory_limit', -1);
$orders = Order::whereIn('status', [0, 1])
->orderBy('created_at', 'ASC')
->get();
foreach ($orders as $order) {
OrderHandleJob::dispatch($order->trade_no);
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Console\Commands;
use App\Services\ServerService;
use App\Services\TelegramService;
use App\Utils\CacheKey;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
class CheckServer extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:server';
/**
* The console command description.
*
* @var string
*/
protected $description = '节点检查任务';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->checkOffline();
}
private function checkOffline()
{
$serverService = new ServerService();
$servers = $serverService->getAllServers();
foreach ($servers as $server) {
if ($server['parent_id']) continue;
if ($server['last_check_at'] && (time() - $server['last_check_at']) > 1800) {
$telegramService = new TelegramService();
$message = sprintf(
"节点掉线通知\r\n----\r\n节点名称:%s\r\n节点地址:%s\r\n",
$server['name'],
$server['host']
);
$telegramService->sendMessageWithAdmin($message);
Cache::forget(CacheKey::get(sprintf("SERVER_%s_LAST_CHECK_AT", strtoupper($server['type'])), $server->id));
}
}
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Console\Commands;
use App\Models\Ticket;
use Illuminate\Console\Command;
class CheckTicket extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'check:ticket';
/**
* The console command description.
*
* @var string
*/
protected $description = '工单检查任务';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
ini_set('memory_limit', -1);
$tickets = Ticket::where('status', 0)
->where('updated_at', '<=', time() - 24 * 3600)
->where('reply_status', 0)
->get();
foreach ($tickets as $ticket) {
if ($ticket->user_id === $ticket->last_reply_user_id) continue;
$ticket->status = 1;
$ticket->save();
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Console\Commands;
use App\Models\Ticket;
use App\Models\User;
use Illuminate\Console\Command;
class ClearUser extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'clear:user';
/**
* The console command description.
*
* @var string
*/
protected $description = '清理用户';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$builder = User::where('plan_id', NULL)
->where('transfer_enable', 0)
->where('expired_at', 0)
->where('last_login_at', NULL);
$count = $builder->count();
if ($builder->delete()) {
$this->info("已删除${count}位没有任何数据的用户");
}
}
}

View File

@@ -0,0 +1,181 @@
<?php
namespace App\Console\Commands;
use App\Models\Setting;
use Illuminate\Console\Command;
class MigrateFromV2b extends Command
{
protected $signature = 'migrateFromV2b {version?}';
protected $description = '供不同版本V2b迁移到本项目的脚本';
public function handle()
{
$version = $this->argument('version');
if($version === 'config'){
$this->MigrateV2ConfigToV2Settings();
return;
}
// Define your SQL commands based on versions
$sqlCommands = [
'dev231027' => [
// SQL commands for version Dev 2023/10/27
'ALTER TABLE v2_order ADD COLUMN surplus_order_ids TEXT NULL;',
'ALTER TABLE v2_plan DROP COLUMN daily_unit_price, DROP COLUMN transfer_unit_price;',
'ALTER TABLE v2_server_hysteria DROP COLUMN ignore_client_bandwidth, DROP COLUMN obfs_type;'
],
'1.7.4' => [
'CREATE TABLE `v2_server_vless` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`group_id` TEXT NOT NULL,
`route_id` TEXT NULL,
`name` VARCHAR(255) NOT NULL,
`parent_id` INT NULL,
`host` VARCHAR(255) NOT NULL,
`port` INT NOT NULL,
`server_port` INT NOT NULL,
`tls` BOOLEAN NOT NULL,
`tls_settings` TEXT NULL,
`flow` VARCHAR(64) NULL,
`network` VARCHAR(11) NOT NULL,
`network_settings` TEXT NULL,
`tags` TEXT NULL,
`rate` VARCHAR(11) NOT NULL,
`show` BOOLEAN DEFAULT 0,
`sort` INT NULL,
`created_at` INT NOT NULL,
`updated_at` INT NOT NULL
);'
],
'1.7.3' => [
'ALTER TABLE `v2_stat_order` RENAME TO `v2_stat`;',
"ALTER TABLE `v2_stat` CHANGE COLUMN order_amount order_total INT COMMENT '订单合计';",
"ALTER TABLE `v2_stat` CHANGE COLUMN commission_amount commission_total INT COMMENT '佣金合计';",
"ALTER TABLE `v2_stat`
ADD COLUMN paid_count INT NULL,
ADD COLUMN paid_total INT NULL,
ADD COLUMN register_count INT NULL,
ADD COLUMN invite_count INT NULL,
ADD COLUMN transfer_used_total VARCHAR(32) NULL;
",
"CREATE TABLE `v2_log` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`title` TEXT NOT NULL,
`level` VARCHAR(11) NULL,
`host` VARCHAR(255) NULL,
`uri` VARCHAR(255) NOT NULL,
`method` VARCHAR(11) NOT NULL,
`data` TEXT NULL,
`ip` VARCHAR(128) NULL,
`context` TEXT NULL,
`created_at` INT NOT NULL,
`updated_at` INT NOT NULL
);",
'CREATE TABLE `v2_server_hysteria` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`group_id` VARCHAR(255) NOT NULL,
`route_id` VARCHAR(255) NULL,
`name` VARCHAR(255) NOT NULL,
`parent_id` INT NULL,
`host` VARCHAR(255) NOT NULL,
`port` VARCHAR(11) NOT NULL,
`server_port` INT NOT NULL,
`tags` VARCHAR(255) NULL,
`rate` VARCHAR(11) NOT NULL,
`show` BOOLEAN DEFAULT FALSE,
`sort` INT NULL,
`up_mbps` INT NOT NULL,
`down_mbps` INT NOT NULL,
`server_name` VARCHAR(64) NULL,
`insecure` BOOLEAN DEFAULT FALSE,
`created_at` INT NOT NULL,
`updated_at` INT NOT NULL
);',
"CREATE TABLE `v2_server_vless` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`group_id` TEXT NOT NULL,
`route_id` TEXT NULL,
`name` VARCHAR(255) NOT NULL,
`parent_id` INT NULL,
`host` VARCHAR(255) NOT NULL,
`port` INT NOT NULL,
`server_port` INT NOT NULL,
`tls` BOOLEAN NOT NULL,
`tls_settings` TEXT NULL,
`flow` VARCHAR(64) NULL,
`network` VARCHAR(11) NOT NULL,
`network_settings` TEXT NULL,
`tags` TEXT NULL,
`rate` VARCHAR(11) NOT NULL,
`show` BOOLEAN DEFAULT FALSE,
`sort` INT NULL,
`created_at` INT NOT NULL,
`updated_at` INT NOT NULL
);",
],
'wyx2685' => [
"ALTER TABLE `v2_plan` DROP COLUMN `device_limit`;",
"ALTER TABLE `v2_server_hysteria` DROP COLUMN `version`, DROP COLUMN `obfs`, DROP COLUMN `obfs_password`;",
"ALTER TABLE `v2_server_trojan` DROP COLUMN `network`, DROP COLUMN `network_settings`;",
"ALTER TABLE `v2_user` DROP COLUMN `device_limit`;"
]
];
if (!$version) {
$version = $this->choice('请选择你迁移前的V2board版本:', array_keys($sqlCommands));
}
if (array_key_exists($version, $sqlCommands)) {
try {
foreach ($sqlCommands[$version] as $sqlCommand) {
// Execute SQL command
\DB::statement($sqlCommand);
}
$this->info('1⃣、数据库差异矫正成功');
// 初始化数据库迁移
$this->call('db:seed', ['--class' => 'OriginV2bMigrationsTableSeeder']);
$this->info('2⃣、数据库迁移记录初始化成功');
$this->call('xboard:update');
$this->info('3⃣、更新成功');
$this->info("🎉:成功从 $version 迁移到Xboard");
} catch (\Exception $e) {
// An error occurred, rollback the transaction
$this->error('迁移失败'. $e->getMessage() );
}
} else {
$this->error("你所输入的版本未找到");
}
}
public function MigrateV2ConfigToV2Settings()
{
$configValue = config('v2board') ?? [];
foreach ($configValue as $k => $v) {
// 检查记录是否已存在
$existingSetting = Setting::where('name', $k)->first();
// 如果记录不存在,则插入
if ($existingSetting) {
$this->warn("配置 ${k} 在数据库已经存在, 忽略");
continue;
}
Setting::create([
'name' => $k,
'value' => $v,
]);
$this->info("配置 ${k} 迁移成功");
}
$this->info('所有配置迁移完成');
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Console\Commands;
use App\Models\Log;
use App\Models\Plan;
use App\Models\StatServer;
use App\Models\StatUser;
use App\Utils\Helper;
use Illuminate\Console\Command;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class ResetLog extends Command
{
protected $builder;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'reset:log';
/**
* The console command description.
*
* @var string
*/
protected $description = '清空日志';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
StatUser::where('record_at', '<', strtotime('-2 month', time()))->delete();
StatServer::where('record_at', '<', strtotime('-2 month', time()))->delete();
Log::where('created_at', '<', strtotime('-1 month', time()))->delete();
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Console\Commands;
use App\Models\Plan;
use App\Utils\Helper;
use Illuminate\Console\Command;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class ResetPassword extends Command
{
protected $builder;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'reset:password {email}';
/**
* The console command description.
*
* @var string
*/
protected $description = '重置用户密码';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$user = User::where('email', $this->argument('email'))->first();
if (!$user) abort(500, '邮箱不存在');
$password = Helper::guid(false);
$user->password = password_hash($password, PASSWORD_DEFAULT);
$user->password_algo = null;
if (!$user->save()) abort(500, '重置失败');
$this->info("!!!重置成功!!!");
$this->info("新密码为:{$password},请尽快修改密码。");
}
}

View File

@@ -0,0 +1,164 @@
<?php
namespace App\Console\Commands;
use App\Models\Plan;
use Illuminate\Console\Command;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class ResetTraffic extends Command
{
protected $builder;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'reset:traffic';
/**
* The console command description.
*
* @var string
*/
protected $description = '流量清空';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->builder = User::where('expired_at', '!=', NULL)
->where('expired_at', '>', time());
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
ini_set('memory_limit', -1);
$resetMethods = Plan::select(
DB::raw("GROUP_CONCAT(`id`) as plan_ids"),
DB::raw("reset_traffic_method as method")
)
->groupBy('reset_traffic_method')
->get()
->toArray();
foreach ($resetMethods as $resetMethod) {
$planIds = explode(',', $resetMethod['plan_ids']);
switch (true) {
case ($resetMethod['method'] === NULL): {
$resetTrafficMethod = admin_setting('reset_traffic_method', 0);
$builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
switch ((int)$resetTrafficMethod) {
// month first day
case 0:
$this->resetByMonthFirstDay($builder);
break;
// expire day
case 1:
$this->resetByExpireDay($builder);
break;
// no action
case 2:
break;
// year first day
case 3:
$this->resetByYearFirstDay($builder);
// year expire day
case 4:
$this->resetByExpireYear($builder);
}
break;
}
case ($resetMethod['method'] === 0): {
$builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
$this->resetByMonthFirstDay($builder);
break;
}
case ($resetMethod['method'] === 1): {
$builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
$this->resetByExpireDay($builder);
break;
}
case ($resetMethod['method'] === 2): {
break;
}
case ($resetMethod['method'] === 3): {
$builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
$this->resetByYearFirstDay($builder);
break;
}
case ($resetMethod['method'] === 4): {
$builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
$this->resetByExpireYear($builder);
break;
}
}
}
}
private function resetByExpireYear($builder):void
{
$users = [];
foreach ($builder->get() as $item) {
$expireDay = date('m-d', $item->expired_at);
$today = date('m-d');
if ($expireDay === $today) {
array_push($users, $item->id);
}
}
User::whereIn('id', $users)->update([
'u' => 0,
'd' => 0
]);
}
private function resetByYearFirstDay($builder):void
{
if ((string)date('md') === '0101') {
$builder->update([
'u' => 0,
'd' => 0
]);
}
}
private function resetByMonthFirstDay($builder):void
{
if ((string)date('d') === '01') {
$builder->update([
'u' => 0,
'd' => 0
]);
}
}
private function resetByExpireDay($builder):void
{
$lastDay = date('d', strtotime('last day of +0 months'));
$users = [];
foreach ($builder->get() as $item) {
$expireDay = date('d', $item->expired_at);
$today = date('d');
if ($expireDay === $today) {
array_push($users, $item->id);
}
if (($today === $lastDay) && $expireDay >= $lastDay) {
array_push($users, $item->id);
}
}
User::whereIn('id', $users)->update([
'u' => 0,
'd' => 0
]);
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Console\Commands;
use App\Models\Plan;
use App\Utils\Helper;
use Illuminate\Console\Command;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class ResetUser extends Command
{
protected $builder;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'reset:user';
/**
* The console command description.
*
* @var string
*/
protected $description = '重置所有用户信息';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if (!$this->confirm("确定要重置所有用户安全信息吗?")) {
return;
}
ini_set('memory_limit', -1);
$users = User::all();
foreach ($users as $user)
{
$user->token = Helper::guid();
$user->uuid = Helper::guid(true);
$user->save();
$this->info("已重置用户{$user->email}的安全信息");
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Console\Commands;
use App\Services\MailService;
use Illuminate\Console\Command;
use App\Models\User;
use App\Models\MailLog;
use App\Jobs\SendEmailJob;
class SendRemindMail extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'send:remindMail';
/**
* The console command description.
*
* @var string
*/
protected $description = '发送提醒邮件';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$users = User::all();
$mailService = new MailService();
foreach ($users as $user) {
if ($user->remind_expire) $mailService->remindExpire($user);
if ($user->remind_traffic) $mailService->remindTraffic($user);
}
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class Test extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'test';
/**
* The console command description.
*
* @var string
*/
protected $description = '';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
}
}

View File

@@ -0,0 +1,192 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Encryption\Encrypter;
use App\Models\User;
use App\Utils\Helper;
use Illuminate\Support\Env;
use Illuminate\Support\Facades\DB;
class XboardInstall extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'xboard:install';
/**
* The console command description.
*
* @var string
*/
protected $description = 'xboard 初始化安装';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
try {
\Artisan::call('config:clear');
$this->info("__ __ ____ _ ");
$this->info("\ \ / /| __ ) ___ __ _ _ __ __| | ");
$this->info(" \ \/ / | __ \ / _ \ / _` | '__/ _` | ");
$this->info(" / /\ \ | |_) | (_) | (_| | | | (_| | ");
$this->info("/_/ \_\|____/ \___/ \__,_|_| \__,_| ");
if (\File::exists(base_path() . '/.env') && $this->getEnvValue('INSTALLED')) {
$securePath = admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key'))));
$this->info("访问 http(s)://你的站点/{$securePath} 进入管理面板,你可以在用户中心修改你的密码。");
abort(500, '如需重新安装请清空目录下 .env 文件的内容Docker安装方式不可以删除此文件');
\Artisan::call('config:cache');
}
// 选择是否使用Sqlite
$isSqlite = $this->ask('是否启用Sqlite代替Mysql(默认不启动 y/n)','n');
if( $isSqlite == 'y' ) {
$sqliteFile = '.docker/.data/database.sqlite';
if (!file_exists(base_path($sqliteFile))) {
// 创建空文件
if (touch(base_path($sqliteFile))) {
echo "sqlite创建成功: $sqliteFile";
} else {
echo "sqlite创建成功";
}
}
$envConfig = [
'APP_KEY' => 'base64:' . base64_encode(Encrypter::generateKey('AES-256-CBC')),
'DB_CONNECTION' => 'sqlite',
'DB_DATABASE' => $sqliteFile,
'DB_HOST' => '',
'DB_USERNAME' => '',
'DB_PASSWORD' => '',
'REDIS_HOST' => $this->ask('请输入redis地址(默认: 127.0.0.1)', '127.0.0.1'),
'REDIS_PORT'=> $this->ask('请输入redis端口(默认: 6379)', '6379'),
'REDIS_PASSWORD' => $this->ask('请输入redis密码(默认: null)', null),
'INSTALLED' => 'true'
];
if (!copy(base_path() . '/.env.example', base_path() . '/.env')) {
abort(500, '复制环境文件失败,请检查目录权限');
}
$this->saveToEnv($envConfig);
}else{
$envConfig = [
'APP_KEY' => 'base64:' . base64_encode(Encrypter::generateKey('AES-256-CBC')),
'DB_CONNECTION' => 'mysql',
'DB_HOST' => $this->ask('请输入数据库地址(默认:127.0.0.1)', '127.0.0.1'),
'DB_PORT' => $this->ask('请输入数据库端口(默认:3306)', '3306'),
'DB_DATABASE' => $this->ask('请输入数据库名', 'xboard'),
'DB_USERNAME' => $this->ask('请输入数据库用户名'),
'DB_PASSWORD' => $this->ask('请输入数据库密码'),
'REDIS_HOST' => $this->ask('请输入redis地址(默认: 127.0.0.1)', '127.0.0.1'),
'REDIS_PORT'=> $this->ask('请输入redis端口(默认: 6379)', '6379'),
'REDIS_PASSWORD' => $this->ask('请输入redis密码(默认: null)', null),
'INSTALLED' => 'true'
];
if (!copy(base_path() . '/.env.example', base_path() . '/.env')) {
abort(500, '复制环境文件失败,请检查目录权限');
}
$this->saveToEnv($envConfig);
}
\Artisan::call('config:clear');
\Artisan::call('config:cache');
\Artisan::call('cache:clear');
$this->info('正在清空数据库请稍等');
\Artisan::call('db:wipe');
$this->info('数据库清空完成');
$this->info('正在导入数据库请稍等...');
\Artisan::call("migrate");
$this->info(\Artisan::output());
$this->info('数据库导入完成');
$email = '';
while (!$email) {
$email = $this->ask('请输入管理员邮箱?');
}
$password = Helper::guid(false);
if (!$this->registerAdmin($email, $password)) {
abort(500, '管理员账号注册失败,请重试');
}
$this->info('一切就绪');
$this->info("管理员邮箱:{$email}");
$this->info("管理员密码:{$password}");
$defaultSecurePath = hash('crc32b', config('app.key'));
$this->info("访问 http(s)://你的站点/{$defaultSecurePath} 进入管理面板,你可以在用户中心修改你的密码。");
} catch (\Exception $e) {
$this->error($e->getMessage());
}
}
public function registerAdmin($email, $password)
{
$user = new User();
$user->email = $email;
if (strlen($password) < 8) {
abort(500, '管理员密码长度最小为8位字符');
}
$user->password = password_hash($password, PASSWORD_DEFAULT);
$user->uuid = Helper::guid(true);
$user->token = Helper::guid();
$user->is_admin = 1;
return $user->save();
}
private function saveToEnv($data = [])
{
function set_env_var($key, $value)
{
if (! is_bool(strpos($value, ' '))) {
$value = '"' . $value . '"';
}
$key = strtoupper($key);
$envPath = app()->environmentFilePath();
$contents = file_get_contents($envPath);
preg_match("/^{$key}=[^\r\n]*/m", $contents, $matches);
$oldValue = count($matches) ? $matches[0] : '';
if ($oldValue) {
$contents = str_replace("{$oldValue}", "{$key}={$value}", $contents);
} else {
$contents = $contents . "\n{$key}={$value}\n";
}
$file = fopen($envPath, 'w');
fwrite($file, $contents);
return fclose($file);
}
foreach($data as $key => $value) {
set_env_var($key, $value);
}
return true;
}
function getEnvValue($key, $default = null)
{
$dotenv = \Dotenv\Dotenv::createImmutable(base_path());
$dotenv->load();
return Env::get($key, $default);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class XboardRollback extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'xboard:rollback';
/**
* The console command description.
*
* @var string
*/
protected $description = 'xboard 回滚';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('正在回滚数据库请稍等...');
\Artisan::call("migrate:rollback");
$this->info(\Artisan::output());
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace App\Console\Commands;
use App\Models\StatServer;
use App\Models\StatUser;
use App\Services\StatisticalService;
use Illuminate\Console\Command;
use App\Models\Stat;
use Illuminate\Support\Facades\DB;
class XboardStatistics extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'xboard:statistics';
/**
* The console command description.
*
* @var string
*/
protected $description = '统计任务';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$startAt = microtime(true);
ini_set('memory_limit', -1);
$this->statUser();
$this->statServer();
$this->stat();
info('统计任务执行完毕。耗时:' . (microtime(true) - $startAt) / 1000);
}
private function statServer()
{
try {
DB::beginTransaction();
$createdAt = time();
$recordAt = strtotime('-1 day', strtotime(date('Y-m-d')));
$statService = new StatisticalService();
$statService->setStartAt($recordAt);
$statService->setServerStats();
$stats = $statService->getStatServer();
foreach ($stats as $stat) {
if (!StatServer::insert([
'server_id' => $stat['server_id'],
'server_type' => $stat['server_type'],
'u' => $stat['u'],
'd' => $stat['d'],
'created_at' => $createdAt,
'updated_at' => $createdAt,
'record_type' => 'd',
'record_at' => $recordAt
])) {
throw new \Exception('stat server fail');
}
}
DB::commit();
$statService->clearStatServer();
} catch (\Exception $e) {
DB::rollback();
\Log::error($e->getMessage(), ['exception' => $e]);
}
}
private function statUser()
{
try {
DB::beginTransaction();
$createdAt = time();
$recordAt = strtotime('-1 day', strtotime(date('Y-m-d')));
$statService = new StatisticalService();
$statService->setStartAt($recordAt);
$statService->setUserStats();
$stats = $statService->getStatUser();
foreach ($stats as $stat) {
if (!StatUser::insert([
'user_id' => $stat['user_id'],
'u' => $stat['u'],
'd' => $stat['d'],
'server_rate' => $stat['server_rate'],
'created_at' => $createdAt,
'updated_at' => $createdAt,
'record_type' => 'd',
'record_at' => $recordAt
])) {
throw new \Exception('stat user fail');
}
}
DB::commit();
$statService->clearStatUser();
} catch (\Exception $e) {
DB::rollback();
\Log::error($e->getMessage(), ['exception' => $e]);
}
}
private function stat()
{
try {
$endAt = strtotime(date('Y-m-d'));
$startAt = strtotime('-1 day', $endAt);
$statisticalService = new StatisticalService();
$statisticalService->setStartAt($startAt);
$statisticalService->setEndAt($endAt);
$data = $statisticalService->generateStatData();
$data['record_at'] = $startAt;
$data['record_type'] = 'd';
$statistic = Stat::where('record_at', $startAt)
->where('record_type', 'd')
->first();
if ($statistic) {
$statistic->update($data);
return;
}
Stat::create($data);
} catch (\Exception $e) {
\Log::error($e->getMessage(), ['exception' => $e]);
}
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class XboardUpdate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'xboard:update';
/**
* The console command description.
*
* @var string
*/
protected $description = 'xboard 更新';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('正在导入数据库请稍等...');
\Artisan::call("migrate");
$this->info(\Artisan::output());
\Artisan::call('horizon:terminate');
$this->info('更新完毕,队列服务已重启,你无需进行任何操作。');
}
}

56
app/Console/Kernel.php Normal file
View File

@@ -0,0 +1,56 @@
<?php
namespace App\Console;
use App\Utils\CacheKey;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Cache;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
Cache::put(CacheKey::get('SCHEDULE_LAST_CHECK_AT', null), time());
// v2board
$schedule->command('xboard:statistics')->dailyAt('0:10');
// check
$schedule->command('check:order')->everyMinute();
$schedule->command('check:commission')->everyMinute();
$schedule->command('check:ticket')->everyMinute();
// reset
$schedule->command('reset:traffic')->daily();
$schedule->command('reset:log')->daily();
// send
$schedule->command('send:remindMail')->dailyAt('11:30');
// horizon metrics
$schedule->command('horizon:snapshot')->everyFiveMinutes();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}
}