Merge pull request #399 from v2board/dev

1.4.3
This commit is contained in:
tokumeikoi 2021-02-13 17:34:45 +09:00 committed by GitHub
commit f81426b2c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 1116 additions and 638 deletions

View File

@ -5,6 +5,7 @@ namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class CheckCommission extends Command
{
@ -64,10 +65,19 @@ class CheckCommission extends Command
foreach ($order as $item) {
$inviter = User::find($item->invite_user_id);
if (!$inviter) continue;
$inviter->commission_balance = $inviter->commission_balance + $item->commission_balance;
if ((int)config('v2board.withdraw_close_enable', 0)) {
$inviter->balance = $inviter->balance + $item->commission_balance;
} else {
$inviter->commission_balance = $inviter->commission_balance + $item->commission_balance;
}
DB::beginTransaction();
if ($inviter->save()) {
$item->commission_status = 2;
$item->save();
if (!$item->save()) {
DB::rollBack();
continue;
}
DB::commit();
}
}
}

View File

@ -1,68 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Utils\CacheKey;
use Illuminate\Console\Command;
use App\Models\ServerLog;
use App\Models\ServerStat;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class V2boardCache extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'v2board:cache';
/**
* 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()
{
}
private function cacheServerStat()
{
$serverLogs = ServerLog::select(
'server_id',
DB::raw("sum(u) as u"),
DB::raw("sum(d) as d"),
DB::raw("count(*) as online")
)
->where('updated_at', '>=', time() - 3600)
->groupBy('server_id')
->get();
foreach ($serverLogs as $serverLog) {
$data = [
'server_id' => $serverLog->server_id,
'u' => $serverLog->u,
'd' => $serverLog->d,
'online' => $serverLog->online
];
// ServerStat::create($data);
}
}
}

View File

@ -0,0 +1,101 @@
<?php
namespace App\Console\Commands;
use App\Jobs\StatServerJob;
use Illuminate\Console\Command;
use App\Models\Order;
use App\Models\StatOrder;
use App\Models\ServerLog;
use Illuminate\Support\Facades\DB;
class V2BoardStatistics extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'v2board: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()
{
$this->statOrder();
$this->statServer();
}
private function statOrder()
{
$endAt = strtotime(date('Y-m-d'));
$startAt = strtotime('-1 day', $endAt);
$builder = Order::where('created_at', '>=', $startAt)
->where('created_at', '<', $endAt)
->whereIn('status', [3, 4]);
$orderCount = $builder->count();
$orderAmount = $builder->sum('total_amount');
$builder = $builder->where('commission_balance', '!=', NULL)
->whereIn('commission_status', [1, 2]);
$commissionCount = $builder->count();
$commissionAmount = $builder->sum('commission_balance');
$data = [
'order_count' => $orderCount,
'order_amount' => $orderAmount,
'commission_count' => $commissionCount,
'commission_amount' => $commissionAmount,
'record_type' => 'd',
'record_at' => $startAt
];
$statistic = StatOrder::where('record_at', $startAt)
->where('record_type', 'd')
->first();
if ($statistic) {
$statistic->update($data);
return;
}
StatOrder::create($data);
}
private function statServer()
{
$endAt = strtotime(date('Y-m-d'));
$startAt = strtotime('-1 day', $endAt);
$statistics = ServerLog::select([
'server_id',
'method as server_type',
DB::raw("sum(u) as u"),
DB::raw("sum(d) as d"),
])
->where('log_at', '>=', $startAt)
->where('log_at', '<', $endAt)
->groupBy('server_id', 'method')
->get()
->toArray();
foreach ($statistics as $statistic) {
$statistic['record_type'] = 'd';
$statistic['record_at'] = $startAt;
StatServerJob::dispatch($statistic);
}
}
}

View File

@ -25,7 +25,7 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule)
{
// v2board
$schedule->command('v2board:cache')->hourly();
$schedule->command('v2board:statistics')->daily();
// check
$schedule->command('check:order')->everyMinute();
$schedule->command('check:commission')->everyMinute();

View File

@ -47,7 +47,9 @@ class ConfigController extends Controller
'invite_never_expire' => config('v2board.invite_never_expire', 0),
'commission_first_time_enable' => config('v2board.commission_first_time_enable', 1),
'commission_auto_check_enable' => config('v2board.commission_auto_check_enable', 1),
'commission_withdraw_limit' => config('v2board.commission_withdraw_limit', 100)
'commission_withdraw_limit' => config('v2board.commission_withdraw_limit', 100),
'commission_withdraw_method' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
'withdraw_close_enable' => config('v2board.withdraw_close_enable', 0)
],
'site' => [
'safe_mode_enable' => (int)config('v2board.safe_mode_enable', 0),

View File

@ -95,7 +95,9 @@ class CouponController extends Controller
{
$coupons = [];
$coupon = $request->validated();
$coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
if (isset($coupon['limit_plan_ids'])) {
$coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
}
$coupon['created_at'] = $coupon['updated_at'] = time();
unset($coupon['generate_count']);
for ($i = 0;$i < $request->input('generate_count');$i++) {
@ -116,7 +118,8 @@ class CouponController extends Controller
$endTime = date('Y-m-d H:i:s', $coupon['ended_at']);
$limitUse = $coupon['limit_use'] ?? '不限制';
$createTime = date('Y-m-d H:i:s', $coupon['created_at']);
$data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$coupon['limit_plan_ids']},{$coupon['code']},{$createTime}\r\n";
$limitPlanIds = $coupon['limit_plan_ids'] ?? '不限制';
$data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n";
}
echo $data;
}

View File

@ -125,7 +125,7 @@ class V2rayController extends Controller
public function viewConfig(Request $request)
{
$serverService = new ServerService();
$config = $serverService->getVmessConfig($request->input('node_id'), 23333);
$config = $serverService->getV2RayConfig($request->input('node_id'), 23333);
return response([
'data' => $config
]);

View File

@ -2,6 +2,9 @@
namespace App\Http\Controllers\Admin;
use App\Models\ServerShadowsocks;
use App\Models\ServerTrojan;
use App\Services\ServerService;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\ServerGroup;
@ -10,7 +13,10 @@ use App\Models\Plan;
use App\Models\User;
use App\Models\Ticket;
use App\Models\Order;
use App\Models\StatOrder;
use App\Models\StatServer;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class StatController extends Controller
{
@ -43,4 +49,76 @@ class StatController extends Controller
]
]);
}
public function getOrder(Request $request)
{
$statistics = StatOrder::where('record_type', 'd')
->limit(31)
->orderBy('record_at', 'DESC')
->get()
->toArray();
$result = [];
foreach ($statistics as $statistic) {
$date = date('m-d', $statistic['record_at']);
array_push($result, [
'type' => '收款金额',
'date' => $date,
'value' => $statistic['order_amount'] / 100
]);
array_push($result, [
'type' => '收款笔数',
'date' => $date,
'value' => $statistic['order_count']
]);
array_push($result, [
'type' => '佣金金额',
'date' => $date,
'value' => $statistic['commission_amount'] / 100
]);
array_push($result, [
'type' => '佣金笔数',
'date' => $date,
'value' => $statistic['commission_count']
]);
}
$result = array_reverse($result);
return response([
'data' => $result
]);
}
public function getServerLastRank()
{
$servers = [
'shadowsocks' => ServerShadowsocks::where('parent_id', null)->get()->toArray(),
'vmess' => Server::where('parent_id', null)->get()->toArray(),
'trojan' => ServerTrojan::where('parent_id', null)->get()->toArray()
];
$timestamp = strtotime('-1 day', strtotime(date('Y-m-d')));
$statistics = StatServer::select([
'server_id',
'server_type',
'u',
'd'
])
->where('record_at', '>=', $timestamp)
->where('record_type', 'd')
->limit(10)
->orderBy('record_at', 'DESC')
->get()
->toArray();
foreach ($statistics as $k => $v) {
foreach ($servers[$v['server_type']] as $server) {
if ($server['id'] === $v['server_id']) {
$statistics[$k]['server_name'] = $server['name'];
}
}
$statistics[$k]['total'] = ($v['u'] + $v['d']) / 1073741824;
}
array_multisort(array_column($statistics, 'total'), SORT_DESC, $statistics);
return response([
'data' => $statistics
]);
}
}

View File

@ -17,6 +17,16 @@ use Illuminate\Support\Facades\DB;
class UserController extends Controller
{
public function resetSecret(Request $request)
{
$user = User::find($request->input('id'));
if (!$user) abort(500, '用户不存在');
$user->token = Helper::guid();
$user->uuid = Helper::guid(true);
return response([
'data' => $user->save()
]);
}
private function filter(Request $request, $builder)
{

View File

@ -61,7 +61,7 @@ class ClientController extends Controller
private function quantumult($user, $servers = [])
{
$uri = '';
header('subscription-userinfo: upload=' . $user->u . '; download=' . $user->d . ';total=' . $user->transfer_enable);
header('subscription-userinfo: upload=' . $user['u'] . '; download=' . $user['d'] . ';total=' . $user['transfer_enable']);
foreach ($servers as $item) {
if ($item['type'] === 'v2ray') {
$str = '';
@ -84,10 +84,10 @@ class ClientController extends Controller
{
$uri = '';
//display remaining traffic and expire date
$upload = round($user->u / (1024*1024*1024), 2);
$download = round($user->d / (1024*1024*1024), 2);
$totalTraffic = round($user->transfer_enable / (1024*1024*1024), 2);
$expiredDate = date('Y-m-d', $user->expired_at);
$upload = round($user['u'] / (1024*1024*1024), 2);
$download = round($user['d'] / (1024*1024*1024), 2);
$totalTraffic = round($user['transfer_enable'] / (1024*1024*1024), 2);
$expiredDate = date('Y-m-d', $user['expired_at']);
$uri .= "STATUS=🚀↑:{$upload}GB,↓:{$download}GB,TOT:{$totalTraffic}GB💡Expires:{$expiredDate}\r\n";
foreach ($servers as $item) {
if ($item['type'] === 'shadowsocks') {
@ -106,7 +106,7 @@ class ClientController extends Controller
private function quantumultX($user, $servers = [])
{
$uri = '';
header("subscription-userinfo: upload={$user->u}; download={$user->d}; total={$user->transfer_enable}; expire={$user->expired_at}");
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
foreach ($servers as $item) {
if ($item['type'] === 'shadowsocks') {
$uri .= QuantumultX::buildShadowsocks($user['uuid'], $item);
@ -143,6 +143,11 @@ class ClientController extends Controller
$configs = [];
$subs = [];
$subs['servers'] = [];
$subs['bytes_used'] = '';
$subs['bytes_remaining'] = '';
$bytesUsed = $user['u'] + $user['d'];
$bytesRemaining = $user['transfer_enable'] - $bytesUsed;
foreach ($servers as $item) {
if ($item['type'] === 'shadowsocks') {
@ -151,7 +156,8 @@ class ClientController extends Controller
}
$subs['version'] = 1;
$subs['remark'] = config('v2board.app_name', 'V2Board');
$subs['bytes_used'] = $bytesUsed;
$subs['bytes_remaining'] = $bytesRemaining;
$subs['servers'] = array_merge($subs['servers'] ? $subs['servers'] : [], $configs);
return json_encode($subs, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT);

View File

@ -109,7 +109,7 @@ class DeepbworkController extends Controller
}
$serverService = new ServerService();
try {
$json = $serverService->getVmessConfig($nodeId, $localPort);
$json = $serverService->getV2RayConfig($nodeId, $localPort);
} catch (\Exception $e) {
abort(500, $e->getMessage());
}

View File

@ -92,7 +92,7 @@ class PoseidonController extends Controller
$serverService = new ServerService();
try {
$json = $serverService->getVmessConfig($nodeId, $localPort);
$json = $serverService->getV2RayConfig($nodeId, $localPort);
$json->poseidon = [
'license_key' => (string)config('v2board.server_license'),
];

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers\User;
use App\Utils\Dict;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
@ -11,8 +12,10 @@ class CommController extends Controller
{
return response([
'data' => [
'isTelegram' => (int)config('v2board.telegram_bot_enable', 0),
'stripePk' => config('v2board.stripe_pk_live')
'is_telegram' => (int)config('v2board.telegram_bot_enable', 0),
'stripe_pk' => config('v2board.stripe_pk_live'),
'withdraw_methods' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
'withdraw_close' => (int)config('v2board.withdraw_close_enable', 0)
]
]);
}

View File

@ -11,25 +11,25 @@ class CouponController extends Controller
public function check(Request $request)
{
if (empty($request->input('code'))) {
abort(500, '优惠券码不能为空');
abort(500, __('user.coupon.check.coupon_not_empty'));
}
$coupon = Coupon::where('code', $request->input('code'))->first();
if (!$coupon) {
abort(500, '优惠券无效');
abort(500, __('user.coupon.check.coupon_invalid'));
}
if ($coupon->limit_use <= 0 && $coupon->limit_use !== NULL) {
abort(500, '优惠券已无可用次数');
abort(500, __('user.coupon.check.coupon_not_available_by_number'));
}
if (time() < $coupon->started_at) {
abort(500, '优惠券还未到可用时间');
abort(500, __('user.coupon.check.coupon_not_available_by_time'));
}
if (time() > $coupon->ended_at) {
abort(500, '优惠券已过期');
abort(500, __('user.coupon.check.coupon_expired'));
}
if ($coupon->limit_plan_ids) {
$limitPlanIds = json_decode($coupon->limit_plan_ids);
if (!in_array($request->input('plan_id'), $limitPlanIds)) {
abort(500, '这个计划无法使用该优惠码');
abort(500, __('user.coupon.check.coupon_limit_plan'));
}
}
return response([

View File

@ -14,7 +14,7 @@ class InviteController extends Controller
public function save(Request $request)
{
if (InviteCode::where('user_id', $request->session()->get('id'))->where('status', 0)->count() >= config('v2board.invite_gen_limit', 5)) {
abort(500, '已达到创建数量上限');
abort(500, __('user.invite.save.invite_create_limit'));
}
$inviteCode = new InviteCode();
$inviteCode->user_id = $request->session()->get('id');

View File

@ -17,11 +17,11 @@ class KnowledgeController extends Controller
->where('show', 1)
->first()
->toArray();
if (!$knowledge) abort(500, '知识不存在');
if (!$knowledge) abort(500, __('user.knowledge.fetch.knowledge_not_exist'));
$user = User::find($request->session()->get('id'));
$userService = new UserService();
$appleId = $userService->isAvailable($user) ? config('v2board.apple_id') : '没有有效订阅无法使用本站提供的AppleID';
$appleIdPassword = $userService->isAvailable($user) ? config('v2board.apple_id_password') : '没有有效订阅无法使用本站提供的AppleID';
$appleId = $userService->isAvailable($user) ? config('v2board.apple_id') : __('user.knowledge.fetch.apple_id_must_be_plan');
$appleIdPassword = $userService->isAvailable($user) ? config('v2board.apple_id_password') : __('user.knowledge.fetch.apple_id_must_be_plan');
$subscribeUrl = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
$knowledge['body'] = str_replace('{{siteName}}', config('v2board.app_name', 'V2Board'), $knowledge['body']);
$knowledge['body'] = str_replace('{{appleId}}', $appleId, $knowledge['body']);

View File

@ -50,12 +50,12 @@ class OrderController extends Controller
->where('trade_no', $request->input('trade_no'))
->first();
if (!$order) {
abort(500, '订单不存在');
abort(500, __('user.order.details.order_not_exist'));
}
$order['plan'] = Plan::find($order->plan_id);
$order['try_out_plan_id'] = (int)config('v2board.try_out_plan_id');
if (!$order['plan']) {
abort(500, '订阅不存在');
abort(500, __('user.order.details.plan_not_exist'));
}
return response([
'data' => $order
@ -66,38 +66,38 @@ class OrderController extends Controller
{
$userService = new UserService();
if ($userService->isNotCompleteOrderByUserId($request->session()->get('id'))) {
abort(500, '您有未付款或开通中的订单,请稍后或取消再试');
abort(500, __('user.order.save.exist_open_order'));
}
$plan = Plan::find($request->input('plan_id'));
$user = User::find($request->session()->get('id'));
if (!$plan) {
abort(500, '该订阅不存在');
abort(500, __('user.order.save.plan_not_exist'));
}
if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
if ($request->input('cycle') !== 'reset_price') {
abort(500, '该订阅已售罄,请更换其他订阅');
abort(500, __('user.order.save.plan_stop_sell'));
}
}
if (!$plan->renew && $user->plan_id == $plan->id && $request->input('cycle') !== 'reset_price') {
abort(500, '该订阅无法续费,请更换其他订阅');
abort(500, __('user.order.save.plan_stop_renew'));
}
if ($plan[$request->input('cycle')] === NULL) {
abort(500, '该订阅周期无法进行购买,请选择其他周期');
abort(500, __('user.order.save.plan_stop'));
}
if ($request->input('cycle') === 'reset_price') {
if ($user->expired_at <= time() || !$user->plan_id) {
abort(500, '订阅已过期或无有效订阅,无法购买重置包');
abort(500, __('user.order.save.plan_exist_not_buy_package'));
}
}
if (!$plan->show && $plan->renew && !$userService->isAvailable($user)) {
abort(500, '订阅已过期,请更换其他订阅');
abort(500, __('user.order.save.plan_expired'));
}
DB::beginTransaction();
@ -113,7 +113,7 @@ class OrderController extends Controller
$couponService = new CouponService($request->input('coupon_code'));
if (!$couponService->use($order)) {
DB::rollBack();
abort(500, '优惠券使用失败');
abort(500, __('user.order.save.coupon_use_failed'));
}
$order->coupon_id = $couponService->getId();
}
@ -128,14 +128,14 @@ class OrderController extends Controller
if ($remainingBalance > 0) {
if (!$userService->addBalance($order->user_id, - $order->total_amount)) {
DB::rollBack();
abort(500, '余额不足');
abort(500, __('user.order.save.insufficient_balance'));
}
$order->balance_amount = $order->total_amount;
$order->total_amount = 0;
} else {
if (!$userService->addBalance($order->user_id, - $user->balance)) {
DB::rollBack();
abort(500, '余额不足');
abort(500, __('user.order.save.insufficient_balance'));
}
$order->balance_amount = $user->balance;
$order->total_amount = $order->total_amount - $user->balance;
@ -144,7 +144,7 @@ class OrderController extends Controller
if (!$order->save()) {
DB::rollback();
abort(500, '订单创建失败');
abort(500, __('user.order.save.order_create_failed'));
}
DB::commit();
@ -163,7 +163,7 @@ class OrderController extends Controller
->where('status', 0)
->first();
if (!$order) {
abort(500, '订单不存在或已支付');
abort(500, __('user.order.checkout.order_not_exist_or_paid'));
}
// free process
if ($order->total_amount <= 0) {
@ -180,7 +180,7 @@ class OrderController extends Controller
case 0:
// alipayF2F
if (!(int)config('v2board.alipay_enable')) {
abort(500, '支付方式不可用');
abort(500, __('user.order.checkout.pay_method_not_use'));
}
return response([
'type' => 0,
@ -189,7 +189,7 @@ class OrderController extends Controller
case 2:
// stripeAlipay
if (!(int)config('v2board.stripe_alipay_enable')) {
abort(500, '支付方式不可用');
abort(500, __('user.order.checkout.pay_method_not_use'));
}
return response([
'type' => 1,
@ -198,7 +198,7 @@ class OrderController extends Controller
case 3:
// stripeWepay
if (!(int)config('v2board.stripe_wepay_enable')) {
abort(500, '支付方式不可用');
abort(500, __('user.order.checkout.pay_method_not_use'));
}
return response([
'type' => 0,
@ -207,7 +207,7 @@ class OrderController extends Controller
case 4:
// bitpayX
if (!(int)config('v2board.bitpayx_enable')) {
abort(500, '支付方式不可用');
abort(500, __('user.order.checkout.pay_method_not_use'));
}
return response([
'type' => 1,
@ -215,7 +215,7 @@ class OrderController extends Controller
]);
case 5:
if (!(int)config('v2board.mgate_enable')) {
abort(500, '支付方式不可用');
abort(500, __('user.order.checkout.pay_method_not_use'));
}
return response([
'type' => 1,
@ -223,7 +223,7 @@ class OrderController extends Controller
]);
case 6:
if (!(int)config('v2board.stripe_card_enable')) {
abort(500, '支付方式不可用');
abort(500, __('user.order.checkout.pay_method_not_use'));
}
return response([
'type' => 2,
@ -231,14 +231,14 @@ class OrderController extends Controller
]);
case 7:
if (!(int)config('v2board.epay_enable')) {
abort(500, '支付方式不可用');
abort(500, __('user.order.checkout.pay_method_not_use'));
}
return response([
'type' => 1,
'data' => $this->epay($order)
]);
default:
abort(500, '支付方式不存在');
abort(500, __('user.order.checkout.pay_method_not_use'));
}
}
@ -249,7 +249,7 @@ class OrderController extends Controller
->where('user_id', $request->session()->get('id'))
->first();
if (!$order) {
abort(500, '订单不存在');
abort(500, __('user.order.check.order_not_exist'));
}
return response([
'data' => $order->status
@ -323,20 +323,20 @@ class OrderController extends Controller
public function cancel(Request $request)
{
if (empty($request->input('trade_no'))) {
abort(500, '参数有误');
abort(500, __('user.order.cancel.params_wrong'));
}
$order = Order::where('trade_no', $request->input('trade_no'))
->where('user_id', $request->session()->get('id'))
->first();
if (!$order) {
abort(500, '订单不存在');
abort(500, __('user.order.cancel.order_not_exist'));
}
if ($order->status !== 0) {
abort(500, '只可以取消待支付订单');
abort(500, __('user.order.cancel.only_cancel_pending_order'));
}
$orderService = new OrderService($order);
if (!$orderService->cancel()) {
abort(500, '取消失败');
abort(500, __('user.order.cancel.cancel_failed'));
}
return response([
'data' => true
@ -372,7 +372,7 @@ class OrderController extends Controller
$currency = config('v2board.stripe_currency', 'hkd');
$exchange = Helper::exchange('CNY', strtoupper($currency));
if (!$exchange) {
abort(500, '货币转换超时,请稍后再试');
abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
}
Stripe::setApiKey(config('v2board.stripe_sk_live'));
$source = Source::create([
@ -390,7 +390,7 @@ class OrderController extends Controller
]
]);
if (!$source['redirect']['url']) {
abort(500, '支付网关请求失败');
abort(500, __('user.order.stripeAlipay.gateway_request_failed'));
}
return $source['redirect']['url'];
}
@ -400,7 +400,7 @@ class OrderController extends Controller
$currency = config('v2board.stripe_currency', 'hkd');
$exchange = Helper::exchange('CNY', strtoupper($currency));
if (!$exchange) {
abort(500, '货币转换超时,请稍后再试');
abort(500, __('user.order.stripeWepay.currency_convert_timeout'));
}
Stripe::setApiKey(config('v2board.stripe_sk_live'));
$source = Source::create([
@ -417,7 +417,7 @@ class OrderController extends Controller
]
]);
if (!$source['wechat']['qr_code_url']) {
abort(500, '支付网关请求失败');
abort(500, __('user.order.stripeWepay.gateway_request_failed'));
}
return $source['wechat']['qr_code_url'];
}
@ -427,7 +427,7 @@ class OrderController extends Controller
$currency = config('v2board.stripe_currency', 'hkd');
$exchange = Helper::exchange('CNY', strtoupper($currency));
if (!$exchange) {
abort(500, '货币转换超时,请稍后再试');
abort(500, __('user.order.stripeCard.currency_convert_timeout'));
}
Stripe::setApiKey(config('v2board.stripe_sk_live'));
try {
@ -442,11 +442,11 @@ class OrderController extends Controller
]
]);
} catch (\Exception $e) {
abort(500, '遇到了点问题,请刷新页面稍后再试');
abort(500, __('user.order.stripeCard.was_problem'));
}
info($charge);
if (!$charge->paid) {
abort(500, '扣款失败,请检查信用卡信息');
abort(500, __('user.order.stripeCard.deduction_failed'));
}
return $charge->paid;
}

View File

@ -14,7 +14,7 @@ class PlanController extends Controller
$plan = Plan::where('id', $request->input('id'))
->first();
if (!$plan) {
abort(500, '该订阅不存在');
abort(500, __('user.plan.fetch.plan_not_exist'));
}
return response([
'data' => $plan

View File

@ -8,6 +8,7 @@ use App\Http\Requests\User\TicketWithdraw;
use App\Jobs\SendTelegramJob;
use App\Models\User;
use App\Services\TelegramService;
use App\Utils\Dict;
use Illuminate\Http\Request;
use App\Models\Ticket;
use App\Models\TicketMessage;
@ -22,7 +23,7 @@ class TicketController extends Controller
->where('user_id', $request->session()->get('id'))
->first();
if (!$ticket) {
abort(500, '工单不存在');
abort(500, __('user.ticket.fetch.ticket_not_exist'));
}
$ticket['message'] = TicketMessage::where('ticket_id', $ticket->id)->get();
for ($i = 0; $i < count($ticket['message']); $i++) {
@ -55,7 +56,7 @@ class TicketController extends Controller
{
DB::beginTransaction();
if ((int)Ticket::where('status', 0)->where('user_id', $request->session()->get('id'))->count()) {
abort(500, '存在其他工单尚未处理');
abort(500, __('user.ticket.save.exist_other_open_ticket'));
}
$ticket = Ticket::create(array_merge($request->only([
'subject',
@ -66,7 +67,7 @@ class TicketController extends Controller
]));
if (!$ticket) {
DB::rollback();
abort(500, '工单创建失败');
abort(500, __('user.ticket.save.ticket_create_failed'));
}
$ticketMessage = TicketMessage::create([
'user_id' => $request->session()->get('id'),
@ -75,7 +76,7 @@ class TicketController extends Controller
]);
if (!$ticketMessage) {
DB::rollback();
abort(500, '工单创建失败');
abort(500, __('user.ticket.save.ticket_create_failed'));
}
DB::commit();
$this->sendNotify($ticket, $ticketMessage);
@ -87,22 +88,22 @@ class TicketController extends Controller
public function reply(Request $request)
{
if (empty($request->input('id'))) {
abort(500, '参数错误');
abort(500, __('user.ticket.reply.params_wrong'));
}
if (empty($request->input('message'))) {
abort(500, '消息不能为空');
abort(500, __('user.ticket.reply.message_not_empty'));
}
$ticket = Ticket::where('id', $request->input('id'))
->where('user_id', $request->session()->get('id'))
->first();
if (!$ticket) {
abort(500, '工单不存在');
abort(500, __('user.ticket.reply.ticket_not_exist'));
}
if ($ticket->status) {
abort(500, '工单已关闭,无法回复');
abort(500, __('user.ticket.reply.ticket_close_not_reply'));
}
if ($request->session()->get('id') == $this->getLastMessage($ticket->id)->user_id) {
abort(500, '请等待技术支持回复');
abort(500, __('user.ticket.reply.wait_reply'));
}
DB::beginTransaction();
$ticketMessage = TicketMessage::create([
@ -113,7 +114,7 @@ class TicketController extends Controller
$ticket->last_reply_user_id = $request->session()->get('id');
if (!$ticketMessage || !$ticket->save()) {
DB::rollback();
abort(500, '工单回复失败');
abort(500, __('user.ticket.reply.ticket_reply_failed'));
}
DB::commit();
$this->sendNotify($ticket, $ticketMessage);
@ -126,17 +127,17 @@ class TicketController extends Controller
public function close(Request $request)
{
if (empty($request->input('id'))) {
abort(500, '参数错误');
abort(500, __('user.ticket.close.params_wrong'));
}
$ticket = Ticket::where('id', $request->input('id'))
->where('user_id', $request->session()->get('id'))
->first();
if (!$ticket) {
abort(500, '工单不存在');
abort(500, __('user.ticket.close.ticket_not_exist'));
}
$ticket->status = 1;
if (!$ticket->save()) {
abort(500, '关闭失败');
abort(500, __('user.ticket.close.close_failed'));
}
return response([
'data' => true
@ -152,13 +153,25 @@ class TicketController extends Controller
public function withdraw(TicketWithdraw $request)
{
if ((int)config('v2board.withdraw_close_enable', 0)) {
abort(500, 'user.ticket.withdraw.not_support_withdraw');
}
if (!in_array(
$request->input('withdraw_method'),
config(
'v2board.commission_withdraw_method',
Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT
)
)) {
abort(500, __('user.ticket.withdraw.not_support_withdraw_method'));
}
$user = User::find($request->session()->get('id'));
$limit = config('v2board.commission_withdraw_limit', 100);
if ($limit > ($user->commission_balance / 100)) {
abort(500, "当前系统要求的提现门槛佣金需为{$limit}CNY");
abort(500, __('user.ticket.withdraw.system_require_withdraw_limit', ['limit' => $limit]));
}
DB::beginTransaction();
$subject = '[提现申请]本工单由系统发出';
$subject = __('user.ticket.withdraw.ticket_subject');
$ticket = Ticket::create([
'subject' => $subject,
'level' => 2,
@ -167,15 +180,12 @@ class TicketController extends Controller
]);
if (!$ticket) {
DB::rollback();
abort(500, '工单创建失败');
abort(500, __('user.ticket.withdraw.ticket_create_failed'));
}
$methodText = [
'alipay' => '支付宝',
'paypal' => '贝宝(Paypal)',
'usdt' => 'USDT',
'btc' => '比特币'
];
$message = "提现方式:{$methodText[$request->input('withdraw_method')]}\r\n提现账号:{$request->input('withdraw_account')}\r\n";
$message = __('user.ticket.withdraw.ticket_message', [
'method' => $request->input('withdraw_method'),
'account' => $request->input('withdraw_account')
]);
$ticketMessage = TicketMessage::create([
'user_id' => $request->session()->get('id'),
'ticket_id' => $ticket->id,
@ -183,7 +193,7 @@ class TicketController extends Controller
]);
if (!$ticketMessage) {
DB::rollback();
abort(500, '工单创建失败');
abort(500, __('user.ticket.withdraw.ticket_create_failed'));
}
DB::commit();
$this->sendNotify($ticket, $ticketMessage);

View File

@ -27,17 +27,20 @@ class UserController extends Controller
public function changePassword(UserChangePassword $request)
{
$user = User::find($request->session()->get('id'));
if (!$user) {
abort(500, __('user.user.changePassword.user_not_exist'));
}
if (!Helper::multiPasswordVerify(
$user->password_algo,
$request->input('old_password'),
$user->password)
) {
abort(500, '旧密码有误');
abort(500, __('user.user.changePassword.old_password_wrong'));
}
$user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
$user->password_algo = NULL;
if (!$user->save()) {
abort(500, '保存失败');
abort(500, __('user.user.changePassword.save_failed'));
}
$request->session()->flush();
return response([
@ -65,6 +68,9 @@ class UserController extends Controller
'telegram_id'
])
->first();
if (!$user) {
abort(500, __('user.user.info.user_not_exist'));
}
$user['avatar_url'] = 'https://cdn.v2ex.com/gravatar/' . md5($user->email) . '?s=64&d=identicon';
return response([
'data' => $user
@ -90,11 +96,25 @@ class UserController extends Controller
public function getSubscribe(Request $request)
{
$user = User::find($request->session()->get('id'));
$user = User::where('id', $request->session()->get('id'))
->select([
'id',
'plan_id',
'token',
'expired_at',
'u',
'd',
'transfer_enable',
'email'
])
->first();
if (!$user) {
abort(500, __('user.user.getSubscribe.user_not_exist'));
}
if ($user->plan_id) {
$user['plan'] = Plan::find($user->plan_id);
if (!$user['plan']) {
abort(500, '订阅计划不存在');
abort(500, __('user.user.getSubscribe.plan_not_exist'));
}
}
$user['subscribe_url'] = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
@ -107,10 +127,13 @@ class UserController extends Controller
public function resetSecurity(Request $request)
{
$user = User::find($request->session()->get('id'));
if (!$user) {
abort(500, __('user.user.resetSecurity.user_not_exist'));
}
$user->uuid = Helper::guid(true);
$user->token = Helper::guid();
if (!$user->save()) {
abort(500, '重置失败');
abort(500, __('user.user.resetSecurity.reset_failed'));
}
return response([
'data' => config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user->token
@ -126,12 +149,12 @@ class UserController extends Controller
$user = User::find($request->session()->get('id'));
if (!$user) {
abort(500, '该用户不存在');
abort(500, __('user.user.update.user_not_exist'));
}
try {
$user->update($updateData);
} catch (\Exception $e) {
abort(500, '保存失败');
abort(500, __('user.user.update.save_failed'));
}
return response([
@ -143,18 +166,18 @@ class UserController extends Controller
{
$user = User::find($request->session()->get('id'));
if (!$user) {
abort(500, '该用户不存在');
abort(500, __('user.user.transfer.user_not_exist'));
}
if ($request->input('transfer_amount') <= 0) {
abort(500, '参数错误');
abort(500, __('user.user.transfer.params_wrong'));
}
if ($request->input('transfer_amount') > $user->commission_balance) {
abort(500, '推广佣金余额不足');
abort(500, __('user.user.transfer.insufficient_commission_balance'));
}
$user->commission_balance = $user->commission_balance - $request->input('transfer_amount');
$user->balance = $user->balance + $request->input('transfer_amount');
if (!$user->save()) {
abort(500, '划转失败');
abort(500, __('user.user.transfer.transfer_failed'));
}
return response([
'data' => true
@ -172,6 +195,9 @@ class UserController extends Controller
return $lastDay - $today;
}
if ((int)config('v2board.reset_traffic_method') === 1) {
if ((int)$day >= (int)$today && (int)$day >= (int)$lastDay) {
return $lastDay - $today;
}
if ((int)$day >= (int)$today) {
return $day - $today;
} else {

View File

@ -43,6 +43,7 @@ class Kernel extends HttpKernel
\Illuminate\Session\Middleware\StartSession::class,
\App\Http\Middleware\ForceJson::class,
\App\Http\Middleware\CORS::class,
\App\Http\Middleware\Language::class,
'bindings',
],
];

View File

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\App;
class Language
{
public function handle($request, Closure $next)
{
if ($request->header('content-language')) {
App::setLocale($request->header('content-language'));
}
return $next($request);
}
}

View File

@ -22,6 +22,12 @@ class User
$request->session()->put('id', $user->id);
}
}
// if ($request->input('lang')) {
// $request->session()->put('lang', $request->input('lang'));
// }
// if ($request->session()->get('lang')) {
// App::setLocale($request->session()->get('lang'));
// }
if (!$request->session()->get('id')) {
abort(403, '未登录或登陆已过期');
}

View File

@ -23,6 +23,8 @@ class ConfigSave extends FormRequest
'commission_first_time_enable' => 'in:0,1',
'commission_auto_check_enable' => 'in:0,1',
'commission_withdraw_limit' => 'nullable|numeric',
'commission_withdraw_method' => 'nullable|array',
'withdraw_close_enable' => 'in:0,1',
// site
'stop_register' => 'in:0,1',
'email_verify' => 'in:0,1',
@ -34,7 +36,7 @@ class ConfigSave extends FormRequest
'try_out_plan_id' => 'integer',
'try_out_hour' => 'numeric',
'email_whitelist_enable' => 'in:0,1',
'email_whitelist_suffix' => '',
'email_whitelist_suffix' => 'nullable|array',
'email_gmail_limit_enable' => 'in:0,1',
'recaptcha_enable' => 'in:0,1',
'recaptcha_key' => '',

View File

@ -14,7 +14,7 @@ class UserFetch extends FormRequest
public function rules()
{
return [
'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id',
'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned',
'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊',
'filter.*.value' => 'required'
];

View File

@ -27,7 +27,8 @@ class UserUpdate extends FormRequest
'u' => 'integer',
'd' => 'integer',
'balance' => 'integer',
'commission_balance' => 'integer'
'commission_balance' => 'integer',
'remarks' => 'nullable'
];
}

View File

@ -14,7 +14,7 @@ class TicketWithdraw extends FormRequest
public function rules()
{
return [
'withdraw_method' => 'required|in:alipay,paypal,usdt,btc',
'withdraw_method' => 'required',
'withdraw_account' => 'required'
];
}

View File

@ -73,8 +73,11 @@ class AdminRoute
$router->post('/user/dumpCSV', 'Admin\\UserController@dumpCSV');
$router->post('/user/sendMail', 'Admin\\UserController@sendMail');
$router->post('/user/ban', 'Admin\\UserController@ban');
// Stat
$router->post('/user/resetSecret', 'Admin\\UserController@resetSecret');
// StatOrder
$router->get ('/stat/getOverride', 'Admin\\StatController@getOverride');
$router->get ('/stat/getServerLastRank', 'Admin\\StatController@getServerLastRank');
$router->get ('/stat/getOrder', 'Admin\\StatController@getOrder');
// Notice
$router->get ('/notice/fetch', 'Admin\\NoticeController@fetch');
$router->post('/notice/save', 'Admin\\NoticeController@save');

View File

@ -0,0 +1,54 @@
<?php
namespace App\Jobs;
use App\Models\StatServer;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class StatServerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $statistic;
public $tries = 3;
public $timeout = 5;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(array $statistic)
{
$this->onQueue('stat_server');
$this->statistic = $statistic;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$statistic = $this->statistic;
$data = StatServer::where('record_at', $statistic['record_at'])
->where('server_id', $statistic['server_id'])
->first();
if ($data) {
try {
$data->update($statistic);
} catch (\Exception $e) {
abort(500, '节点统计数据更新失败');
}
} else {
if (!StatServer::create($statistic)) {
abort(500, '节点统计数据创建失败');
}
}
}
}

View File

@ -4,9 +4,9 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Tutorial extends Model
class StatOrder extends Model
{
protected $table = 'v2_tutorial';
protected $table = 'v2_stat_order';
protected $dateFormat = 'U';
protected $guarded = ['id'];
}

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

@ -0,0 +1,12 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class StatServer extends Model
{
protected $table = 'v2_stat_server';
protected $dateFormat = 'U';
protected $guarded = ['id'];
}

View File

@ -126,7 +126,7 @@ class ServerService
->get();
}
public function getVmessConfig(int $nodeId, int $localPort)
public function getV2RayConfig(int $nodeId, int $localPort)
{
$server = Server::find($nodeId);
if (!$server) {

View File

@ -15,4 +15,9 @@ class Dict
'yeah.net',
'foxmail.com'
];
CONST WITHDRAW_METHOD_WHITELIST_DEFAULT = [
'支付宝',
'USDT',
'Paypal'
];
}

View File

@ -33,33 +33,32 @@ class QuantumultX
];
if ($server['tls']) {
if ($server['network'] === 'tcp') {
if ($server['network'] === 'tcp')
array_push($config, 'obfs=over-tls');
} else {
array_push($config, 'obfs=wss');
}
} else if ($server['network'] === 'ws') {
array_push($config, 'obfs=ws');
}
if ($server['tls']) {
if ($server['tlsSettings']) {
$tlsSettings = json_decode($server['tlsSettings'], true);
if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
array_push($config, 'tls-verification=' . ($tlsSettings['allowInsecure'] ? 'false' : 'true'));
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
array_push($config, "tls-host={$tlsSettings['serverName']}");
$host = $tlsSettings['serverName'];
}
}
if ($server['network'] === 'ws') {
if ($server['tls'])
array_push($config, 'obfs=wss');
else
array_push($config, 'obfs=ws');
if ($server['networkSettings']) {
$wsSettings = json_decode($server['networkSettings'], true);
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
array_push($config, "obfs-uri={$wsSettings['path']}");
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
array_push($config, "obfs-host={$wsSettings['headers']['Host']}");
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']) && !isset($host))
$host = $wsSettings['headers']['Host'];
}
}
if (isset($host)) {
array_push($config, "obfs-host={$host}");
}
$uri = implode(',', $config);
$uri .= "\r\n";

View File

@ -21,7 +21,7 @@ class URLSchemes
{
$config = [
"id" => $server['id'],
"remark" => $server['name'],
"remarks" => $server['name'],
"server" => $server['host'],
"server_port" => $server['port'],
"password" => $user['uuid'],

View File

@ -80,7 +80,7 @@ return [
|
*/
'locale' => 'en',
'locale' => 'zh-CN',
/*
|--------------------------------------------------------------------------
@ -93,7 +93,7 @@ return [
|
*/
'fallback_locale' => 'en',
'fallback_locale' => 'zh-CN',
/*
|--------------------------------------------------------------------------
@ -106,7 +106,7 @@ return [
|
*/
'faker_locale' => 'en_US',
'faker_locale' => 'zh-CN',
/*
|--------------------------------------------------------------------------
@ -236,5 +236,5 @@ return [
| The only modification by laravel config
|
*/
'version' => '1.4.2.1607914998'
'version' => '1.4.3.1612347430'
];

View File

@ -1,4 +1,4 @@
-- Adminer 4.7.6 MySQL dump
-- Adminer 4.7.8 MySQL dump
SET NAMES utf8;
SET time_zone = '+00:00';
@ -9,305 +9,326 @@ SET NAMES utf8mb4;
DROP TABLE IF EXISTS `failed_jobs`;
CREATE TABLE `failed_jobs` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
`queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
`payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
`failed_at` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
DROP TABLE IF EXISTS `v2_coupon`;
CREATE TABLE `v2_coupon` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
`type` tinyint(1) NOT NULL,
`value` int(11) NOT NULL,
`limit_use` int(11) DEFAULT NULL,
`limit_plan_ids` varchar(255) DEFAULT NULL,
`started_at` int(11) NOT NULL,
`ended_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
`type` tinyint(1) NOT NULL,
`value` int(11) NOT NULL,
`limit_use` int(11) DEFAULT NULL,
`limit_plan_ids` varchar(255) DEFAULT NULL,
`started_at` int(11) NOT NULL,
`ended_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_invite_code`;
CREATE TABLE `v2_invite_code` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`code` char(32) NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '0',
`pv` int(11) NOT NULL DEFAULT '0',
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`code` char(32) NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT 0,
`pv` int(11) NOT NULL DEFAULT 0,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_knowledge`;
CREATE TABLE `v2_knowledge` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`language` char(5) NOT NULL COMMENT '語言',
`category` varchar(255) NOT NULL COMMENT '分類名',
`title` varchar(255) NOT NULL COMMENT '標題',
`body` text NOT NULL COMMENT '內容',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '顯示',
`created_at` int(11) NOT NULL COMMENT '創建時間',
`updated_at` int(11) NOT NULL COMMENT '更新時間',
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`language` char(5) NOT NULL COMMENT '語言',
`category` varchar(255) NOT NULL COMMENT '分類名',
`title` varchar(255) NOT NULL COMMENT '標題',
`body` text NOT NULL COMMENT '內容',
`sort` int(11) DEFAULT NULL COMMENT '排序',
`show` tinyint(1) NOT NULL DEFAULT 0 COMMENT '顯示',
`created_at` int(11) NOT NULL COMMENT '創建時間',
`updated_at` int(11) NOT NULL COMMENT '更新時間',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知識庫';
DROP TABLE IF EXISTS `v2_mail_log`;
CREATE TABLE `v2_mail_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(64) NOT NULL,
`subject` varchar(255) NOT NULL,
`template_name` varchar(255) NOT NULL,
`error` text,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(64) NOT NULL,
`subject` varchar(255) NOT NULL,
`template_name` varchar(255) NOT NULL,
`error` text DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_notice`;
CREATE TABLE `v2_notice` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
`img_url` varchar(255) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
`img_url` varchar(255) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_order`;
CREATE TABLE `v2_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`invite_user_id` int(11) DEFAULT NULL,
`user_id` int(11) NOT NULL,
`plan_id` int(11) NOT NULL,
`coupon_id` int(11) DEFAULT NULL,
`type` int(11) NOT NULL COMMENT '1新购2续费3升级',
`cycle` varchar(255) NOT NULL,
`trade_no` varchar(36) NOT NULL,
`callback_no` varchar(255) DEFAULT NULL,
`total_amount` int(11) NOT NULL,
`discount_amount` int(11) DEFAULT NULL,
`surplus_amount` int(11) DEFAULT NULL COMMENT '剩余价值',
`refund_amount` int(11) DEFAULT NULL COMMENT '退款金额',
`balance_amount` int(11) DEFAULT NULL COMMENT '使用余额',
`surplus_order_ids` text COMMENT '折抵订单',
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待支付1开通中2已取消3已完成4已折抵',
`commission_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待确认1发放中2有效3无效',
`commission_balance` int(11) NOT NULL DEFAULT '0',
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`invite_user_id` int(11) DEFAULT NULL,
`user_id` int(11) NOT NULL,
`plan_id` int(11) NOT NULL,
`coupon_id` int(11) DEFAULT NULL,
`type` int(11) NOT NULL COMMENT '1新购2续费3升级',
`cycle` varchar(255) NOT NULL,
`trade_no` varchar(36) NOT NULL,
`callback_no` varchar(255) DEFAULT NULL,
`total_amount` int(11) NOT NULL,
`discount_amount` int(11) DEFAULT NULL,
`surplus_amount` int(11) DEFAULT NULL COMMENT '剩余价值',
`refund_amount` int(11) DEFAULT NULL COMMENT '退款金额',
`balance_amount` int(11) DEFAULT NULL COMMENT '使用余额',
`surplus_order_ids` text DEFAULT NULL COMMENT '折抵订单',
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0待支付1开通中2已取消3已完成4已折抵',
`commission_status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0待确认1发放中2有效3无效',
`commission_balance` int(11) NOT NULL DEFAULT 0,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_plan`;
CREATE TABLE `v2_plan` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` int(11) NOT NULL,
`transfer_enable` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`show` tinyint(1) NOT NULL DEFAULT '0',
`sort` int(11) DEFAULT NULL,
`renew` tinyint(1) NOT NULL DEFAULT '1',
`content` text,
`month_price` int(11) DEFAULT NULL,
`quarter_price` int(11) DEFAULT NULL,
`half_year_price` int(11) DEFAULT NULL,
`year_price` int(11) DEFAULT NULL,
`two_year_price` int(11) DEFAULT NULL,
`three_year_price` int(11) DEFAULT NULL,
`onetime_price` int(11) DEFAULT NULL,
`reset_price` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` int(11) NOT NULL,
`transfer_enable` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`show` tinyint(1) NOT NULL DEFAULT 0,
`sort` int(11) DEFAULT NULL,
`renew` tinyint(1) NOT NULL DEFAULT 1,
`content` text DEFAULT NULL,
`month_price` int(11) DEFAULT NULL,
`quarter_price` int(11) DEFAULT NULL,
`half_year_price` int(11) DEFAULT NULL,
`year_price` int(11) DEFAULT NULL,
`two_year_price` int(11) DEFAULT NULL,
`three_year_price` int(11) DEFAULT NULL,
`onetime_price` int(11) DEFAULT NULL,
`reset_price` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_server`;
CREATE TABLE `v2_server` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` varchar(255) NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
`parent_id` int(11) DEFAULT NULL,
`host` varchar(255) NOT NULL,
`port` int(11) NOT NULL,
`server_port` int(11) NOT NULL,
`tls` tinyint(4) NOT NULL DEFAULT '0',
`tags` varchar(255) DEFAULT NULL,
`rate` varchar(11) NOT NULL,
`network` text NOT NULL,
`alter_id` int(11) NOT NULL DEFAULT '1',
`settings` text,
`rules` text,
`networkSettings` text,
`tlsSettings` text,
`ruleSettings` text,
`dnsSettings` text,
`show` tinyint(1) NOT NULL DEFAULT '0',
`sort` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` varchar(255) NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
`parent_id` int(11) DEFAULT NULL,
`host` varchar(255) NOT NULL,
`port` int(11) NOT NULL,
`server_port` int(11) NOT NULL,
`tls` tinyint(4) NOT NULL DEFAULT 0,
`tags` varchar(255) DEFAULT NULL,
`rate` varchar(11) NOT NULL,
`network` text NOT NULL,
`alter_id` int(11) NOT NULL DEFAULT 1,
`settings` text DEFAULT NULL,
`rules` text DEFAULT NULL,
`networkSettings` text DEFAULT NULL,
`tlsSettings` text DEFAULT NULL,
`ruleSettings` text DEFAULT NULL,
`dnsSettings` text DEFAULT NULL,
`show` tinyint(1) NOT NULL DEFAULT 0,
`sort` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_server_group`;
CREATE TABLE `v2_server_group` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_server_log`;
CREATE TABLE `v2_server_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`server_id` int(11) NOT NULL,
`u` varchar(255) NOT NULL,
`d` varchar(255) NOT NULL,
`rate` decimal(10,2) NOT NULL,
`method` varchar(255) NOT NULL,
`log_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `log_at` (`log_at`)
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`server_id` int(11) NOT NULL,
`u` varchar(255) NOT NULL,
`d` varchar(255) NOT NULL,
`rate` decimal(10,2) NOT NULL,
`method` varchar(255) NOT NULL,
`log_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `log_at` (`log_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_server_shadowsocks`;
CREATE TABLE `v2_server_shadowsocks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` varchar(255) NOT NULL,
`parent_id` int(11) DEFAULT NULL,
`tags` varchar(255) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`rate` varchar(11) NOT NULL,
`host` varchar(255) NOT NULL,
`port` int(11) NOT NULL,
`server_port` int(11) NOT NULL,
`cipher` varchar(255) NOT NULL,
`show` tinyint(4) NOT NULL DEFAULT '0',
`sort` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` varchar(255) NOT NULL,
`parent_id` int(11) DEFAULT NULL,
`tags` varchar(255) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`rate` varchar(11) NOT NULL,
`host` varchar(255) NOT NULL,
`port` int(11) NOT NULL,
`server_port` int(11) NOT NULL,
`cipher` varchar(255) NOT NULL,
`show` tinyint(4) NOT NULL DEFAULT 0,
`sort` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `v2_server_stat`;
CREATE TABLE `v2_server_stat` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`server_id` int(11) NOT NULL,
`method` varchar(255) NOT NULL,
`u` varchar(255) NOT NULL,
`d` varchar(255) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_server_trojan`;
CREATE TABLE `v2_server_trojan` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '节点ID',
`group_id` varchar(255) NOT NULL COMMENT '节点组',
`parent_id` int(11) DEFAULT NULL COMMENT '父节点',
`tags` varchar(255) DEFAULT NULL COMMENT '节点标签',
`name` varchar(255) NOT NULL COMMENT '节点名称',
`rate` varchar(11) NOT NULL COMMENT '倍率',
`host` varchar(255) NOT NULL COMMENT '主机名',
`port` int(11) NOT NULL COMMENT '连接端口',
`server_port` int(11) NOT NULL COMMENT '服务端口',
`allow_insecure` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否允许不安全',
`server_name` varchar(255) DEFAULT NULL,
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示',
`sort` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '节点ID',
`group_id` varchar(255) NOT NULL COMMENT '节点组',
`parent_id` int(11) DEFAULT NULL COMMENT '父节点',
`tags` varchar(255) DEFAULT NULL COMMENT '节点标签',
`name` varchar(255) NOT NULL COMMENT '节点名称',
`rate` varchar(11) NOT NULL COMMENT '倍率',
`host` varchar(255) NOT NULL COMMENT '主机名',
`port` int(11) NOT NULL COMMENT '连接端口',
`server_port` int(11) NOT NULL COMMENT '服务端口',
`allow_insecure` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否允许不安全',
`server_name` varchar(255) DEFAULT NULL,
`show` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否显示',
`sort` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='trojan伺服器表';
DROP TABLE IF EXISTS `v2_stat_order`;
CREATE TABLE `v2_stat_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_count` int(11) NOT NULL COMMENT '订单数量',
`order_amount` int(11) NOT NULL COMMENT '订单合计',
`commission_count` int(11) NOT NULL,
`commission_amount` int(11) NOT NULL COMMENT '佣金合计',
`record_type` char(1) NOT NULL,
`record_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `record_at` (`record_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单统计';
DROP TABLE IF EXISTS `v2_stat_server`;
CREATE TABLE `v2_stat_server` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`server_id` int(11) NOT NULL COMMENT '节点id',
`server_type` char(11) NOT NULL COMMENT '节点类型',
`u` varchar(255) NOT NULL,
`d` varchar(255) NOT NULL,
`record_type` char(1) NOT NULL COMMENT 'd day m month',
`record_at` int(11) NOT NULL COMMENT '记录时间',
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `server_id_server_type_record_at` (`server_id`,`server_type`,`record_at`),
KEY `record_at` (`record_at`),
KEY `server_id` (`server_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='节点数据统计';
DROP TABLE IF EXISTS `v2_ticket`;
CREATE TABLE `v2_ticket` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`last_reply_user_id` int(11) NOT NULL,
`subject` varchar(255) NOT NULL,
`level` tinyint(1) NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0:已开启 1:已关闭',
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`last_reply_user_id` int(11) NOT NULL,
`subject` varchar(255) NOT NULL,
`level` tinyint(1) NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '0:已开启 1:已关闭',
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_ticket_message`;
CREATE TABLE `v2_ticket_message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`ticket_id` int(11) NOT NULL,
`message` varchar(255) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`ticket_id` int(11) NOT NULL,
`message` varchar(255) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_user`;
CREATE TABLE `v2_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`invite_user_id` int(11) DEFAULT NULL,
`telegram_id` bigint(20) DEFAULT NULL,
`email` varchar(64) NOT NULL,
`password` varchar(64) NOT NULL,
`password_algo` char(10) DEFAULT NULL,
`balance` int(11) NOT NULL DEFAULT '0',
`discount` int(11) DEFAULT NULL,
`commission_rate` int(11) DEFAULT NULL,
`commission_balance` int(11) NOT NULL DEFAULT '0',
`t` int(11) NOT NULL DEFAULT '0',
`u` bigint(20) NOT NULL DEFAULT '0',
`d` bigint(20) NOT NULL DEFAULT '0',
`transfer_enable` bigint(20) NOT NULL DEFAULT '0',
`banned` tinyint(1) NOT NULL DEFAULT '0',
`is_admin` tinyint(1) NOT NULL DEFAULT '0',
`is_staff` tinyint(1) NOT NULL DEFAULT '0',
`last_login_at` int(11) DEFAULT NULL,
`last_login_ip` int(11) DEFAULT NULL,
`uuid` varchar(36) NOT NULL,
`group_id` int(11) DEFAULT NULL,
`plan_id` int(11) DEFAULT NULL,
`remind_expire` tinyint(4) DEFAULT '1',
`remind_traffic` tinyint(4) DEFAULT '1',
`token` char(32) NOT NULL,
`expired_at` bigint(20) DEFAULT '0',
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`invite_user_id` int(11) DEFAULT NULL,
`telegram_id` bigint(20) DEFAULT NULL,
`email` varchar(64) NOT NULL,
`password` varchar(64) NOT NULL,
`password_algo` char(10) DEFAULT NULL,
`balance` int(11) NOT NULL DEFAULT 0,
`discount` int(11) DEFAULT NULL,
`commission_rate` int(11) DEFAULT NULL,
`commission_balance` int(11) NOT NULL DEFAULT 0,
`t` int(11) NOT NULL DEFAULT 0,
`u` bigint(20) NOT NULL DEFAULT 0,
`d` bigint(20) NOT NULL DEFAULT 0,
`transfer_enable` bigint(20) NOT NULL DEFAULT 0,
`banned` tinyint(1) NOT NULL DEFAULT 0,
`is_admin` tinyint(1) NOT NULL DEFAULT 0,
`is_staff` tinyint(1) NOT NULL DEFAULT 0,
`last_login_at` int(11) DEFAULT NULL,
`last_login_ip` int(11) DEFAULT NULL,
`uuid` varchar(36) NOT NULL,
`group_id` int(11) DEFAULT NULL,
`plan_id` int(11) DEFAULT NULL,
`remind_expire` tinyint(4) DEFAULT 1,
`remind_traffic` tinyint(4) DEFAULT 1,
`token` char(32) NOT NULL,
`remarks` text DEFAULT NULL,
`expired_at` bigint(20) DEFAULT 0,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 2020-11-17 10:46:49
-- 2021-01-21 14:25:17

View File

@ -352,3 +352,45 @@ ADD `alter_id` int(11) NOT NULL DEFAULT '1' AFTER `network`;
ALTER TABLE `v2_user`
DROP `v2ray_alter_id`,
DROP `v2ray_level`;
DROP TABLE `v2_server_stat`;
CREATE TABLE `v2_stat_server` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`server_id` int(11) NOT NULL COMMENT '节点id',
`server_type` char(11) NOT NULL COMMENT '节点类型',
`u` varchar(255) NOT NULL,
`d` varchar(255) NOT NULL,
`record_type` char(1) NOT NULL COMMENT 'd day m month',
`record_at` int(11) NOT NULL COMMENT '记录时间',
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='节点数据统计';
ALTER TABLE `v2_stat_server`
ADD UNIQUE `server_id_server_type_record_at` (`server_id`, `server_type`, `record_at`);
ALTER TABLE `v2_stat_server`
ADD INDEX `record_at` (`record_at`),
ADD INDEX `server_id` (`server_id`);
CREATE TABLE `v2_stat_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_count` int(11) NOT NULL COMMENT '订单数量',
`order_amount` int(11) NOT NULL COMMENT '订单合计',
`commission_count` int(11) NOT NULL,
`commission_amount` int(11) NOT NULL COMMENT '佣金合计',
`record_type` char(1) NOT NULL,
`record_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `record_at` (`record_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单统计';
ALTER TABLE `v2_user`
DROP `enable`;
ALTER TABLE `v2_user`
ADD `remarks` text COLLATE 'utf8_general_ci' NULL AFTER `token`;

View File

@ -1,5 +1,5 @@
apps:
- name : 'V2Board'
script : 'php artisan queue:work --queue=send_email,send_telegram'
script : 'php artisan queue:work --queue=send_email,send_telegram,stat_server'
instances: 4
out_file : './storage/logs/queue/queue.log'

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,37 @@
<?php
return [
'auth' => [
'register' => [
'verify_incorrect' => 'Invalid code is incorrect',
'email_suffix_not_in_whitelist' => 'Email suffix is not in the Whitelist',
'no_support_gmail_alias' => 'Gmail alias is not supported',
'close_register' => 'Registration has closed',
'must_use_invite_code' => 'You must use the invitation code to register',
'email_code_not_empty' => 'Email verification code cannot be empty',
'email_code_incorrect' => 'Incorrect email verification code',
'email_exist_system' => 'Email already exists',
'invalid_invite_code' => 'Invalid invitation code',
'register_failed' => 'Register failed'
],
'login' => [
'wrong_email_or_password' => 'Incorrect email or password',
'account_been_discontinued' => 'Your account has been suspended'
],
'getQuickLoginUrl' => [
'wrong_token' => 'Token error'
],
'forget' => [
'email_verification_code_incorrect' => 'Incorrect email verification code',
'email_not_exist_system' => 'This email is not registered in the system',
'reset_failed' => 'Reset failed'
]
],
'comm' => [
'sendEmailVerify' => [
'verification_code_incorrect' => 'Incorrect email verification code',
'code_sent_request_later' => 'Email verification code has been sent, please request again later',
'email_verification_code' => 'Email verification code'
]
]
];

View File

@ -0,0 +1,132 @@
<?php
return [
'user' => [
'changePassword' => [
'user_not_exist' => 'The user does not exist',
'old_password_wrong' => 'The old password is wrong',
'save_failed' => 'Save failed'
],
'info' => [
'user_not_exist' => 'The user does not exist'
],
'getSubscribe' => [
'user_not_exist' => 'The user does not exist',
'plan_not_exist' => 'Subscription plan does not exist',
],
'resetSecurity' => [
'user_not_exist' => 'The user does not exist',
'reset_failed' => 'Reset failed'
],
'update' => [
'user_not_exist' => 'The user does not exist',
'save_failed' => 'Save failed',
],
'transfer' => [
'user_not_exist' => 'The user does not exist',
'params_wrong' => 'Invalid parameter',
'insufficient_commission_balance' => 'Insufficient commission balance',
'transfer_failed' => 'Transfer failed'
]
],
'ticket' => [
'fetch' => [
'ticket_not_exist' => 'Ticket does not exist',
],
'save' => [
'exist_other_open_ticket' => 'There are other unresolved tickets',
'ticket_create_failed' => 'Failed to open ticket',
],
'reply' => [
'params_wrong' => 'Invalid parameter',
'message_not_empty' => 'Message cannot be empty',
'ticket_not_exist' => 'Ticket does not exist',
'ticket_close_not_reply' => 'The ticket is closed and cannot be replied',
'wait_reply' => 'Please wait for the technical enginneer to reply',
'ticket_reply_failed' => 'Ticket reply failed',
],
'close' => [
'params_wrong' => 'Invalid parameter',
'ticket_not_exist' => 'Ticket does not exist',
'close_failed' => 'Close failed',
],
'withdraw' => [
'not_support_withdraw_method' => 'Unsupported withdrawal method',
'system_require_withdraw_limit' => 'The current required minimum withdrawal commission is: ¥:limitCNY',
'ticket_subject' => '[Commission Withdrawal Request] This ticket is opened by the system',
'ticket_create_failed' => 'Failed to open ticket',
'ticket_message' => "Withdrawal method: :method\r\nPayment account: :account\r\n",
'not_support_withdraw' => 'Unsupported withdrawal'
]
],
'plan' => [
'fetch' => [
'plan_not_exist' => 'Subscription plan does not exist'
]
],
'order' => [
'details' => [
'order_not_exist' => 'Order does not exist',
'plan_not_exist' => 'Subscription plan does not exist',
],
'save' => [
'plan_not_exist' => 'Subscription plan does not exist',
'exist_open_order' => 'You have an unpaid or pending order, please try again later or cancel it',
'plan_stop_sell' => 'This subscription has been sold out, please choose another subscription',
'plan_stop_renew' => 'This subscription cannot be renewed, please change to another subscription',
'plan_stop' => 'This payment cycle cannot be purchased, please choose another cycle',
'plan_exist_not_buy_package' => 'Subscription has expired or no active subscription, unable to purchase Data Reset Package',
'plan_expired' => 'This subscription has expired, please change to another subscription',
'coupon_use_failed' => 'Invalid coupon',
'insufficient_balance' => 'Insufficient balance',
'order_create_failed' => 'Failed to create order'
],
'checkout' => [
'order_not_exist_or_paid' => 'Order does not exist or has been paid',
'pay_method_not_use' => 'Payment method is not available',
],
'check' => [
'order_not_exist' => 'Order does not exist'
],
'cancel' => [
'params_wrong' => 'Invalid parameter',
'order_not_exist' => 'Order does not exist',
'only_cancel_pending_order' => 'You can only cancel pending orders',
'cancel_failed' => 'Cancel failed',
],
'stripeAlipay' => [
'currency_convert_timeout' => 'Currency conversion has timed out, please try again later',
'gateway_request_failed' => 'Payment gateway request failed',
],
'stripeWepay' => [
'currency_convert_timeout' => 'Currency conversion has timed out, please try again later',
'gateway_request_failed' => 'Payment gateway request failed',
],
'stripeCard' => [
'currency_convert_timeout' => 'Currency conversion has timed out, please try again later',
'was_problem' => 'Oops, there's a problem... Please refresh the page and try again later',
'deduction_failed' => 'Payment failed. Please check your credit card information'
]
],
'knowledge' => [
'fetch' => [
'knowledge_not_exist' => 'Article does not exist',
'apple_id_must_be_plan' => 'No active subscription. Unable to use our provided Apple ID'
]
],
'invite' => [
'save' => [
'invite_create_limit' => 'The maximum number of creations has been reached'
]
],
'coupon' => [
'check' => [
'coupon_not_empty' => 'Coupon cannot be empty',
'coupon_invalid' => 'Invalid coupon',
'coupon_not_available_by_number' => 'This coupon is no longer available',
'coupon_not_available_by_time' => 'This coupon has not yet started',
'coupon_expired' => 'This coupon has expired',
'coupon_limit_plan' => 'The coupon code cannot be used for this subscription'
]
]
];

View File

@ -1,19 +0,0 @@
<?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.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
];

View File

@ -1,19 +0,0 @@
<?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;',
];

View File

@ -1,21 +0,0 @@
<?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
| has failed, such as for an invalid token or invalid new password.
|
*/
'reset' => 'Your password has been reset!',
'sent' => 'We have e-mailed your password reset link!',
'token' => 'This password reset token is invalid.',
'user' => "We can't find a user with that e-mail address.",
];

View File

@ -1,150 +0,0 @@
<?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 must be accepted.',
'active_url' => 'The :attribute is not a valid URL.',
'after' => 'The :attribute must be a date after :date.',
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
'alpha' => 'The :attribute may only contain letters.',
'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.',
'alpha_num' => 'The :attribute may only contain letters and numbers.',
'array' => 'The :attribute must be an array.',
'before' => 'The :attribute must be a date before :date.',
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
'between' => [
'numeric' => 'The :attribute must be between :min and :max.',
'file' => 'The :attribute must be between :min and :max kilobytes.',
'string' => 'The :attribute must be between :min and :max characters.',
'array' => 'The :attribute must have between :min and :max items.',
],
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'date' => 'The :attribute is not a valid date.',
'date_equals' => 'The :attribute must be a date equal to :date.',
'date_format' => 'The :attribute does not match the format :format.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
'digits_between' => 'The :attribute must be between :min and :max digits.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'email' => 'The :attribute must be a valid email address.',
'ends_with' => 'The :attribute must end with one of the following: :values',
'exists' => 'The selected :attribute is invalid.',
'file' => 'The :attribute must be a file.',
'filled' => 'The :attribute field must have a value.',
'gt' => [
'numeric' => 'The :attribute must be greater than :value.',
'file' => 'The :attribute must be greater than :value kilobytes.',
'string' => 'The :attribute must be greater than :value characters.',
'array' => 'The :attribute must have more than :value items.',
],
'gte' => [
'numeric' => 'The :attribute must be greater than or equal :value.',
'file' => 'The :attribute must be greater than or equal :value kilobytes.',
'string' => 'The :attribute must be greater than or equal :value characters.',
'array' => 'The :attribute must have :value items or more.',
],
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field does not exist in :other.',
'integer' => 'The :attribute must be an integer.',
'ip' => 'The :attribute must be a valid IP address.',
'ipv4' => 'The :attribute must be a valid IPv4 address.',
'ipv6' => 'The :attribute must be a valid IPv6 address.',
'json' => 'The :attribute must be a valid JSON string.',
'lt' => [
'numeric' => 'The :attribute must be less than :value.',
'file' => 'The :attribute must be less than :value kilobytes.',
'string' => 'The :attribute must be less than :value characters.',
'array' => 'The :attribute must have less than :value items.',
],
'lte' => [
'numeric' => 'The :attribute must be less than or equal :value.',
'file' => 'The :attribute must be less than or equal :value kilobytes.',
'string' => 'The :attribute must be less than or equal :value characters.',
'array' => 'The :attribute must not have more than :value items.',
],
'max' => [
'numeric' => 'The :attribute may not be greater than :max.',
'file' => 'The :attribute may not be greater than :max kilobytes.',
'string' => 'The :attribute may not be greater than :max characters.',
'array' => 'The :attribute may not have more than :max items.',
],
'mimes' => 'The :attribute must be a file of type: :values.',
'mimetypes' => 'The :attribute must be a file of type: :values.',
'min' => [
'numeric' => 'The :attribute must be at least :min.',
'file' => 'The :attribute must be at least :min kilobytes.',
'string' => 'The :attribute must be at least :min characters.',
'array' => 'The :attribute must have at least :min items.',
],
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute format is invalid.',
'numeric' => 'The :attribute must be a number.',
'present' => 'The :attribute field must be present.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',
'required_if' => 'The :attribute field is required when :other is :value.',
'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 and :other must match.',
'size' => [
'numeric' => 'The :attribute must be :size.',
'file' => 'The :attribute must be :size kilobytes.',
'string' => 'The :attribute must be :size characters.',
'array' => 'The :attribute must contain :size items.',
],
'starts_with' => 'The :attribute must start with one of the following: :values',
'string' => 'The :attribute must be a string.',
'timezone' => 'The :attribute must be a valid zone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute format is invalid.',
'uuid' => 'The :attribute 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' => [],
];

View File

@ -0,0 +1,37 @@
<?php
return [
'auth' => [
'register' => [
'verify_incorrect' => '验证码有误',
'email_suffix_not_in_whitelist' => '邮箱后缀不处于白名单中',
'no_support_gmail_alias' => '不支持 Gmail 别名邮箱',
'close_register' => '本站已关闭注册',
'must_use_invite_code' => '必须使用邀请码才可以注册',
'email_code_not_empty' => '邮箱验证码不能为空',
'email_code_incorrect' => '邮箱验证码有误',
'email_exist_system' => '邮箱已存在系统中',
'invalid_invite_code' => '邀请码无效',
'register_failed' => '注册失败'
],
'login' => [
'wrong_email_or_password' => '邮箱或密码错误',
'account_been_discontinued' => '该账户已被停止使用'
],
'getQuickLoginUrl' => [
'wrong_token' => '令牌有误'
],
'forget' => [
'email_verification_code_incorrect' => '邮箱验证码有误',
'email_not_exist_system' => '该邮箱不存在系统中',
'reset_failed' => '重置失败'
]
],
'comm' => [
'sendEmailVerify' => [
'verification_code_incorrect' => '验证码有误',
'code_sent_request_later' => '验证码已发送,请过一会再请求',
'email_verification_code' => '邮箱验证码'
]
]
];

View File

@ -0,0 +1,132 @@
<?php
return [
'user' => [
'changePassword' => [
'user_not_exist' => '该用户不存在',
'old_password_wrong' => '旧密码有误',
'save_failed' => '保存失败'
],
'info' => [
'user_not_exist' => '该用户不存在'
],
'getSubscribe' => [
'user_not_exist' => '该用户不存在',
'plan_not_exist' => '订阅计划不存在',
],
'resetSecurity' => [
'user_not_exist' => '该用户不存在',
'reset_failed' => '重置失败'
],
'update' => [
'user_not_exist' => '该用户不存在',
'save_failed' => '保存失败',
],
'transfer' => [
'user_not_exist' => '该用户不存在',
'params_wrong' => '参数错误',
'insufficient_commission_balance' => '推广佣金余额不足',
'transfer_failed' => '划转失败'
]
],
'ticket' => [
'fetch' => [
'ticket_not_exist' => '工单不存在',
],
'save' => [
'exist_other_open_ticket' => '存在其它工单尚未处理',
'ticket_create_failed' => '工单创建失败',
],
'reply' => [
'params_wrong' => '参数错误',
'message_not_empty' => '消息不能为空',
'ticket_not_exist' => '工单不存在',
'ticket_close_not_reply' => '工单已关闭,无法回复',
'wait_reply' => '请等待技术支持回复',
'ticket_reply_failed' => '工单回复失败',
],
'close' => [
'params_wrong' => '参数错误',
'ticket_not_exist' => '工单不存在',
'close_failed' => '关闭失败',
],
'withdraw' => [
'not_support_withdraw_method' => '不支持的提现方式',
'system_require_withdraw_limit' => '当前系统要求的最少提现佣金为:¥:limitCNY',
'ticket_subject' => '[提现申请] 本工单由系统发出',
'ticket_create_failed' => '工单创建失败',
'ticket_message' => "提现方式::method\r\n提现账号::account\r\n",
'not_support_withdraw' => '不支持提现'
]
],
'plan' => [
'fetch' => [
'plan_not_exist' => '订阅计划不存在'
]
],
'order' => [
'details' => [
'order_not_exist' => '订单不存在',
'plan_not_exist' => '订阅计划不存在',
],
'save' => [
'plan_not_exist' => '订阅计划不存在',
'exist_open_order' => '您有未付款或开通中的订单,请稍后再试或将其取消',
'plan_stop_sell' => '该订阅已售罄,请更换其它订阅',
'plan_stop_renew' => '该订阅无法续费,请更换其它订阅',
'plan_stop' => '该订阅周期无法进行购买,请选择其它周期',
'plan_exist_not_buy_package' => '订阅已过期或无有效订阅,无法购买重置包',
'plan_expired' => '订阅已过期,请更换其它订阅',
'coupon_use_failed' => '优惠券使用失败',
'insufficient_balance' => '余额不足',
'order_create_failed' => '订单创建失败'
],
'checkout' => [
'order_not_exist_or_paid' => '订单不存在或已支付',
'pay_method_not_use' => '支付方式不可用',
],
'check' => [
'order_not_exist' => '订单不存在'
],
'cancel' => [
'params_wrong' => '参数有误',
'order_not_exist' => '订单不存在',
'only_cancel_pending_order' => '只可以取消待支付订单',
'cancel_failed' => '取消失败',
],
'stripeAlipay' => [
'currency_convert_timeout' => '货币转换超时,请稍后再试',
'gateway_request_failed' => '支付网关请求失败',
],
'stripeWepay' => [
'currency_convert_timeout' => '货币转换超时,请稍后再试',
'gateway_request_failed' => '支付网关请求失败',
],
'stripeCard' => [
'currency_convert_timeout' => '货币转换超时,请稍后再试',
'was_problem' => '遇到了点问题,请刷新页面稍后再试',
'deduction_failed' => '扣款失败,请检查信用卡信息'
]
],
'knowledge' => [
'fetch' => [
'knowledge_not_exist' => '文章不存在',
'apple_id_must_be_plan' => '无有效订阅,无法使用本站提供的 AppleID'
]
],
'invite' => [
'save' => [
'invite_create_limit' => '已达到创建数量上限'
]
],
'coupon' => [
'check' => [
'coupon_not_empty' => '优惠券不能为空',
'coupon_invalid' => '优惠券无效',
'coupon_not_available_by_number' => '优惠券已无可用次数',
'coupon_not_available_by_time' => '优惠券还未到可用时间',
'coupon_expired' => '优惠券已过期',
'coupon_limit_plan' => '该订阅无法使用此优惠码'
]
]
];

View File

@ -1,11 +1,14 @@
port: 8890
socks-port: 8891
allow-lan: false
port: 7890
socks-port: 7891
# redir-port: 7892
# tproxy-port: 7893
# mixed-port: 7890
allow-lan: true
bind-address: "*"
mode: rule
log-level: info
external-controller: 127.0.0.1:9091
experimental:
ignore-resolve-fail: true
external-controller: 127.0.0.1:9090
dns:
enable: true
# listen: 0.0.0.0:53
@ -18,15 +21,16 @@ dns:
fake-ip-range: 198.18.0.1/16
use-hosts: true
nameserver:
- https://dns.alidns.com/dns-query
- https://doh.pub/dns-query
fallback:
- tls://1.0.0.1:853
- https://cloudflare-dns.com/dns-query
- https://dns.google/dns-query
fallback-filter:
geoip: true
ipcidr:
- 240.0.0.0/4
- 0.0.0.0/32
proxies: