Explorar o código

1.账号临近过期提醒
2.账号流量提醒
3.邮件投递记录改进
4.后台管理首页优化

zhangjiangbin %!s(int64=8) %!d(string=hai) anos
pai
achega
46722f80da

+ 180 - 136
app/Http/Controllers/AdminController.php

@@ -4,7 +4,6 @@ namespace App\Http\Controllers;
 
 use App\Http\Models\Article;
 use App\Http\Models\Config;
-use App\Http\Models\EmailLog;
 use App\Http\Models\Goods;
 use App\Http\Models\Invite;
 use App\Http\Models\OrderGoods;
@@ -18,6 +17,7 @@ use App\Http\Models\SsNodeInfo;
 use App\Http\Models\SsNodeOnlineLog;
 use App\Http\Models\User;
 use App\Http\Models\UserTrafficLog;
+use App\Mail\userExpireWarning;
 use App\Mail\userTrafficWarning;
 use Illuminate\Http\Request;
 use Redirect;
@@ -46,7 +46,7 @@ class AdminController extends BaseController
         $flowCount = $this->flowAutoShow($flowCount);
         $view['flowCount'] = $flowCount;
         $view['totalBalance'] = User::sum('balance');
-        $view['totalWaitRefAmount'] = ReferralLog::whereIn('status', [0,1])->sum('ref_amount');
+        $view['totalWaitRefAmount'] = ReferralLog::whereIn('status', [0, 1])->sum('ref_amount');
         $view['totalRefAmount'] = ReferralApply::where('status', 2)->sum('amount');
         $view['expireWarningUserCount'] = User::where('expire_time', '<=', date('Y-m-d', strtotime("+15 days")))->where('enable', 1)->count();
 
@@ -74,24 +74,48 @@ class AdminController extends BaseController
 
         // 用户流量警告提醒发邮件 TODO:加入定时任务
         if (self::$config['traffic_warning']) {
-            $userList = User::get();
+            $userList = User::where('transfer_enable', '>', 0)->whereIn('status', [0, 1])->where('enable', 1)->get();
             foreach ($userList as $user) {
-                if (empty($user->transfer_enable)) {
+                // 用户名不是邮箱的跳过
+                if (false === filter_var($user->username, FILTER_VALIDATE_EMAIL)) {
                     continue;
                 }
 
                 $usedPercent = round(($user->d + $user->u) / $user->transfer_enable, 2) * 100; // 已使用流量百分比
                 if ($usedPercent >= self::$config['traffic_warning_percent']) {
-                    $ret = Mail::to($user->username)->send(new userTrafficWarning(self::$config['website_name'], $usedPercent));
-
-                    // 写入邮件发送日志
-                    $emailLogObj = new EmailLog();
-                    $emailLogObj->user_id = $user->id;
-                    $emailLogObj->title = '用户流量警告';
-                    $emailLogObj->content = '流量已使用:' . $usedPercent . '%,超过流量阈值' . self::$config['traffic_warning_percent'];
-                    $emailLogObj->status = $ret ? 1 : 0;
-                    $emailLogObj->created_at = date('Y-m-d H:i:s');
-                    $emailLogObj->save();
+                    $title = '流量警告';
+                    $content = '流量已使用:' . $usedPercent . '%,超过设置的流量阈值' . self::$config['traffic_warning_percent'] . '%';
+
+                    try {
+                        Mail::to($user->username)->send(new userTrafficWarning(self::$config['website_name'], $usedPercent));
+                        $this->sendEmailLog($user->id, $title, $content);
+                    } catch (\Exception $e) {
+                        $this->sendEmailLog($user->id, $title, $content, 0, $e->getMessage());
+                    }
+                }
+            }
+        }
+
+        // 用户到期提醒发邮件 TODO:加入定时任务
+        if (self::$config['expire_warning']) {
+            $userList = User::where('transfer_enable', '>', 0)->whereIn('status', [0, 1])->where('enable', 1)->get();
+            foreach ($userList as $user) {
+                // 用户名不是邮箱的跳过
+                if (false === filter_var($user->username, FILTER_VALIDATE_EMAIL)) {
+                    continue;
+                }
+
+                $lastCanUseDays = floor(round(strtotime($user->expire_time) - strtotime(date('Y-m-d H:i:s'))) / 3600 / 24);
+                if ($lastCanUseDays > 0 && $lastCanUseDays <= self::$config['expire_days']) {
+                    $title = '账号过期提醒';
+                    $content = '账号还剩【' . $lastCanUseDays . '】天即将过期';
+
+                    try {
+                        Mail::to($user->username)->send(new userExpireWarning(self::$config['website_name'], $lastCanUseDays));
+                        $this->sendEmailLog($user->id, $title, $content);
+                    } catch (\Exception $e) {
+                        $this->sendEmailLog($user->id, $title, $content, 0, $e->getMessage());
+                    }
                 }
             }
         }
@@ -208,30 +232,30 @@ class AdminController extends BaseController
             }
 
             $ret = User::create([
-                'username' => $username,
-                'password' => $password,
-                'port' => $port,
-                'passwd' => empty($passwd) ? $this->makeRandStr() : $passwd, // SS密码为空时生成默认密码
+                'username'        => $username,
+                'password'        => $password,
+                'port'            => $port,
+                'passwd'          => empty($passwd) ? $this->makeRandStr() : $passwd, // SS密码为空时生成默认密码
                 'transfer_enable' => $this->toGB($transfer_enable),
-                'enable' => $enable,
-                'method' => $method,
-                'custom_method' => $method,
-                'protocol' => $protocol,
-                'protocol_param' => $protocol_param,
-                'obfs' => $obfs,
-                'obfs_param' => $obfs_param,
-                'wechat' => $wechat,
-                'qq' => $qq,
-                'usage' => $usage,
-                'pay_way' => $pay_way,
-                'balance' => $balance,
-                'score' => $score,
-                'enable_time' => empty($enable_time) ? date('Y-m-d') : $enable_time,
-                'expire_time' => empty($expire_time) ? date('Y-m-d', strtotime("+365 days")) : $expire_time,
-                'remark' => $remark,
-                'level' => $level,
-                'is_admin' => $is_admin,
-                'reg_ip' => $request->getClientIp()
+                'enable'          => $enable,
+                'method'          => $method,
+                'custom_method'   => $method,
+                'protocol'        => $protocol,
+                'protocol_param'  => $protocol_param,
+                'obfs'            => $obfs,
+                'obfs_param'      => $obfs_param,
+                'wechat'          => $wechat,
+                'qq'              => $qq,
+                'usage'           => $usage,
+                'pay_way'         => $pay_way,
+                'balance'         => $balance,
+                'score'           => $score,
+                'enable_time'     => empty($enable_time) ? date('Y-m-d') : $enable_time,
+                'expire_time'     => empty($expire_time) ? date('Y-m-d', strtotime("+365 days")) : $expire_time,
+                'remark'          => $remark,
+                'level'           => $level,
+                'is_admin'        => $is_admin,
+                'reg_ip'          => $request->getClientIp()
             ]);
 
             if ($ret) {
@@ -245,9 +269,9 @@ class AdminController extends BaseController
             $view['last_port'] = self::$config['is_rand_port'] ? $this->getRandPort() : $last_user->port + 1;
 
             // 加密方式、协议、混淆
-            $view['method_list'] =  $this->methodList();
-            $view['protocol_list'] =  $this->protocolList();
-            $view['obfs_list'] =  $this->obfsList();
+            $view['method_list'] = $this->methodList();
+            $view['protocol_list'] = $this->protocolList();
+            $view['obfs_list'] = $this->obfsList();
 
             return Response::view('admin/addUser', $view);
         }
@@ -286,31 +310,31 @@ class AdminController extends BaseController
             $is_admin = $request->get('is_admin');
 
             $data = [
-                'username' => $username,
-                'port' => $port,
-                'passwd' => $passwd,
-                'transfer_enable' => $this->toGB($transfer_enable),
-                'enable' => $enable,
-                'method' => $method,
-                'custom_method' => $method,
-                'protocol' => $protocol,
-                'protocol_param' => $protocol_param,
-                'obfs' => $obfs,
-                'obfs_param' => $obfs_param,
-                'speed_limit_per_con' => $speed_limit_per_con,
+                'username'             => $username,
+                'port'                 => $port,
+                'passwd'               => $passwd,
+                'transfer_enable'      => $this->toGB($transfer_enable),
+                'enable'               => $enable,
+                'method'               => $method,
+                'custom_method'        => $method,
+                'protocol'             => $protocol,
+                'protocol_param'       => $protocol_param,
+                'obfs'                 => $obfs,
+                'obfs_param'           => $obfs_param,
+                'speed_limit_per_con'  => $speed_limit_per_con,
                 'speed_limit_per_user' => $speed_limit_per_user,
-                'wechat' => $wechat,
-                'qq' => $qq,
-                'usage' => $usage,
-                'pay_way' => $pay_way,
-                'balance' => $balance,
-                'score' => $score,
-                'status' => $status,
-                'enable_time' => empty($enable_time) ? date('Y-m-d') : $enable_time,
-                'expire_time' => empty($expire_time) ? date('Y-m-d', strtotime("+365 days")) : $expire_time,
-                'remark' => $remark,
-                'level' => $level,
-                'is_admin' => $is_admin
+                'wechat'               => $wechat,
+                'qq'                   => $qq,
+                'usage'                => $usage,
+                'pay_way'              => $pay_way,
+                'balance'              => $balance,
+                'score'                => $score,
+                'status'               => $status,
+                'enable_time'          => empty($enable_time) ? date('Y-m-d') : $enable_time,
+                'expire_time'          => empty($expire_time) ? date('Y-m-d', strtotime("+365 days")) : $expire_time,
+                'remark'               => $remark,
+                'level'                => $level,
+                'is_admin'             => $is_admin
             ];
 
             if (!empty($password)) {
@@ -332,9 +356,9 @@ class AdminController extends BaseController
             $view['user'] = $user;
 
             // 加密方式、协议、混淆
-            $view['method_list'] =  $this->methodList();
-            $view['protocol_list'] =  $this->protocolList();
-            $view['obfs_list'] =  $this->obfsList();
+            $view['method_list'] = $this->methodList();
+            $view['protocol_list'] = $this->protocolList();
+            $view['obfs_list'] = $this->obfsList();
 
             return Response::view('admin/editUser', $view);
         }
@@ -402,39 +426,39 @@ class AdminController extends BaseController
             $status = $request->get('status');
 
             $node = SsNode::create([
-                'name' => $name,
-                'group_id' => $group_id,
-                'server' => $server,
-                'method' => $method,
-                'custom_method' => $method,
-                'protocol' => $protocol,
+                'name'           => $name,
+                'group_id'       => $group_id,
+                'server'         => $server,
+                'method'         => $method,
+                'custom_method'  => $method,
+                'protocol'       => $protocol,
                 'protocol_param' => $protocol_param,
-                'obfs' => $obfs,
-                'obfs_param' => $obfs_param,
-                'traffic_rate' => $traffic_rate,
-                'bandwidth' => $bandwidth,
-                'traffic' => $traffic,
-                'monitor_url' => $monitor_url,
-                'compatible' => $compatible,
-                'sort' => $sort,
-                'status' => $status,
+                'obfs'           => $obfs,
+                'obfs_param'     => $obfs_param,
+                'traffic_rate'   => $traffic_rate,
+                'bandwidth'      => $bandwidth,
+                'traffic'        => $traffic,
+                'monitor_url'    => $monitor_url,
+                'compatible'     => $compatible,
+                'sort'           => $sort,
+                'status'         => $status,
             ]);
 
             // 建立分组关联
             if ($group_id) {
                 SsGroupNode::create([
                     'group_id' => $group_id,
-                    'node_id' => $node->id
+                    'node_id'  => $node->id
                 ]);
             }
 
             return Response::json(['status' => 'success', 'data' => '', 'message' => '添加成功']);
         } else {
             // 加密方式、协议、混淆
-            $view['method_list'] =  $this->methodList();
-            $view['protocol_list'] =  $this->protocolList();
-            $view['obfs_list'] =  $this->obfsList();
-            $view['group_list'] =  SsGroup::get();
+            $view['method_list'] = $this->methodList();
+            $view['protocol_list'] = $this->protocolList();
+            $view['obfs_list'] = $this->obfsList();
+            $view['group_list'] = SsGroup::get();
 
             return Response::view('admin/addNode', $view);
         }
@@ -463,22 +487,22 @@ class AdminController extends BaseController
             $status = $request->get('status');
 
             $data = [
-                'name' => $name,
-                'group_id' => $group_id,
-                'server' => $server,
-                'method' => $method,
-                'custom_method' => $method,
-                'protocol' => $protocol,
+                'name'           => $name,
+                'group_id'       => $group_id,
+                'server'         => $server,
+                'method'         => $method,
+                'custom_method'  => $method,
+                'protocol'       => $protocol,
                 'protocol_param' => $protocol_param,
-                'obfs' => $obfs,
-                'obfs_param' => $obfs_param,
-                'traffic_rate' => $traffic_rate,
-                'bandwidth' => $bandwidth,
-                'traffic' => $traffic,
-                'monitor_url' => $monitor_url,
-                'compatible' => $compatible,
-                'sort' => $sort,
-                'status' => $status
+                'obfs'           => $obfs,
+                'obfs_param'     => $obfs_param,
+                'traffic_rate'   => $traffic_rate,
+                'bandwidth'      => $bandwidth,
+                'traffic'        => $traffic,
+                'monitor_url'    => $monitor_url,
+                'compatible'     => $compatible,
+                'sort'           => $sort,
+                'status'         => $status
             ];
 
             $ret = SsNode::where('id', $id)->update($data);
@@ -490,7 +514,7 @@ class AdminController extends BaseController
 
                     SsGroupNode::create([
                         'group_id' => $group_id,
-                        'node_id' => $id
+                        'node_id'  => $id
                     ]);
                 }
 
@@ -502,10 +526,10 @@ class AdminController extends BaseController
             $view['node'] = SsNode::where('id', $id)->first();
 
             // 加密方式、协议、混淆
-            $view['method_list'] =  $this->methodList();
-            $view['protocol_list'] =  $this->protocolList();
-            $view['obfs_list'] =  $this->obfsList();
-            $view['group_list'] =  SsGroup::get();
+            $view['method_list'] = $this->methodList();
+            $view['protocol_list'] = $this->protocolList();
+            $view['obfs_list'] = $this->obfsList();
+            $view['group_list'] = SsGroup::get();
 
             return Response::view('admin/editNode', $view);
         }
@@ -540,10 +564,10 @@ class AdminController extends BaseController
             $sort = $request->get('sort');
 
             Article::create([
-                'title' => $title,
+                'title'   => $title,
                 'content' => $content,
-                'is_del' => 0,
-                'sort' => $sort
+                'is_del'  => 0,
+                'sort'    => $sort
             ]);
 
             return Response::json(['status' => 'success', 'data' => '', 'message' => '添加成功']);
@@ -562,9 +586,9 @@ class AdminController extends BaseController
             $content = $request->get('content');
 
             $data = [
-                'title' => $title,
+                'title'   => $title,
                 'content' => $content,
-                'sort' => $sort
+                'sort'    => $sort
             ];
 
             $ret = Article::where('id', $id)->update($data);
@@ -608,7 +632,7 @@ class AdminController extends BaseController
             $level = $request->get('level');
 
             SsGroup::create([
-                'name' => $name,
+                'name'  => $name,
                 'level' => $level
             ]);
 
@@ -627,7 +651,7 @@ class AdminController extends BaseController
             $level = $request->get('level');
 
             $data = [
-                'name' => $name,
+                'name'  => $name,
                 'level' => $level
             ];
 
@@ -673,7 +697,7 @@ class AdminController extends BaseController
         $query = UserTrafficLog::with(['User', 'SsNode']);
 
         if (!empty($port)) {
-            $query->whereHas('user', function($q) use($port) {
+            $query->whereHas('user', function ($q) use ($port) {
                 $q->where('port', $port);
             });
         }
@@ -683,7 +707,7 @@ class AdminController extends BaseController
         }
 
         if (!empty($username)) {
-            $query->whereHas('user', function($q) use($username) {
+            $query->whereHas('user', function ($q) use ($username) {
                 $q->where('username', 'like', '%' . $username . '%');
             });
         }
@@ -729,18 +753,18 @@ class AdminController extends BaseController
             $data = [];
             foreach ($content->port_password as $port => $passwd) {
                 $data[] = [
-                    'd' => 0,
-                    'enable' => 1,
-                    'method' => $method,
-                    'obfs' => $obfs,
-                    'obfs_param' => empty($obfs_param) ? "" : $obfs_param,
-                    'passwd' => $passwd,
-                    'port' => $port,
-                    'protocol' => $protocol,
-                    'protocol_param' => empty($protocol_param) ? "" : $protocol_param,
+                    'd'               => 0,
+                    'enable'          => 1,
+                    'method'          => $method,
+                    'obfs'            => $obfs,
+                    'obfs_param'      => empty($obfs_param) ? "" : $obfs_param,
+                    'passwd'          => $passwd,
+                    'port'            => $port,
+                    'protocol'        => $protocol,
+                    'protocol_param'  => empty($protocol_param) ? "" : $protocol_param,
                     'transfer_enable' => $this->toGB($transfer_enable),
-                    'u' => 0,
-                    'user' => date('Ymd') . '_IMPORT_' . $port,
+                    'u'               => 0,
+                    'user'            => date('Ymd') . '_IMPORT_' . $port,
                 ];
             }
 
@@ -752,9 +776,9 @@ class AdminController extends BaseController
             return Response::json(['status' => 'success', 'data' => $json, 'message' => '转换成功']);
         } else {
             // 加密方式、协议、混淆
-            $view['method_list'] =  $this->methodList();
-            $view['protocol_list'] =  $this->protocolList();
-            $view['obfs_list'] =  $this->obfsList();
+            $view['method_list'] = $this->methodList();
+            $view['protocol_list'] = $this->protocolList();
+            $view['obfs_list'] = $this->obfsList();
 
             return Response::view('admin/convert', $view);
         }
@@ -777,6 +801,7 @@ class AdminController extends BaseController
 
             if (!$request->hasFile('uploadFile')) {
                 $request->session()->flash('errorMsg', '请选择要上传的文件');
+
                 return Redirect::back();
             }
 
@@ -785,23 +810,26 @@ class AdminController extends BaseController
             // 只能上传JSON文件
             if ($file->getClientMimeType() != 'application/json' || $file->getClientOriginalExtension() != 'json') {
                 $request->session()->flash('errorMsg', '只允许上传JSON文件');
+
                 return Redirect::back();
             }
 
             if (!$file->isValid()) {
                 $request->session()->flash('errorMsg', '产生未知错误,请重新上传');
+
                 return Redirect::back();
             }
 
             $save_path = realpath(storage_path('uploads'));
-            $new_name = md5($file->getClientOriginalExtension()).'json';
+            $new_name = md5($file->getClientOriginalExtension()) . 'json';
             $file->move($save_path, $new_name);
 
             // 读取文件内容
-            $data = file_get_contents($save_path.'/'.$new_name);
+            $data = file_get_contents($save_path . '/' . $new_name);
             $data = json_decode($data);
             if (!$data) {
                 $request->session()->flash('errorMsg', '内容格式解析异常,请上传符合SSR配置规范的JSON文件');
+
                 return Redirect::back();
             }
 
@@ -846,11 +874,13 @@ class AdminController extends BaseController
                 \DB::rollBack();
 
                 $request->session()->flash('errorMsg', '出错了,可能是导入的配置中有端口已经存在了');
+
                 return Redirect::back();
             }
 
 
             $request->session()->flash('successMsg', '导入成功');
+
             return Redirect::back();
         } else {
             return Response::view('admin/import');
@@ -950,18 +980,22 @@ TXT;
             $user = User::where('id', $user['id'])->first();
             if ($user->password != $old_password) {
                 $request->session()->flash('errorMsg', '旧密码错误,请重新输入');
+
                 return Redirect::back();
             } else if ($user->password == $new_password) {
                 $request->session()->flash('errorMsg', '新密码不可与旧密码一样,请重新输入');
+
                 return Redirect::back();
             }
 
             $ret = User::where('id', $user['id'])->update(['password' => $new_password]);
             if (!$ret) {
                 $request->session()->flash('errorMsg', '修改失败');
+
                 return Redirect::back();
             } else {
                 $request->session()->flash('successMsg', '修改成功');
+
                 return Redirect::back();
             }
         } else {
@@ -1026,10 +1060,10 @@ TXT;
             }
 
             SsConfig::create([
-                'name' => $name,
-                'type' => $type,
+                'name'       => $name,
+                'type'       => $type,
                 'is_default' => $is_default,
-                'sort' => $sort
+                'sort'       => $sort
             ]);
 
             return Response::json(['status' => 'success', 'data' => '', 'message' => '添加成功']);
@@ -1223,6 +1257,16 @@ TXT;
         return Response::json(['status' => 'success', 'data' => '', 'message' => '设置成功']);
     }
 
+    // 设置账号过期提醒阈值
+    public function setExpireDays(Request $request)
+    {
+        $value = intval($request->get('value'));
+
+        Config::where('name', 'expire_days')->update(['value' => $value]);
+
+        return Response::json(['status' => 'success', 'data' => '', 'message' => '设置成功']);
+    }
+
     // 设置激活账号次数
     public function setAddScoreRange(Request $request)
     {
@@ -1326,7 +1370,7 @@ TXT;
 
         $query = ReferralApply::with('user');
         if ($username) {
-            $query->whereHas('user', function($q) use($username) {
+            $query->whereHas('user', function ($q) use ($username) {
                 $q->where('username', 'like', '%' . $username . '%');
             });
         }

+ 21 - 0
app/Http/Controllers/BaseController.php

@@ -2,6 +2,7 @@
 
 namespace App\Http\Controllers;
 use App\Http\Models\Config;
+use App\Http\Models\EmailLog;
 use App\Http\Models\SsConfig;
 use App\Http\Models\User;
 
@@ -162,4 +163,24 @@ class BaseController extends Controller
 
         return round($bytes, $precision) . ' ' . $units[$pow];
     }
+
+    /**
+     * 写入邮件发送日志
+     * @param int $user_id 用户ID
+     * @param string $title 投递类型(投递标题)
+     * @param string $content 投递内容(简要概述)
+     * @param int $status 投递状态
+     * @param string $error 投递失败时记录的异常信息
+     */
+    public function sendEmailLog($user_id, $title, $content, $status = 1, $error = '')
+    {
+        $emailLogObj = new EmailLog();
+        $emailLogObj->user_id = $user_id;
+        $emailLogObj->title = $title;
+        $emailLogObj->content = $content;
+        $emailLogObj->status = $status;
+        $emailLogObj->error = $error;
+        $emailLogObj->created_at = date('Y-m-d H:i:s');
+        $emailLogObj->save();
+    }
 }

+ 1 - 1
app/Http/Controllers/EmailLogController.php

@@ -23,7 +23,7 @@ class EmailLogController extends BaseController
     // 邮件发送日志列表
     public function list(Request $request)
     {
-        $view['list'] = EmailLog::paginate(10);
+        $view['list'] = EmailLog::with('user')->paginate(10);
 
         return Response::view('emailLog/list', $view);
     }

+ 25 - 22
app/Http/Controllers/RegisterController.php

@@ -121,29 +121,32 @@ class RegisterController extends BaseController
                 Invite::where('id', $code->id)->update(['fuid' => $user->id, 'status' => 1]);
             }
 
-            // 生成激活账号的地址
-            $token = md5(self::$config['website_name'] . $username . microtime());
-            $verify = new Verify();
-            $verify->user_id = $user->id;
-            $verify->username = $username;
-            $verify->token = $token;
-            $verify->status = 0;
-            $verify->save();
-
             // 发送邮件
-            $activeUserUrl = self::$config['website_url'] . '/active/' . $token;
-            $ret = Mail::to($username)->send(new activeUser(self::$config['website_name'], $activeUserUrl));
-
-            // 写入邮件发送日志
-            $emailLogObj = new EmailLog();
-            $emailLogObj->user_id = $user->id;
-            $emailLogObj->title = '注册激活账号';
-            $emailLogObj->content = '请求地址:' . $activeUserUrl;
-            $emailLogObj->status = $ret ? 1 : 0;
-            $emailLogObj->created_at = date('Y-m-d H:i:s');
-            $emailLogObj->save();
-
-            $request->session()->flash('regSuccessMsg', '注册成功:激活邮件已发送,请查看邮箱');
+            if (self::$config['is_active_register']) {
+                // 生成激活账号的地址
+                $token = md5(self::$config['website_name'] . $username . microtime());
+                $verify = new Verify();
+                $verify->user_id = $user->id;
+                $verify->username = $username;
+                $verify->token = $token;
+                $verify->status = 0;
+                $verify->save();
+
+                $activeUserUrl = self::$config['website_url'] . '/active/' . $token;
+                $title = '注册激活';
+                $content = '请求地址:' . $activeUserUrl;
+
+                try {
+                    Mail::to($username)->send(new activeUser(self::$config['website_name'], $activeUserUrl));
+                    $this->sendEmailLog($user->id, $title, $content);
+                } catch (\Exception $e) {
+                    $this->sendEmailLog($user->id, $title, $content, 0, $e->getMessage());
+                }
+
+                $request->session()->flash('regSuccessMsg', '注册成功:激活邮件已发送,请查看邮箱');
+            } else {
+                $request->session()->flash('regSuccessMsg', '注册成功');
+            }
 
             return Redirect::to('login');
         } else {

+ 17 - 19
app/Http/Controllers/UserController.php

@@ -445,16 +445,15 @@ TXT;
 
             // 发送邮件
             $activeUserUrl = self::$config['website_url'] . '/active/' . $token;
-            $ret = Mail::to($user->username)->send(new activeUser(self::$config['website_name'], $activeUserUrl));
+            $title = '重新激活账号';
+            $content = '请求地址:' . $activeUserUrl;
 
-            // 写入邮件发送日志
-            $emailLogObj = new EmailLog();
-            $emailLogObj->user_id = $user->id;
-            $emailLogObj->title = '重新激活账号';
-            $emailLogObj->content = '请求地址:' . $activeUserUrl;
-            $emailLogObj->status = $ret ? 1 : 0;
-            $emailLogObj->created_at = date('Y-m-d H:i:s');
-            $emailLogObj->save();
+            try {
+                Mail::to($user->username)->send(new activeUser(self::$config['website_name'], $activeUserUrl));
+                $this->sendEmailLog($user->id, $title, $content);
+            } catch (\Exception $e) {
+                $this->sendEmailLog($user->id, $title, $content, 0, $e->getMessage());
+            }
 
             Cache::put('activeUser_' . md5($username), $activeTimes + 1, 1440);
             $request->session()->flash('successMsg', '邮件已发送,请查看邮箱');
@@ -559,16 +558,15 @@ TXT;
 
             // 发送邮件
             $resetPasswordUrl = self::$config['website_url'] . '/reset/' . $token;
-            $ret = Mail::to($user->username)->send(new resetPassword(self::$config['website_name'], $resetPasswordUrl));
-
-            // 写入邮件发送日志
-            $emailLogObj = new EmailLog();
-            $emailLogObj->user_id = $user->id;
-            $emailLogObj->title = '重置账号密码';
-            $emailLogObj->content = '请求地址:' . $resetPasswordUrl;
-            $emailLogObj->status = $ret ? 1 : 0;
-            $emailLogObj->created_at = date('Y-m-d H:i:s');
-            $emailLogObj->save();
+            $title = '重置密码';
+            $content = '请求地址:' . $resetPasswordUrl;
+
+            try {
+                Mail::to($user->username)->send(new resetPassword(self::$config['website_name'], $resetPasswordUrl));
+                $this->sendEmailLog($user->id, $title, $content);
+            } catch (\Exception $e) {
+                $this->sendEmailLog($user->id, $title, $content, 0, $e->getMessage());
+            }
 
             Cache::put('resetPassword_' . md5($username), $resetTimes + 1, 1440);
             $request->session()->flash('successMsg', '重置成功,请查看邮箱');

+ 5 - 0
app/Http/Models/EmailLog.php

@@ -18,7 +18,12 @@ class EmailLog extends Model
         'user_id',
         'title',
         'content',
+        'status',
+        'error',
         'created_at'
     ];
 
+    function user() {
+        return $this->hasOne(User::class, 'id', 'user_id');
+    }
 }

+ 30 - 0
app/Mail/userExpireWarning.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Mail;
+
+use Illuminate\Bus\Queueable;
+use Illuminate\Mail\Mailable;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Contracts\Queue\ShouldQueue;
+
+class userExpireWarning extends Mailable
+{
+    use Queueable, SerializesModels;
+
+    protected $websiteName;
+    protected $lastCanUseDays;
+
+    public function __construct($websiteName, $lastCanUseDays)
+    {
+        $this->websiteName = $websiteName;
+        $this->lastCanUseDays = $lastCanUseDays;
+    }
+
+    public function build()
+    {
+        return $this->view('emails.userExpireWarning')->subject('账号过期提醒')->with([
+            'websiteName'    => $this->websiteName,
+            'lastCanUseDays' => $this->lastCanUseDays
+        ]);
+    }
+}

+ 1 - 1
app/Mail/userTrafficWarning.php

@@ -22,7 +22,7 @@ class userTrafficWarning extends Mailable
 
     public function build()
     {
-        return $this->view('emails.activeUser')->subject('流量警告')->with([
+        return $this->view('emails.userTrafficWarning')->subject('流量警告')->with([
             'websiteName' => $this->websiteName,
             'usedPercent' => $this->usedPercent
         ]);

+ 1 - 1
resources/views/admin/layouts.blade.php

@@ -208,7 +208,7 @@
                         <li class="nav-item {{Request::getRequestUri() == '/emailLog/list' ? 'active open' : ''}}">
                             <a href="{{url('emailLog/list')}}" class="nav-link ">
                                 <i class="icon-list"></i>
-                                <span class="title">邮件发送记录</span>
+                                <span class="title">邮件投递记录</span>
                             </a>
                         </li>
                     </ul>

+ 79 - 21
resources/views/admin/system.blade.php

@@ -43,7 +43,10 @@
                                             <a href="#tab_4" data-toggle="tab"> 推广返利设置 </a>
                                         </li>
                                         <li>
-                                            <a href="#tab_5" data-toggle="tab"> 充值二维码设置 </a>
+                                            <a href="#tab_5" data-toggle="tab"> 警告提醒设置 </a>
+                                        </li>
+                                        <li>
+                                            <a href="#tab_6" data-toggle="tab"> 充值二维码设置 </a>
                                         </li>
                                     </ul>
                                 </div>
@@ -173,26 +176,6 @@
                                                             <span class="help-block"> 24小时内可以通过邮件激活账号次数 </span>
                                                         </div>
                                                     </div>
-                                                    <div class="form-group">
-                                                        <label for="traffic_warning" class="col-md-2 control-label">流量警告</label>
-                                                        <div class="col-md-6">
-                                                            <input type="checkbox" class="make-switch" @if($traffic_warning) checked @endif id="traffic_warning" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
-                                                            <span class="help-block"> 启用后用户的已使用流量超过警告阈值则自动发邮件提醒 </span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="form-group">
-                                                        <label for="traffic_warning_percent" class="col-md-2 control-label">流量警告阈值</label>
-                                                        <div class="col-md-3">
-                                                            <div class="input-group">
-                                                                <input class="form-control" type="text" name="traffic_warning_percent" value="{{$traffic_warning_percent}}" id="traffic_warning_percent" />
-                                                                <span class="input-group-addon">%</span>
-                                                                <span class="input-group-btn">
-                                                                    <button class="btn btn-success" type="button" onclick="setTrafficWarningPercent()">修改</button>
-                                                                </span>
-                                                            </div>
-                                                            <span class="help-block"> 建议设置在70%~90%,否则浪费邮件 </span>
-                                                        </div>
-                                                    </div>
                                                 </div>
                                             </form>
                                         </div>
@@ -284,6 +267,52 @@
                                             </form>
                                         </div>
                                         <div class="tab-pane" id="tab_5">
+                                            <form action="#" method="post" class="form-horizontal">
+                                                <div class="portlet-body">
+                                                    <div class="form-group">
+                                                        <label for="expire_warning" class="col-md-2 control-label">过期警告</label>
+                                                        <div class="col-md-6">
+                                                            <input type="checkbox" class="make-switch" @if($expire_warning) checked @endif id="expire_warning" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
+                                                            <span class="help-block"> 启用后账号距到期还剩阈值设置的值时自动发邮件提醒用户 </span>
+                                                        </div>
+                                                    </div>
+                                                    <div class="form-group">
+                                                        <label for="expire_days" class="col-md-2 control-label">过期警告阈值</label>
+                                                        <div class="col-md-3">
+                                                            <div class="input-group">
+                                                                <input class="form-control" type="text" name="expire_days" value="{{$expire_days}}" id="expire_days" />
+                                                                <span class="input-group-addon">天</span>
+                                                                <span class="input-group-btn">
+                                                                    <button class="btn btn-success" type="button" onclick="setExpireDays()">修改</button>
+                                                                </span>
+                                                            </div>
+                                                            <span class="help-block"> 账号距离过期还差多少天时发警告邮件 </span>
+                                                        </div>
+                                                    </div>
+                                                    <div class="form-group">
+                                                        <label for="traffic_warning" class="col-md-2 control-label">流量警告</label>
+                                                        <div class="col-md-6">
+                                                            <input type="checkbox" class="make-switch" @if($traffic_warning) checked @endif id="traffic_warning" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
+                                                            <span class="help-block"> 启用后账号已使用流量超过警告阈值时自动发邮件提醒用户 </span>
+                                                        </div>
+                                                    </div>
+                                                    <div class="form-group">
+                                                        <label for="traffic_warning_percent" class="col-md-2 control-label">流量警告阈值</label>
+                                                        <div class="col-md-3">
+                                                            <div class="input-group">
+                                                                <input class="form-control" type="text" name="traffic_warning_percent" value="{{$traffic_warning_percent}}" id="traffic_warning_percent" />
+                                                                <span class="input-group-addon">%</span>
+                                                                <span class="input-group-btn">
+                                                                    <button class="btn btn-success" type="button" onclick="setTrafficWarningPercent()">修改</button>
+                                                                </span>
+                                                            </div>
+                                                            <span class="help-block"> 建议设置在70%~90% </span>
+                                                        </div>
+                                                    </div>
+                                                </div>
+                                            </form>
+                                        </div>
+                                        <div class="tab-pane" id="tab_6">
                                             <form action="{{url('admin/setQrcode')}}" method="post" enctype="multipart/form-data" class="form-horizontal">
                                                 <div class="form-body">
                                                     <div class="portlet-body">
@@ -449,6 +478,21 @@
             }
         });
 
+        // 启用、禁用账号到期自动邮件提醒
+        $('#expire_warning').on({
+            'switchChange.bootstrapSwitch': function(event, state) {
+                var expire_warning = state ? 1 : 0;
+
+                $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'expire_warning', value:expire_warning}, function (ret) {
+                    if (ret.status == 'fail') {
+                        bootbox.alert(ret.message, function() {
+                            window.location.reload();
+                        });
+                    }
+                });
+            }
+        });
+
         // 启用、禁用退关返利用户可见与否
         $('#referral_status').on({
             'switchChange.bootstrapSwitch': function(event, state) {
@@ -566,6 +610,20 @@
             });
         }
 
+
+        // 设置账号过期提醒阈值
+        function setExpireDays() {
+            var expire_days = $("#expire_days").val();
+
+            $.post("{{url('admin/setExpireDays')}}", {_token:'{{csrf_token()}}', value:expire_days}, function (ret) {
+                if (ret.status == 'success') {
+                    bootbox.alert(ret.message, function() {
+                        window.location.reload();
+                    });
+                }
+            });
+        }
+
         // 设置网站名称
         function setWebsiteName() {
             var website_name = $("#website_name").val();

+ 6 - 6
resources/views/emailLog/list.blade.php

@@ -42,10 +42,10 @@
                                 <tr>
                                     <th> ID </th>
                                     <th> 接收者 </th>
-                                    <th> 邮件标题 </th>
-                                    <th> 邮件内容 </th>
-                                    <th> 发送时间 </th>
-                                    <th> 状态 </th>
+                                    <th> 邮件类型 </th>
+                                    <th> 投递内容 </th>
+                                    <th> 投递时间 </th>
+                                    <th> 投递状态 </th>
                                 </tr>
                                 </thead>
                                 <tbody>
@@ -57,11 +57,11 @@
                                     @foreach($list as $vo)
                                         <tr class="odd gradeX">
                                             <td> {{$vo->id}} </td>
-                                            <td> {{$vo->user_id}} </td>
+                                            <td> {{$vo->user->username}} </td>
                                             <td> {{$vo->title}} </td>
                                             <td> {{$vo->content}} </td>
                                             <td> {{$vo->created_at}} </td>
-                                            <td> {{$vo->status ? '发送成功' : '发送失败'}} </td>
+                                            <td> {{$vo->status ? '投递成功' : $vo->error}} </td>
                                         </tr>
                                     @endforeach
                                 @endif

+ 88 - 0
resources/views/emails/userExpireWarning.blade.php

@@ -0,0 +1,88 @@
+<table class="body" style="Margin:0;background:#FAFAFA;border-collapse:collapse;border-spacing:0;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0;padding:0;text-align:left;vertical-align:top;width:100%">
+    <tbody>
+    <tr style="padding:0;text-align:left;vertical-align:top">
+        <td class="center" align="center" valign="top" style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:19px;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+            <center data-parsed="" style="min-width:580px;width:100%">
+                <table align="center" class="container no-bg float-center" style="Margin:0 auto;background:0 0;border:0;border-collapse:collapse;border-radius:3px;border-spacing:0;box-shadow:none;float:none;margin:0 auto;margin-top:20px;padding:0;text-align:center;vertical-align:top;width:580px">
+                    <tbody>
+                    <tr style="padding:0;text-align:left;vertical-align:top">
+                        <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:19px;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+                            <table class="row" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%">
+                                <tbody>
+                                <tr style="padding:0;text-align:left;vertical-align:top">
+
+                                    <th class="small-11 large-11 columns last" style="Margin:0 auto;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0 auto;padding:0;padding-bottom:0;padding-left:8px;padding-right:16px;text-align:left;width:515.67px">
+                                        <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+                                            <tbody>
+                                            <tr style="padding:0;text-align:left;vertical-align:top">
+                                                <th style="Margin:0;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0;padding:0;text-align:left">
+                                                    <h3 style="Margin:0;Margin-bottom:10px;color:inherit;font-family:Helvetica,Arial,sans-serif;font-size:28px;font-weight:400;line-height:1.3;margin:0;margin-bottom:0;padding:0;text-align:left;word-wrap:normal">
+                                                        <a href="#" style="Margin:0;color:#40253b;font-family:Helvetica,Arial,sans-serif;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left;text-decoration:none" target="_blank">
+                                                            SSRPanel
+                                                        </a>
+                                                    </h3>
+                                                </th>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </th>
+                                </tr>
+                                </tbody>
+                            </table>
+                        </td>
+                    </tr>
+                    </tbody>
+                </table>
+                <table align="center" class="container float-center" style="Margin:0 auto;background:#fefefe;border:1px solid #cdcdcd;border-collapse:collapse;border-radius:3px;border-spacing:0;float:none;margin:0 auto;margin-top:20px;padding:0;text-align:center;vertical-align:top;width:580px">
+                    <tbody>
+                    <tr style="padding:0;text-align:left;vertical-align:top">
+                        <td style="-moz-hyphens:auto;-webkit-hyphens:auto;Margin:0;border-collapse:collapse!important;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;hyphens:auto;line-height:19px;margin:0;padding:0;text-align:left;vertical-align:top;word-wrap:break-word">
+                            <table class="row container-header-row" style="background-color:#5c97bd;border-collapse:collapse;border-spacing:0;color:#f3f3f3;display:table;padding:0;padding-bottom:8px;padding-top:8px;position:relative;text-align:left;vertical-align:top;width:100%">
+                                <tbody>
+                                <tr style="padding:0;text-align:left;vertical-align:top">
+                                    <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0 auto;padding:0;padding-bottom:0;padding-left:16px;padding-right:16px;text-align:left;width:564px">
+                                        <table style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%">
+                                            <tbody>
+                                            <tr style="padding:0;text-align:left;vertical-align:top">
+                                                <th style="Margin:0;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0;padding:0;text-align:left">
+                                                    <h6 style="Margin:0;Margin-bottom:10px;color:#f3f3f3;font-family:Helvetica,Arial,sans-serif;font-size:18px;font-weight:400;line-height:1.3;margin:0;margin-bottom:8px;margin-top:8px;padding:0;text-align:left;word-wrap:normal">
+                                                        <a href="#" style="Margin:0;color:#f3f3f3;font-family:Helvetica,Arial,sans-serif;font-weight:400;line-height:1.3;margin:0;padding:0;text-align:left;text-decoration:none" target="_blank">
+                                                            账号过期警告
+                                                        </a>
+                                                    </h6>
+                                                </th>
+                                                <th class="expander" style="Margin:0;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>
+                                            </tr>
+                                            </tbody>
+                                        </table>
+                                    </th>
+                                </tr>
+                                </tbody>
+                            </table>
+                            <table class="row" style="border-collapse:collapse;border-spacing:0;display:table;padding:0;position:relative;text-align:left;vertical-align:top;width:100%">
+                                <tbody>
+                                <tr style="padding:0;text-align:left;vertical-align:top">
+                                    <th class="small-12 large-12 columns first last" style="Margin:0 auto;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0 auto;padding:0;padding-bottom:0;padding-left:16px;padding-right:16px;text-align:left;width:564px">
+                                        <p style="border-collapse:collapse;border-spacing:0;padding:0;text-align:left;vertical-align:top;width:100%"></p>
+                                <tbody>
+                                <tr style="padding:0;text-align:left;vertical-align:top">
+                                    <th style="Margin:0;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0;padding:0;text-align:left">
+                                        <div class="release" style="padding-top:5px;padding-left:20px;padding-bottom:20px;">
+                                            <p>这是一封来自 {{$websiteName}} 的账号过期提醒。</p>
+                                            <p>您的账号【{{$lastCanUseDays}}】天后即将过期,为了确保您可要继续正常享受我们的服务,请及时续期。</p>
+                                        </div>
+                                    </th>
+                                    <th class="expander" style="Margin:0;color:#333;font-family:Helvetica,Arial,sans-serif;font-size:16px;font-weight:400;line-height:19px;margin:0;padding:0!important;text-align:left;visibility:hidden;width:0"></th>
+                                </tr>
+                                </tbody>
+                                </th></tr></tbody>
+                            </table>
+                        </td>
+                    </tr>
+                    </tbody>
+                </table>
+            </center>
+        </td>
+    </tr>
+    </tbody>
+</table>

+ 1 - 0
routes/web.php

@@ -54,6 +54,7 @@ Route::group(['middleware' => ['user', 'admin']], function() {
     Route::post('admin/setResetPasswordTimes', 'AdminController@setResetPasswordTimes'); // 设置重置密码次数
     Route::post('admin/setActiveTimes', 'AdminController@setActiveTimes'); // 设置激活账号次数
     Route::post('admin/setTrafficWarningPercent', 'AdminController@setTrafficWarningPercent'); // 设置流量警告阈值
+    Route::post('admin/setExpireDays', 'AdminController@setExpireDays'); // 设置账号过期提醒阈值
     Route::post('admin/setAddScoreRange', 'AdminController@setAddScoreRange'); // 设置登录加积分时间间隔
     Route::post('admin/setReferralTraffic', 'AdminController@setReferralTraffic'); // 设置注册送流量值
     Route::post('admin/setReferralPercent', 'AdminController@setReferralPercent'); // 设置返利比例

+ 5 - 2
sql/db.sql

@@ -213,6 +213,7 @@ CREATE TABLE `config` (
   PRIMARY KEY (`id`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COMMENT='系统配置';
 
+
 -- ----------------------------
 -- Records of config
 -- ----------------------------
@@ -240,6 +241,8 @@ INSERT INTO `config` VALUES ('21', 'referral_status', 1);
 INSERT INTO `config` VALUES ('22', 'default_traffic', 1024);
 INSERT INTO `config` VALUES ('23', 'traffic_warning', 0);
 INSERT INTO `config` VALUES ('24', 'traffic_warning_percent', 80);
+INSERT INTO `config` VALUES ('25', 'expire_warning', 0);
+INSERT INTO `config` VALUES ('26', 'expire_days', 15);
 
 
 -- ----------------------------
@@ -506,10 +509,10 @@ CREATE TABLE `email_log` (
   `title` varchar(255) DEFAULT '' COMMENT '邮件标题',
   `content` text COMMENT '邮件内容',
   `status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '状态:1-发送成功、2-发送失败',
+  `error` text COMMENT '发送失败抛出的异常信息',
   `created_at` datetime DEFAULT NULL COMMENT '创建时间',
   PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邮件投递记录';
 
 
 /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

+ 3 - 0
sql/update/20170929.sql

@@ -0,0 +1,3 @@
+ALTER TABLE `email_log` ADD COLUMN `error` text COMMENT '发送失败抛出的异常信息' AFTER `status`;
+INSERT INTO `config` VALUES ('25', 'expire_warning', 0);
+INSERT INTO `config` VALUES ('26', 'expire_days', 15);