diff --git a/app/Console/Commands/CheckExpire.php b/app/Console/Commands/CheckExpire.php deleted file mode 100644 index 5124efee..00000000 --- a/app/Console/Commands/CheckExpire.php +++ /dev/null @@ -1,53 +0,0 @@ -expired_at < time() || $user->u + $user->d >= $user->transfer_enable) { - $user->enable = 0; - } else { - $user->enable = 1; - } - $user->save(); - } - } - -} diff --git a/app/Console/Commands/CheckOrder.php b/app/Console/Commands/CheckOrder.php index 7f7d9e48..b1b237e7 100755 --- a/app/Console/Commands/CheckOrder.php +++ b/app/Console/Commands/CheckOrder.php @@ -60,15 +60,18 @@ class CheckOrder extends Command } } - private function orderHandle($order) + private function orderHandle(Order $order) { $user = User::find($order->user_id); - return $this->buy($order, $user); + $plan = Plan::find($order->plan_id); + if ($order->cycle === 'onetime_price') { + return $this->buyByOneTime($order, $user, $plan); + } + return $this->buyByCycle($order, $user, $plan); } - private function buy($order, $user) + private function buyByCycle(Order $order, User $user, Plan $plan) { - $plan = Plan::find($order->plan_id); // change plan process if ($order->type == 3) { $user->expired_at = time(); @@ -77,12 +80,30 @@ class CheckOrder extends Command $user->balance = $user->balance + $order->refund_amount; } $user->transfer_enable = $plan->transfer_enable * 1073741824; - $user->enable = 1; + if ((int)config('v2board.renew_reset_traffic_enable', 1)) { + $user->u = 0; + $user->d = 0; + } + $user->plan_id = $plan->id; + $user->group_id = $plan->group_id; + $user->expired_at = $this->getTime($order->cycle, $user->expired_at); + if ($user->save()) { + $order->status = 3; + $order->save(); + } + } + + private function buyByOneTime(Order $order, User $user, Plan $plan) + { + if ($order->refund_amount) { + $user->balance = $user->balance + $order->refund_amount; + } + $user->transfer_enable = $plan->transfer_enable * 1073741824; $user->u = 0; $user->d = 0; $user->plan_id = $plan->id; $user->group_id = $plan->group_id; - $user->expired_at = $this->getTime($order->cycle, $user->expired_at); + $user->expired_at = NULL; if ($user->save()) { $order->status = 3; $order->save(); diff --git a/app/Console/Commands/ResetTraffic.php b/app/Console/Commands/ResetTraffic.php index f534ef21..f864fc6b 100644 --- a/app/Console/Commands/ResetTraffic.php +++ b/app/Console/Commands/ResetTraffic.php @@ -3,7 +3,7 @@ namespace App\Console\Commands; use Illuminate\Console\Command; -use Illuminate\Support\Facades\DB; +use App\Models\User; class ResetTraffic extends Command { @@ -38,9 +38,45 @@ class ResetTraffic extends Command */ public function handle() { - DB::table('v2_user')->update([ + $user = User::where('expired_at', '!=', NULL); + $resetTrafficMethod = config('v2board.reset_traffic_method', 0); + switch ((int)$resetTrafficMethod) { + // 1 a month + case 0: + $this->resetByMonthFirstDay($user); + break; + // expire day + case 1: + $this->resetByExpireDay($user); + break; + } + } + + private function resetByMonthFirstDay(User $user):void + { + $user->update([ 'u' => 0, 'd' => 0 ]); } + + private function resetByExpireDay(User $user):void + { + $date = date('Y-m-d', time()); + $startAt = strtotime((string)$date); + $endAt = (int)$startAt + 24 * 3600; + $lastDay = date('d', strtotime('last day of +0 months')); + if ((string)$lastDay === '29') { + $endAt = (int)$startAt + 72 * 3600; + } + if ((string)$lastDay === '30') { + $endAt = (int)$startAt + 48 * 3600; + } + $user->where('expired_at', '>=', (int)$startAt) + ->where('expired_at', '<', (int)$endAt) + ->update([ + 'u' => 0, + 'd' => 0 + ]); + } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index aaacdc04..b6afc47a 100755 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -28,10 +28,9 @@ class Kernel extends ConsoleKernel $schedule->command('v2board:cache')->hourly(); // check $schedule->command('check:order')->everyMinute(); - $schedule->command('check:expire')->everyMinute(); $schedule->command('check:commission')->everyMinute(); // reset - $schedule->command('reset:traffic')->monthly(); + $schedule->command('reset:traffic')->daily(); $schedule->command('reset:serverLog')->monthly(); // send $schedule->command('send:remindMail')->dailyAt('11:30'); diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 324a050a..2c973d14 100755 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -46,6 +46,9 @@ class Handler extends ExceptionHandler */ public function render($request, Exception $exception) { + if($exception instanceof \Illuminate\Http\Exceptions\ThrottleRequestsException) { + abort(429, '请求频繁,请稍后再试'); + } return parent::render($request, $exception); } diff --git a/app/Http/Controllers/Admin/ConfigController.php b/app/Http/Controllers/Admin/ConfigController.php index 3edd0996..1aa3ef85 100755 --- a/app/Http/Controllers/Admin/ConfigController.php +++ b/app/Http/Controllers/Admin/ConfigController.php @@ -28,12 +28,16 @@ class ConfigController extends Controller 'app_description' => config('v2board.app_description', 'V2Board is best!'), 'app_url' => config('v2board.app_url'), 'subscribe_url' => config('v2board.subscribe_url'), - 'plan_change_enable' => (int)config('v2board.plan_change_enable', 1), 'try_out_plan_id' => (int)config('v2board.try_out_plan_id', 0), 'try_out_hour' => (int)config('v2board.try_out_hour', 1), 'email_whitelist_enable' => (int)config('v2board.email_whitelist_enable', 0), 'email_whitelist_suffix' => config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT) ], + 'subscribe' => [ + 'plan_change_enable' => (int)config('v2board.plan_change_enable', 1), + 'reset_traffic_method' => (int)config('v2board.reset_traffic_method', 0), + 'renew_reset_traffic_enable' => (int)config('v2board.renew_reset_traffic_enable', 1) + ], 'pay' => [ // alipay 'alipay_enable' => (int)config('v2board.alipay_enable'), @@ -46,6 +50,7 @@ class ConfigController extends Controller 'stripe_alipay_enable' => (int)config('v2board.stripe_alipay_enable'), 'stripe_wepay_enable' => (int)config('v2board.stripe_wepay_enable'), 'stripe_webhook_key' => config('v2board.stripe_webhook_key'), + 'stripe_currency' => config('v2board.stripe_currency', 'hkd'), // bitpayx 'bitpayx_enable' => config('v2board.bitpayx_enable'), 'bitpayx_appsecret' => config('v2board.bitpayx_appsecret'), @@ -76,7 +81,7 @@ class ConfigController extends Controller $data = $request->input(); $array = \Config::get('v2board'); foreach ($data as $k => $v) { - if (!in_array($k, ConfigSave::filter())) { + if (!in_array($k, array_keys(ConfigSave::RULES))) { abort(500, '参数' . $k . '不在规则内,禁止修改'); } $array[$k] = $v; diff --git a/app/Http/Controllers/Admin/MailController.php b/app/Http/Controllers/Admin/MailController.php index f72b0433..3383d6fe 100644 --- a/app/Http/Controllers/Admin/MailController.php +++ b/app/Http/Controllers/Admin/MailController.php @@ -3,24 +3,27 @@ namespace App\Http\Controllers\Admin; use App\Http\Requests\Admin\MailSend; +use App\Services\UserService; use Illuminate\Http\Request; use App\Http\Controllers\Controller; -use App\Models\User; use App\Jobs\SendEmail; class MailController extends Controller { public function send(MailSend $request) { - + $userService = new UserService(); + $users = []; switch ($request->input('type')) { - case 1: $users = $this->getAllUser(); + case 1: $users = $userService->getAllUsers(); break; - case 2: $users = $this->getReceiver($request->input('receiver')); + case 2: $users = $userService->getUsersByIds($request->input('receiver')); break; - case 3: $users = $this->getSubscribeUser(); + // available users + case 3: $users = $userService->getAvailableUsers(); break; - case 4: $users = $this->getExpireUser(); + // un available users + case 4: $users = $userService->getUnAvailbaleUsers(); break; } @@ -41,27 +44,4 @@ class MailController extends Controller 'data' => true ]); } - - private function getAllUser() - { - return User::all(); - } - - private function getReceiver($receiver) - { - if (empty($receiver)) { - abort(500, '收件人不能为空'); - } - return User::whereIn('id', $receiver)->get(); - } - - private function getSubscribeUser() - { - return User::where('expired_at', '=>', time())->get(); - } - - private function getExpireUser() - { - return User::where('expired_at', '<', time())->get(); - } } diff --git a/app/Http/Controllers/Admin/PlanController.php b/app/Http/Controllers/Admin/PlanController.php index c718e5c4..ea4dc974 100755 --- a/app/Http/Controllers/Admin/PlanController.php +++ b/app/Http/Controllers/Admin/PlanController.php @@ -9,6 +9,7 @@ use App\Http\Controllers\Controller; use App\Models\Plan; use App\Models\Order; use App\Models\User; +use Illuminate\Support\Facades\DB; class PlanController extends Controller { @@ -21,25 +22,36 @@ class PlanController extends Controller public function save(PlanSave $request) { + $params = $request->only(array_keys(PlanSave::RULES)); if ($request->input('id')) { $plan = Plan::find($request->input('id')); if (!$plan) { abort(500, '该订阅不存在'); } - } else { - $plan = new Plan(); + DB::beginTransaction(); + if (isset($params->group_id) && ($params->group_id !== $plan->group_id)) { + if (!User::where('plan_id', $plan->id) + ->get() + ->update(['group_id', $plan->group_id]) + ) { + DB::rollBack(); + abort(500, '保存失败'); + } + } + if (!$plan->update($params)) { + DB::rollBack(); + abort(500, '保存失败'); + } + DB::commit(); + return response([ + 'data' => true + ]); + } + if (!Plan::create($params)) { + abort(500, '创建失败'); } - $plan->name = $request->input('name'); - $plan->content = $request->input('content'); - $plan->transfer_enable = $request->input('transfer_enable'); - $plan->group_id = $request->input('group_id'); - $plan->month_price = $request->input('month_price'); - $plan->quarter_price = $request->input('quarter_price'); - $plan->half_year_price = $request->input('half_year_price'); - $plan->year_price = $request->input('year_price'); - return response([ - 'data' => $plan->save() + 'data' => true ]); } diff --git a/app/Http/Controllers/Admin/TutorialController.php b/app/Http/Controllers/Admin/TutorialController.php index bf86e318..2c9bd0f7 100644 --- a/app/Http/Controllers/Admin/TutorialController.php +++ b/app/Http/Controllers/Admin/TutorialController.php @@ -12,18 +12,13 @@ class TutorialController extends Controller public function fetch(Request $request) { return response([ - 'data' => Tutorial::all() + 'data' => Tutorial::get() ]); } public function save(TutorialSave $request) { - $params = $request->only([ - 'title', - 'description', - 'steps', - 'icon' - ]); + $params = $request->only(array_keys(TutorialSave::RULES)); if (!$request->input('id')) { if (!Tutorial::create($params)) { diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 4989fddc..82d53f75 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -59,41 +59,27 @@ class UserController extends Controller public function update(UserUpdate $request) { - $updateData = $request->only([ - 'email', - 'password', - 'transfer_enable', - 'expired_at', - 'banned', - 'plan_id', - 'commission_rate', - 'discount', - 'is_admin', - 'u', - 'd', - 'balance', - 'commission_balance' - ]); + $params = $request->only(array_keys(UserUpdate::RULES)); $user = User::find($request->input('id')); if (!$user) { abort(500, '用户不存在'); } - if (User::where('email', $updateData['email'])->first() && $user->email !== $updateData['email']) { + if (User::where('email', $params['email'])->first() && $user->email !== $params['email']) { abort(500, '邮箱已被使用'); } - if (isset($updateData['password'])) { - $updateData['password'] = password_hash($updateData['password'], PASSWORD_DEFAULT); + if (isset($params['password'])) { + $params['password'] = password_hash($params['password'], PASSWORD_DEFAULT); } else { - unset($updateData['password']); + unset($params['password']); } - if (isset($updateData['plan_id'])) { - $plan = Plan::find($updateData['plan_id']); + if (isset($params['plan_id'])) { + $plan = Plan::find($params['plan_id']); if (!$plan) { abort(500, '订阅计划不存在'); } - $updateData['group_id'] = $plan->group_id; + $params['group_id'] = $plan->group_id; } - if (!$user->update($updateData)) { + if (!$user->update($params)) { abort(500, '保存失败'); } return response([ diff --git a/app/Http/Controllers/Client/ClientController.php b/app/Http/Controllers/Client/ClientController.php index 07348b53..dcc6494a 100755 --- a/app/Http/Controllers/Client/ClientController.php +++ b/app/Http/Controllers/Client/ClientController.php @@ -3,10 +3,12 @@ namespace App\Http\Controllers\Client; use App\Http\Controllers\Controller; +use App\Http\Middleware\User; use Illuminate\Http\Request; use App\Models\Server; use App\Utils\Helper; use Symfony\Component\Yaml\Yaml; +use App\Services\UserService; class ClientController extends Controller { @@ -15,7 +17,8 @@ class ClientController extends Controller $user = $request->user; $server = []; // account not expired and is not banned. - if ($user->expired_at > time() && !$user->banned) { + $userService = new UserService(); + if ($userService->isAvailable($user)) { $servers = Server::where('show', 1) ->orderBy('name') ->get(); @@ -137,11 +140,17 @@ class ClientController extends Controller array_push($proxyGroup, [ 'name' => 'select', 'type' => 'select', - 'proxies' => $proxies + 'proxies' => array_merge($proxies, [ + 'auto', + 'fallback-auto' + ]) ]); try { - $rules = Yaml::parseFile(base_path() . '/resources/rules/clash.rule.yaml')['Rule']; + $rules = []; + foreach (glob(base_path() . '/resources/rules/' . '*.clash.yaml') as $file) { + $rules = array_merge($rules, Yaml::parseFile($file)['Rule']); + } } catch (\Exception $e) {} $config = [ diff --git a/app/Http/Controllers/Server/DeepbworkController.php b/app/Http/Controllers/Server/DeepbworkController.php index ad1b6c94..2d5b6c30 100644 --- a/app/Http/Controllers/Server/DeepbworkController.php +++ b/app/Http/Controllers/Server/DeepbworkController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Server; +use App\Services\ServerService; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Models\User; @@ -34,20 +35,8 @@ class DeepbworkController extends Controller abort(500, 'fail'); } Cache::put('server_last_check_at_' . $server->id, time()); - $users = User::whereIn('group_id', json_decode($server->group_id)) - ->select([ - 'id', - 'email', - 't', - 'u', - 'd', - 'transfer_enable', - 'enable', - 'v2ray_uuid', - 'v2ray_alter_id', - 'v2ray_level' - ]) - ->get(); + $serverService = new ServerService(); + $users = $serverService->getAvailableUsers(json_decode($server->group_id)); $result = []; foreach ($users as $user) { $user->v2ray_user = [ @@ -146,7 +135,7 @@ class DeepbworkController extends Controller if ($server->rules) { $rules = json_decode($server->rules); // domain - if (isset($rules->domain)) { + if (isset($rules->domain) && !empty($rules->domain)) { $domainObj = new \StdClass(); $domainObj->type = 'field'; $domainObj->domain = $rules->domain; @@ -154,7 +143,7 @@ class DeepbworkController extends Controller array_push($json->routing->rules, $domainObj); } // protocol - if (isset($rules->protocol)) { + if (isset($rules->protocol) && !empty($rules->protocol)) { $protocolObj = new \StdClass(); $protocolObj->type = 'field'; $protocolObj->protocol = $rules->protocol; diff --git a/app/Http/Controllers/Server/PoseidonController.php b/app/Http/Controllers/Server/PoseidonController.php index efb3d77d..3a0f7d3c 100644 --- a/app/Http/Controllers/Server/PoseidonController.php +++ b/app/Http/Controllers/Server/PoseidonController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Server; +use App\Services\ServerService; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Models\User; @@ -25,24 +26,9 @@ class PoseidonController extends Controller if (!$server) { return $this->error("server could not be found", 404); } - Cache::put('server_last_check_at_' . $server->id, time()); - $users = User::whereIn('group_id', json_decode($server->group_id)) - ->select([ - 'id', - 'email', - 't', - 'u', - 'd', - 'transfer_enable', - 'enable', - 'v2ray_uuid', - 'v2ray_alter_id', - 'v2ray_level' - ]) - ->whereRaw('u + d < transfer_enable') - ->where('enable', 1) - ->get(); + $serverService = new ServerService(); + $users = $serverService->getAvailableUsers(json_decode($server->group_id)); $result = []; foreach ($users as $user) { $user->v2ray_user = [ @@ -137,7 +123,7 @@ class PoseidonController extends Controller if ($server->rules) { $rules = json_decode($server->rules); // domain - if (isset($rules->domain)) { + if (isset($rules->domain) && !empty($rules->domain)) { $domainObj = new \StdClass(); $domainObj->type = 'field'; $domainObj->domain = $rules->domain; @@ -145,7 +131,7 @@ class PoseidonController extends Controller array_push($json->routing->rules, $domainObj); } // protocol - if (isset($rules->protocol)) { + if (isset($rules->protocol) && !empty($rules->protocol)) { $protocolObj = new \StdClass(); $protocolObj->type = 'field'; $protocolObj->protocol = $rules->protocol; diff --git a/app/Http/Controllers/User/OrderController.php b/app/Http/Controllers/User/OrderController.php index 642f917a..ab9296c4 100755 --- a/app/Http/Controllers/User/OrderController.php +++ b/app/Http/Controllers/User/OrderController.php @@ -75,18 +75,43 @@ class OrderController extends Controller private function getSurplusValue(User $user) { $plan = Plan::find($user->plan_id); + switch ($plan->type) { + case 0: return $this->getSurplusValueByCycle($user, $plan); + case 1: return $this->getSurplusValueByOneTime($user, $plan); + } + } + + private function getSurplusValueByOneTime(User $user, Plan $plan) + { + $trafficUnitPrice = 0; + $trafficUnitPrice = $plan->onetime_price / $plan->transfer_enable; + if ($user->discount && $trafficUnitPrice) { + $trafficUnitPrice = $trafficUnitPrice - ($trafficUnitPrice * $user->discount / 100); + } + $notUsedTrafficPrice = $plan->transfer_enable - (($user->u + $user->d) / 1073741824); + $result = $trafficUnitPrice * $notUsedTrafficPrice; + return $result > 0 ? $result : 0; + } + + private function getSurplusValueByCycle(User $user, Plan $plan) + { $dayPrice = 0; if ($plan->month_price) { - $dayPrice = $plan->month_price / 30; + $dayPrice = $plan->month_price / 2592000; } else if ($plan->quarter_price) { - $dayPrice = $plan->quarter_price / 91; + $dayPrice = $plan->quarter_price / 7862400; } else if ($plan->half_year_price) { - $dayPrice = $plan->half_year_price / 183; + $dayPrice = $plan->half_year_price / 15811200; } else if ($plan->year_price) { - $dayPrice = $plan->year_price / 365; + $dayPrice = $plan->year_price / 31536000; } - $remainingDay = ($user->expired_at - time()) / 86400; - return $remainingDay * $dayPrice; + // exclude discount + if ($user->discount && $dayPrice) { + $dayPrice = $dayPrice - ($dayPrice * $user->discount / 100); + } + $remainingDay = $user->expired_at - time(); + $result = $remainingDay * $dayPrice; + return $result > 0 ? $result : 0; } public function save(OrderSave $request) @@ -137,22 +162,6 @@ class OrderController extends Controller $order->cycle = $request->input('cycle'); $order->trade_no = Helper::guid(); $order->total_amount = $plan[$request->input('cycle')]; - // renew and change subscribe process - if ($user->expired_at > time() && $order->plan_id !== $user->plan_id) { - if (!(int)config('v2board.plan_change_enable', 1)) abort(500, '目前不允许更改订阅,请联系管理员'); - $order->type = 3; - $order->surplus_amount = $this->getSurplusValue($user); - if ($order->surplus_amount >= $order->total_amount) { - $order->refund_amount = $order->surplus_amount - $order->total_amount; - $order->total_amount = 0; - } else { - $order->total_amount = $order->total_amount - $order->surplus_amount; - } - } else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) { - $order->type = 2; - } else { - $order->type = 1; - } // discount start // coupon if (isset($coupon)) { @@ -179,6 +188,22 @@ class OrderController extends Controller // discount complete $order->total_amount = $order->total_amount - $order->discount_amount; // discount end + // renew and change subscribe process + if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) { + if (!(int)config('v2board.plan_change_enable', 1)) abort(500, '目前不允许更改订阅,请联系客服或提交工单'); + $order->type = 3; + $order->surplus_amount = $this->getSurplusValue($user); + if ($order->surplus_amount >= $order->total_amount) { + $order->refund_amount = $order->surplus_amount - $order->total_amount; + $order->total_amount = 0; + } else { + $order->total_amount = $order->total_amount - $order->surplus_amount; + } + } else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) { + $order->type = 2; + } else { + $order->type = 1; + } // invite process if ($user->invite_user_id && $order->total_amount > 0) { $order->invite_user_id = $user->invite_user_id; @@ -381,14 +406,15 @@ class OrderController extends Controller private function stripeAlipay($order) { - $exchange = Helper::exchange('CNY', 'HKD'); + $currency = config('stripe_currency', 'hkd'); + $exchange = Helper::exchange('CNY', strtoupper($currency)); if (!$exchange) { abort(500, '货币转换超时,请稍后再试'); } Stripe::setApiKey(config('v2board.stripe_sk_live')); $source = Source::create([ 'amount' => floor($order->total_amount * $exchange), - 'currency' => 'hkd', + 'currency' => $currency, 'type' => 'alipay', 'redirect' => [ 'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order' @@ -406,14 +432,15 @@ class OrderController extends Controller private function stripeWepay($order) { - $exchange = Helper::exchange('CNY', 'HKD'); + $currency = config('stripe_currency', 'hkd'); + $exchange = Helper::exchange('CNY', strtoupper($currency)); if (!$exchange) { abort(500, '货币转换超时,请稍后再试'); } Stripe::setApiKey(config('v2board.stripe_sk_live')); $source = Source::create([ 'amount' => floor($order->total_amount * $exchange), - 'currency' => 'hkd', + 'currency' => $currency, 'type' => 'wechat', 'redirect' => [ 'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order' diff --git a/app/Http/Controllers/User/ServerController.php b/app/Http/Controllers/User/ServerController.php index 7ab45c71..60c1ccb6 100644 --- a/app/Http/Controllers/User/ServerController.php +++ b/app/Http/Controllers/User/ServerController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\User; use App\Http\Controllers\Controller; +use App\Services\UserService; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use App\Models\Server; @@ -17,7 +18,8 @@ class ServerController extends Controller { $user = User::find($request->session()->get('id')); $server = []; - if ($user->expired_at > time()) { + $userService = new UserService(); + if ($userService->isAvailable($user)) { $servers = Server::where('show', 1) ->orderBy('name') ->get(); diff --git a/app/Http/Controllers/User/TutorialController.php b/app/Http/Controllers/User/TutorialController.php index 0b15812f..6f3a71fa 100644 --- a/app/Http/Controllers/User/TutorialController.php +++ b/app/Http/Controllers/User/TutorialController.php @@ -49,9 +49,10 @@ class TutorialController extends Controller 'data' => $tutorial ]); } - $tutorial = Tutorial::select(['id', 'title', 'description', 'icon']) + $tutorial = Tutorial::select(['id', 'category_id', 'title', 'icon']) ->where('show', 1) - ->get(); + ->get() + ->groupBy('category_id'); $user = User::find($request->session()->get('id')); $response = [ 'data' => [ @@ -59,8 +60,8 @@ class TutorialController extends Controller 'safe_area_var' => [ 'subscribe_url' => config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'], 'app_name' => config('v2board.app_name', 'V2board'), - 'apple_id' => $user->expired_at > time() ? config('v2board.apple_id', '管理员暂无提供AppleID信息') : '账号过期或未订阅', - 'apple_id_password' => $user->expired_at > time() ? config('v2board.apple_id_password', '管理员暂无提供AppleID信息') : '账号过期或未订阅' + 'apple_id' => $user->expired_at > time() || $user->expired_at === NULL ? config('v2board.apple_id', '本站暂无提供AppleID信息') : '账号过期或未订阅', + 'apple_id_password' => $user->expired_at > time() || $user->expired_at === NULL ? config('v2board.apple_id_password', '本站暂无提供AppleID信息') : '账号过期或未订阅' ] ] ]; diff --git a/app/Http/Controllers/User/UserController.php b/app/Http/Controllers/User/UserController.php index 4f93f3b4..b728f744 100755 --- a/app/Http/Controllers/User/UserController.php +++ b/app/Http/Controllers/User/UserController.php @@ -58,7 +58,7 @@ class UserController extends Controller 'transfer_enable', 'last_login_at', 'created_at', - 'enable', + 'banned', 'is_admin', 'remind_expire', 'remind_traffic', diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 1a4114d0..dd8de52a 100755 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -43,7 +43,7 @@ class Kernel extends HttpKernel \Illuminate\Session\Middleware\StartSession::class, \App\Http\Middleware\ForceJson::class, \App\Http\Middleware\CORS::class, - 'throttle:60,1', + 'throttle:120,1', 'bindings', ], ]; diff --git a/app/Http/Requests/Admin/ConfigSave.php b/app/Http/Requests/Admin/ConfigSave.php index 7e5a03c4..84c608f5 100755 --- a/app/Http/Requests/Admin/ConfigSave.php +++ b/app/Http/Requests/Admin/ConfigSave.php @@ -16,14 +16,17 @@ class ConfigSave extends FormRequest 'email_verify' => 'in:0,1', 'app_name' => '', 'app_description' => '', - 'app_url' => 'url', - 'subscribe_url' => 'url', - 'plan_change_enable' => 'in:0,1', + 'app_url' => 'nullable|url', + 'subscribe_url' => 'nullable|url', 'try_out_enable' => 'in:0,1', 'try_out_plan_id' => 'integer', 'try_out_hour' => 'numeric', 'email_whitelist_enable' => 'in:0,1', 'email_whitelist_suffix' => '', + // subscribe + 'plan_change_enable' => 'in:0,1', + 'reset_traffic_method' => 'in:0,1', + 'renew_reset_traffic_enable' => 'in:0,1', // server 'server_token' => 'nullable|min:16', 'server_license' => 'nullable', @@ -38,6 +41,7 @@ class ConfigSave extends FormRequest 'stripe_sk_live' => '', 'stripe_pk_live' => '', 'stripe_webhook_key' => '', + 'stripe_currency' => 'in:hkd,usd,sgd', // bitpayx 'bitpayx_enable' => 'in:0,1', 'bitpayx_appsecret' => '', @@ -55,11 +59,6 @@ class ConfigSave extends FormRequest 'apple_id_password' => '' ]; - public static function filter() - { - return array_keys(self::RULES); - } - /** * Get the validation rules that apply to the request. * @@ -72,7 +71,10 @@ class ConfigSave extends FormRequest public function messages() { + // illiteracy prompt return [ + 'app_url.url' => '站点URL格式不正确,必须携带http(s)://', + 'subscribe_url.url' => '订阅URL格式不正确,必须携带http(s)://' ]; } } diff --git a/app/Http/Requests/Admin/OrderUpdate.php b/app/Http/Requests/Admin/OrderUpdate.php index 15ce9514..1d85e9b2 100644 --- a/app/Http/Requests/Admin/OrderUpdate.php +++ b/app/Http/Requests/Admin/OrderUpdate.php @@ -15,7 +15,7 @@ class OrderUpdate extends FormRequest { return [ 'status' => 'in:0,1,2,3', - 'commission_status' => 'in:0,1' + 'commission_status' => 'in:0,1,2' ]; } @@ -23,7 +23,7 @@ class OrderUpdate extends FormRequest { return [ 'status.in' => '销售状态格式不正确', - 'commission_status.in' => '续费状态格式不正确' + 'commission_status.in' => '佣金状态格式不正确' ]; } } diff --git a/app/Http/Requests/Admin/PlanSave.php b/app/Http/Requests/Admin/PlanSave.php index 914c9cae..00ad36d4 100755 --- a/app/Http/Requests/Admin/PlanSave.php +++ b/app/Http/Requests/Admin/PlanSave.php @@ -6,6 +6,17 @@ use Illuminate\Foundation\Http\FormRequest; class PlanSave extends FormRequest { + CONST RULES = [ + 'name' => 'required', + 'content' => '', + 'group_id' => 'required', + 'transfer_enable' => 'required', + 'month_price' => 'nullable|integer', + 'quarter_price' => 'nullable|integer', + 'half_year_price' => 'nullable|integer', + 'year_price' => 'nullable|integer', + 'onetime_price' => 'nullable|integer' + ]; /** * Get the validation rules that apply to the request. * @@ -13,27 +24,22 @@ class PlanSave extends FormRequest */ public function rules() { - return [ - 'name' => 'required', - 'group_id' => 'required', - 'transfer_enable' => 'required', - 'month_price' => 'nullable|integer', - 'quarter_price' => 'nullable|integer', - 'half_year_price' => 'nullable|integer', - 'year_price' => 'nullable|integer' - ]; + return self::RULES; } public function messages() { return [ 'name.required' => '套餐名称不能为空', + 'type.required' => '套餐类型不能为空', + 'type.in' => '套餐类型格式有误', 'group_id.required' => '权限组不能为空', 'transfer_enable.required' => '流量不能为空', 'month_price.integer' => '月付金额格式有误', 'quarter_price.integer' => '季付金额格式有误', 'half_year_price.integer' => '半年付金额格式有误', - 'year_price.integer' => '年付金额格式有误' + 'year_price.integer' => '年付金额格式有误', + 'onetime_price.integer' => '一次性金额有误' ]; } } diff --git a/app/Http/Requests/Admin/TutorialSave.php b/app/Http/Requests/Admin/TutorialSave.php index 48970104..9cf5e401 100644 --- a/app/Http/Requests/Admin/TutorialSave.php +++ b/app/Http/Requests/Admin/TutorialSave.php @@ -6,6 +6,13 @@ use Illuminate\Foundation\Http\FormRequest; class TutorialSave extends FormRequest { + CONST RULES = [ + 'title' => 'required', + // 1:windows 2:macos 3:ios 4:android 5:linux 6:router + 'category_id' => 'required|in:1,2,3,4,5,6', + 'icon' => 'required', + 'steps' => 'required' + ]; /** * Get the validation rules that apply to the request. * @@ -13,19 +20,17 @@ class TutorialSave extends FormRequest */ public function rules() { - return [ - 'title' => 'required', - 'description' => 'required', - 'icon' => 'required' - ]; + return self::RULES; } public function messages() { return [ 'title.required' => '标题不能为空', - 'description.required' => '描述不能为空', - 'icon.required' => '图标不能为空' + 'category_id.required' => '分类不能为空', + 'category_id.in' => '分类格式不正确', + 'icon.required' => '图标不能为空', + 'steps.required' => '教程步骤不能为空' ]; } } diff --git a/app/Http/Requests/Admin/UserUpdate.php b/app/Http/Requests/Admin/UserUpdate.php index e7cfaa7c..8dd82d28 100644 --- a/app/Http/Requests/Admin/UserUpdate.php +++ b/app/Http/Requests/Admin/UserUpdate.php @@ -6,6 +6,21 @@ use Illuminate\Foundation\Http\FormRequest; class UserUpdate extends FormRequest { + CONST RULES = [ + 'email' => 'required|email', + 'password' => 'nullable', + 'transfer_enable' => 'numeric', + 'expired_at' => 'nullable|integer', + 'banned' => 'required|in:0,1', + 'plan_id' => 'nullable|integer', + 'commission_rate' => 'nullable|integer|min:0|max:100', + 'discount' => 'nullable|integer|min:0|max:100', + 'is_admin' => 'required|in:0,1', + 'u' => 'integer', + 'd' => 'integer', + 'balance' => 'integer', + 'commission_balance' => 'integer' + ]; /** * Get the validation rules that apply to the request. * @@ -13,16 +28,7 @@ class UserUpdate extends FormRequest */ public function rules() { - return [ - 'email' => 'required|email', - 'transfer_enable' => 'numeric', - 'expired_at' => 'integer', - 'banned' => 'required|in:0,1', - 'is_admin' => 'required|in:0,1', - 'plan_id' => 'integer', - 'commission_rate' => 'nullable|integer|min:0|max:100', - 'discount' => 'nullable|integer|min:0|max:100' - ]; + return self::RULES; } public function messages() @@ -44,7 +50,11 @@ class UserUpdate extends FormRequest 'discount.integer' => '专属折扣比例格式不正确', 'discount.nullable' => '专属折扣比例格式不正确', 'discount.min' => '专属折扣比例最小为0', - 'discount.max' => '专属折扣比例最大为100' + 'discount.max' => '专属折扣比例最大为100', + 'u.integer' => '上行流量格式不正确', + 'd.integer' => '下行流量格式不正确', + 'balance.integer' => '余额格式不正确', + 'commission_balance.integer' => '佣金格式不正确' ]; } } diff --git a/app/Http/Requests/User/OrderSave.php b/app/Http/Requests/User/OrderSave.php index 686bf72e..114a1d59 100755 --- a/app/Http/Requests/User/OrderSave.php +++ b/app/Http/Requests/User/OrderSave.php @@ -15,7 +15,7 @@ class OrderSave extends FormRequest { return [ 'plan_id' => 'required', - 'cycle' => 'required|in:month_price,quarter_price,half_year_price,year_price' + 'cycle' => 'required|in:month_price,quarter_price,half_year_price,year_price,onetime_price' ]; } diff --git a/app/Services/ServerService.php b/app/Services/ServerService.php new file mode 100644 index 00000000..e9a2f349 --- /dev/null +++ b/app/Services/ServerService.php @@ -0,0 +1,31 @@ +whereRaw('u + d < transfer_enable') + ->where(function ($query) { + $query->where('expired_at', '>=', time()) + ->orWhere('expired_at', NULL); + }) + ->where('banned', 0) + ->select([ + 'id', + 'email', + 't', + 'u', + 'd', + 'transfer_enable', + 'v2ray_uuid', + 'v2ray_alter_id', + 'v2ray_level' + ]) + ->get(); + } +} diff --git a/app/Services/UserService.php b/app/Services/UserService.php new file mode 100644 index 00000000..cf2ecf5e --- /dev/null +++ b/app/Services/UserService.php @@ -0,0 +1,50 @@ +banned && $user->transfer_enable && ($user->expired_at > time() || $user->expired_at === NULL)) { + return true; + } + return false; + } + + public function getAvailableUsers() + { + return User::whereRaw('u + d < transfer_enable') + ->where(function ($query) { + $query->where('expired_at', '>=', time()) + ->orWhere('expired_at', NULL); + }) + ->where('banned', 0) + ->get(); + } + + public function getUnAvailbaleUsers() + { + return User::where(function ($query) { + $query->where('expired_at', '<', time()) + ->orWhere('expired_at', 0); + }) + ->where(function ($query) { + $query->where('plan_id', NULL) + ->orWhere('transfer_enable', 0); + }) + ->get(); + } + + public function getUsersByIds($ids) + { + return User::whereIn('id', $ids)->get(); + } + + public function getAllUsers() + { + return User::all(); + } +} diff --git a/config/app.php b/config/app.php index 46dd4f00..90922c71 100755 --- a/config/app.php +++ b/config/app.php @@ -232,7 +232,9 @@ return [ |-------------------------------------------------------------------------- | V2board version |-------------------------------------------------------------------------- + | + | The only modification by laravel config + | */ - - 'version' => '1.1.2' + 'version' => '1.2' ]; diff --git a/database/install.sql b/database/install.sql index 0c2ecd7c..6ff5b890 100644 --- a/database/install.sql +++ b/database/install.sql @@ -109,6 +109,7 @@ CREATE TABLE `v2_plan` ( `quarter_price` int(11) DEFAULT '0', `half_year_price` int(11) DEFAULT '0', `year_price` int(11) DEFAULT '0', + `onetime_price` int(11) DEFAULT NULL, `created_at` int(11) NOT NULL, `updated_at` int(11) NOT NULL, PRIMARY KEY (`id`) @@ -188,8 +189,8 @@ CREATE TABLE `v2_ticket_message` ( DROP TABLE IF EXISTS `v2_tutorial`; CREATE TABLE `v2_tutorial` ( `id` int(11) NOT NULL AUTO_INCREMENT, + `category_id` int(11) NOT NULL, `title` varchar(255) CHARACTER SET utf8mb4 NOT NULL, - `description` varchar(255) CHARACTER SET utf8mb4 NOT NULL, `icon` varchar(255) CHARACTER SET utf8mb4 NOT NULL, `steps` text, `show` tinyint(1) NOT NULL DEFAULT '0', @@ -198,11 +199,11 @@ CREATE TABLE `v2_tutorial` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -INSERT INTO `v2_tutorial` (`id`, `title`, `description`, `icon`, `steps`, `show`, `created_at`, `updated_at`) VALUES -(1, 'Windows', '兼容 Windows 7 以上的版本', 'fab fa-2x fa-windows', '[{\"default_area\":\"
{{$app_name}}
{{$subscribe_url}}
{{$app_name}}
{{$subscribe_url}}
{{$app_name}}
{{$subscribe_url}}
{{$apple_id}}
点击复制密码
{{$app_name}}
{{$subscribe_url}}
{{$app_name}}
{{$subscribe_url}}
{{$subscribe_url}}
{{$app_name}}
{{$apple_id}}
点击复制密码