ソースを参照

营销管理:通过消息通道推送消息给用户

bingo 7 年 前
コミット
a4d848309a

+ 2 - 2
app/Http/Controllers/AdminController.php

@@ -53,7 +53,7 @@ class AdminController extends Controller
         $view['totalUserCount'] = User::query()->count(); // 总用户数
         $view['enableUserCount'] = User::query()->where('enable', 1)->count(); // 有效用户数
         $view['activeUserCount'] = User::query()->where('t', '>=', $past)->count(); // 活跃用户数
-        $view['unActiveUserCount'] = User::query()->where('t', '<=', $past)->where('enable', 1)->count(); // 不活跃用户数
+        $view['unActiveUserCount'] = User::query()->where('t', '<=', $past)->where('enable', 1)->where('t', '>', 0)->count(); // 不活跃用户数
         $view['onlineUserCount'] = User::query()->where('t', '>=', time() - 600)->count(); // 10分钟内在线用户数
         $view['expireWarningUserCount'] = User::query()->where('expire_time', '<=', date('Y-m-d', strtotime("+" . self::$config['expire_days'] . " days")))->whereIn('status', [0, 1])->where('enable', 1)->count(); // 临近过期用户数
         $view['largeTrafficUserCount'] = User::query()->whereRaw('(u + d) >= 107374182400')->whereIn('status', [0, 1])->count(); // 流量超过100G的用户
@@ -148,7 +148,7 @@ class AdminController extends Controller
 
         // 不活跃用户
         if ($unActive) {
-            $query->where('t', '<=', strtotime(date('Y-m-d', strtotime("-" . self::$config['expire_days'] . " days"))))->where('enable', 1);
+            $query->where('t', '>', 0)->where('t', '<=', strtotime(date('Y-m-d', strtotime("-" . self::$config['expire_days'] . " days"))))->where('enable', 1);
         }
 
         // 24小时内流量异常用户

+ 100 - 0
app/Http/Controllers/MarketingController.php

@@ -0,0 +1,100 @@
+<?php
+namespace App\Http\Controllers;
+
+use App\Http\Models\Marketing;
+use GuzzleHttp\Client;
+use GuzzleHttp\Psr7;
+use GuzzleHttp\Exception\RequestException;
+use Illuminate\Http\Request;
+use Response;
+use Log;
+use DB;
+
+class MarketingController extends Controller
+{
+    protected static $config;
+
+    function __construct()
+    {
+        self::$config = $this->systemConfig();
+    }
+
+    // 邮件群发消息列表
+    public function emailList(Request $request)
+    {
+        $view['list'] = Marketing::query()->where('type', 1)->paginate(15);
+
+        return Response::view('marketing/emailList', $view);
+    }
+
+    // 消息通道群发列表
+    public function pushList(Request $request)
+    {
+        $status = $request->get('status');
+
+        $query = Marketing::query()->where('type', 2);
+
+        if ($status != '') {
+            $query->where('status', $status);
+        }
+
+        $view['list'] = $query->paginate(15);
+
+        return Response::view('marketing/pushList', $view);
+    }
+
+    // 添加推送消息
+    public function addPushMarketing(Request $request)
+    {
+        $title = trim($request->get('title'));
+        $content = $request->get('content');
+
+        if (!self::$config['is_push_bear']) {
+            return Response::json(['status' => 'fail', 'data' => '', 'message' => '推送失败:请先启用并配置PushBear']);
+        }
+
+        DB::beginTransaction();
+        try {
+            $client = new Client();
+            $response = $client->request('GET', 'https://pushbear.ftqq.com/sub', [
+                'query' => [
+                    'sendkey' => self::$config['push_bear_send_key'],
+                    'text'    => $title,
+                    'desp'    => $content
+                ]
+            ]);
+
+            $result = json_decode($response->getBody());
+            if ($result->code) { // 失败
+                $this->addMarketing(2, $title, $content, -1, $result->message);
+
+                throw new \Exception($result->message);
+            }
+
+            $this->addMarketing(2, $title, $content, 1);
+
+            DB::commit();
+
+            return Response::json(['status' => 'success', 'data' => '', 'message' => '推送成功']);
+        } catch (\Exception $e) {
+            Log::info('PushBear消息推送失败:' . $e->getMessage());
+
+            DB::rollBack();
+
+            return Response::json(['status' => 'fail', 'data' => '', 'message' => '推送失败:' . $e->getMessage()]);
+        }
+    }
+
+    private function addMarketing($type = 1, $title = '', $content = '', $status = 1, $error = '', $receiver = '')
+    {
+        $marketing = new Marketing();
+        $marketing->type = $type;
+        $marketing->receiver = $receiver;
+        $marketing->title = $title;
+        $marketing->content = $content;
+        $marketing->error = $error;
+        $marketing->status = $status;
+
+        return $marketing->save();
+    }
+}

+ 2 - 0
app/Http/Controllers/UserController.php

@@ -66,6 +66,8 @@ class UserController extends Controller
         $view['login_add_score'] = self::$config['login_add_score'];
         $view['website_analytics'] = self::$config['website_analytics'];
         $view['website_customer_service'] = self::$config['website_customer_service'];
+        $view['is_push_bear'] = self::$config['is_push_bear'];
+        $view['push_bear_qrcode'] = self::$config['push_bear_qrcode'];
 
         // 推广返利是否可见
         if (!$request->session()->has('referral_status')) {

+ 36 - 0
app/Http/Models/Marketing.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Http\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+/**
+ * 营销
+ * Class Marketing
+ *
+ * @package App\Http\Models
+ */
+class Marketing extends Model
+{
+    protected $table = 'marketing';
+    protected $primaryKey = 'id';
+    protected $appends = ['status_label'];
+
+    public function getStatusLabelAttribute()
+    {
+        $status_label = '';
+        switch ($this->attributes['status']) {
+            case -1:
+                $status_label = '失败';
+                break;
+            case 0:
+                $status_label = '待推送';
+                break;
+            case 1:
+                $status_label = '成功';
+                break;
+        }
+
+        return $status_label;
+    }
+}

+ 3 - 0
database/seeds/ConfigTableSeeder.php

@@ -69,5 +69,8 @@ class ConfigTableSeeder extends Seeder
         DB::insert("INSERT INTO `config` VALUES ('56', 'website_customer_service', '');");
         DB::insert("INSERT INTO `config` VALUES ('57', 'register_ip_limit', 5);");
         DB::insert("INSERT INTO `config` VALUES ('58', 'goods_purchase_limit_strategy', 'none');");
+        DB::insert("INSERT INTO `config` VALUES ('59', 'is_push_bear', 0);");
+        DB::insert("INSERT INTO `config` VALUES ('60', 'push_bear_send_key', '');");
+        DB::insert("INSERT INTO `config` VALUES ('61', 'push_bear_qrcode', '');");
     }
 }

+ 21 - 0
resources/views/admin/layouts.blade.php

@@ -216,6 +216,27 @@
                         </li>
                     </ul>
                 </li>
+                <li class="nav-item {{in_array(Request::path(), ['marketing/emailList', 'marketing/pushList']) ? 'active open' : ''}}">
+                    <a href="javascript:;" class="nav-link nav-toggle">
+                        <i class="fa fa-send-o"></i>
+                        <span class="title">营销管理</span>
+                        <span class="arrow"></span>
+                    </a>
+                    <ul class="sub-menu">
+                        <li class="nav-item {{in_array(Request::path(), ['marketing/emailList']) ? 'active open' : ''}}">
+                            <a href="{{url('marketing/emailList')}}" class="nav-link ">
+                                <i class="fa fa-inbox"></i>
+                                <span class="title">邮件群发</span>
+                            </a>
+                        </li>
+                        <li class="nav-item {{in_array(Request::path(), ['marketing/pushList']) ? 'active open' : ''}}">
+                            <a href="{{url('marketing/pushList')}}" class="nav-link ">
+                                <i class="fa fa-rss"></i>
+                                <span class="title">消息推送</span>
+                            </a>
+                        </li>
+                    </ul>
+                </li>
                 <li class="nav-item {{in_array(Request::path(), ['admin/decompile', 'admin/convert', 'admin/import', 'admin/trafficLog', 'admin/analysis', 'admin/subscribeLog', 'emailLog/logList', 'payment/callbackList']) ? 'active open' : ''}}">
                     <a href="javascript:;" class="nav-link nav-toggle">
                         <i class="fa fa-wrench"></i>

+ 76 - 0
resources/views/admin/system.blade.php

@@ -510,6 +510,41 @@
                                                             </div>
                                                         </div>
                                                     </div>
+                                                    <div class="form-group">
+                                                        <div class="col-md-6">
+                                                            <label for="is_push_bear" class="col-md-3 control-label">PushBear</label>
+                                                            <div class="col-md-9">
+                                                                <input type="checkbox" class="make-switch" @if($is_push_bear) checked @endif id="is_push_bear" data-on-color="success" data-off-color="danger" data-on-text="启用" data-off-text="关闭">
+                                                                <span class="help-block"> 使用PushBear批量推送微信消息给用户(<a href="https://pushbear.ftqq.com/admin/#/signin" target="_blank">创建消息通道</a>) </span>
+                                                            </div>
+                                                        </div>
+                                                        <div class="col-md-6">
+                                                            <label for="push_bear_send_key" class="col-md-3 control-label">SendKey</label>
+                                                            <div class="col-md-9">
+                                                                <div class="input-group">
+                                                                    <input class="form-control" type="text" name="push_bear_send_key" value="{{$push_bear_send_key}}" id="push_bear_send_key" placeholder="创建消息通道后即可获取" />
+                                                                    <span class="input-group-btn">
+                                                                        <button class="btn btn-success" type="button" onclick="setPushBearSendKey()">修改</button>
+                                                                    </span>
+                                                                </div>
+                                                                <span class="help-block"> 启用PushBear,请务必填入本值 </span>
+                                                            </div>
+                                                        </div>
+                                                    </div>
+                                                    <div class="form-group">
+                                                        <div class="col-md-6">
+                                                            <label for="push_bear_qrcode" class="col-md-3 control-label">订阅二维码</label>
+                                                            <div class="col-md-9">
+                                                                <div class="input-group">
+                                                                    <input class="form-control" type="text" name="push_bear_qrcode" value="{{$push_bear_qrcode}}" id="push_bear_qrcode" placeholder="填入创建好的消息通道的二维码URL" />
+                                                                    <span class="input-group-btn">
+                                                                        <button class="btn btn-success" type="button" onclick="setPushBearQrCode()">修改</button>
+                                                                    </span>
+                                                                </div>
+                                                                <span class="help-block"> 创建消息通道后,在二维码上点击右键“复制图片地址”,展示于个人中心 </span>
+                                                            </div>
+                                                        </div>
+                                                    </div>
                                                 </div>
                                             </form>
                                         </div>
@@ -931,6 +966,21 @@
             }
         });
 
+        // 启用、禁用PushBear
+        $('#is_push_bear').on({
+            'switchChange.bootstrapSwitch': function(event, state) {
+                var is_push_bear = state ? 1 : 0;
+
+                $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'is_push_bear', value:is_push_bear}, function (ret) {
+                    layer.msg(ret.message, {time:1000}, function() {
+                        if (ret.status == 'fail') {
+                            window.location.reload();
+                        }
+                    });
+                });
+            }
+        });
+
         // 启用、禁用订阅异常自动封禁
         $('#is_subscribe_ban').on({
             'switchChange.bootstrapSwitch': function(event, state) {
@@ -1113,6 +1163,32 @@
             });
         }
 
+        // 设置PushBear的SendKey
+        function setPushBearSendKey() {
+            var push_bear_send_key = $("#push_bear_send_key").val();
+
+            $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'push_bear_send_key', value:push_bear_send_key}, function (ret) {
+                layer.msg(ret.message, {time:1000}, function() {
+                    if (ret.status == 'fail') {
+                        window.location.reload();
+                    }
+                });
+            });
+        }
+
+        // 设置PushBear的消息通道二维码URL
+        function setPushBearQrCode() {
+            var push_bear_qrcode = $("#push_bear_qrcode").val();
+
+            $.post("{{url('admin/setConfig')}}", {_token:'{{csrf_token()}}', name:'push_bear_qrcode', value:push_bear_qrcode}, function (ret) {
+                layer.msg(ret.message, {time:1000}, function() {
+                    if (ret.status == 'fail') {
+                        window.location.reload();
+                    }
+                });
+            });
+        }
+
         // 设置订阅封禁阈值
         function setSubscribeBanTimes() {
             var subscribe_ban_times = $("#subscribe_ban_times").val();

+ 46 - 46
resources/views/admin/userList.blade.php

@@ -95,52 +95,52 @@
                                     @else
                                         @foreach ($userList as $user)
                                             <tr class="odd gradeX {{$user->trafficWarning ? 'danger' : ''}}">
-                                            <td> {{$user->id}} </td>
-                                            <td> {{$user->username}} </td>
-                                            <td> <span class="label label-danger"> {{$user->port}} </span> </td>
-                                            <td> <span class="label label-default"> {{$user->method}} </span> </td>
-                                            <td> <span class="label label-default"> {{$user->protocol}} </span> </td>
-                                            <td> <span class="label label-default"> {{$user->obfs}} </span> </td>
-                                            <td class="center"> {{$user->used_flow}} / {{$user->transfer_enable}} </td>
-                                            <td class="center"> {{empty($user->t) ? '未使用' : date('Y-m-d H:i:s', $user->t)}} </td>
-                                            <td class="center">
-                                                @if ($user->expireWarning)
-                                                    <span class="label label-warning"> {{$user->expire_time}} </span>
-                                                @else
-                                                    {{$user->expire_time}}
-                                                @endif
-                                            </td>
-                                            <td>
-                                                @if ($user->status == '1')
-                                                    <span class="label label-info">正常</span>
-                                                @elseif ($user->status == '0')
-                                                    <span class="label label-default">未激活</span>
-                                                @else
-                                                    <span class="label label-danger">禁用</span>
-                                                @endif
-                                            </td>
-                                            <td>
-                                                @if ($user->enable)
-                                                    <span class="label label-info">启用</span>
-                                                @else
-                                                    <span class="label label-danger">禁用</span>
-                                                @endif
-                                            </td>
-                                            <td>
-                                                <button type="button" class="btn btn-sm blue btn-outline" onclick="editUser('{{$user->id}}')">
-                                                    <i class="fa fa-pencil"></i>
-                                                </button>
-                                                <button type="button" class="btn btn-sm green btn-outline" onclick="doExport('{{$user->id}}')">
-                                                    <i class="fa fa-paper-plane-o"></i>
-                                                </button>
-                                                <button type="button" class="btn btn-sm purple btn-outline" onclick="doMonitor('{{$user->id}}')">
-                                                    <i class="fa fa-area-chart"></i>
-                                                </button>
-                                                <button type="button" class="btn btn-sm green-meadow btn-outline" onclick="resetTraffic('{{$user->id}}')">
-                                                    <i class="fa fa-refresh"></i>
-                                                </button>
-                                            </td>
-                                        </tr>
+                                                <td> {{$user->id}} </td>
+                                                <td> {{$user->username}} </td>
+                                                <td> <span class="label label-danger"> {{$user->port}} </span> </td>
+                                                <td> <span class="label label-default"> {{$user->method}} </span> </td>
+                                                <td> <span class="label label-default"> {{$user->protocol}} </span> </td>
+                                                <td> <span class="label label-default"> {{$user->obfs}} </span> </td>
+                                                <td class="center"> {{$user->used_flow}} / {{$user->transfer_enable}} </td>
+                                                <td class="center"> {{empty($user->t) ? '未使用' : date('Y-m-d H:i:s', $user->t)}} </td>
+                                                <td class="center">
+                                                    @if ($user->expireWarning)
+                                                        <span class="label label-warning"> {{$user->expire_time}} </span>
+                                                    @else
+                                                        {{$user->expire_time}}
+                                                    @endif
+                                                </td>
+                                                <td>
+                                                    @if ($user->status == '1')
+                                                        <span class="label label-info">正常</span>
+                                                    @elseif ($user->status == '0')
+                                                        <span class="label label-default">未激活</span>
+                                                    @else
+                                                        <span class="label label-danger">禁用</span>
+                                                    @endif
+                                                </td>
+                                                <td>
+                                                    @if ($user->enable)
+                                                        <span class="label label-info">启用</span>
+                                                    @else
+                                                        <span class="label label-danger">禁用</span>
+                                                    @endif
+                                                </td>
+                                                <td>
+                                                    <button type="button" class="btn btn-sm blue btn-outline" onclick="editUser('{{$user->id}}')">
+                                                        <i class="fa fa-pencil"></i>
+                                                    </button>
+                                                    <button type="button" class="btn btn-sm green btn-outline" onclick="doExport('{{$user->id}}')">
+                                                        <i class="fa fa-paper-plane-o"></i>
+                                                    </button>
+                                                    <button type="button" class="btn btn-sm purple btn-outline" onclick="doMonitor('{{$user->id}}')">
+                                                        <i class="fa fa-area-chart"></i>
+                                                    </button>
+                                                    <button type="button" class="btn btn-sm green-meadow btn-outline" onclick="resetTraffic('{{$user->id}}')">
+                                                        <i class="fa fa-refresh"></i>
+                                                    </button>
+                                                </td>
+                                            </tr>
                                         @endforeach
                                     @endif
                                 </tbody>

+ 100 - 0
resources/views/marketing/emailList.blade.php

@@ -0,0 +1,100 @@
+@extends('admin.layouts')
+
+@section('css')
+    <link href="/assets/global/plugins/datatables/datatables.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/datatables/plugins/bootstrap/datatables.bootstrap.css" rel="stylesheet" type="text/css" />
+@endsection
+@section('title', '控制面板')
+@section('content')
+    <!-- BEGIN CONTENT BODY -->
+    <div class="page-content" style="padding-top:0;">
+        <!-- BEGIN PAGE BASE CONTENT -->
+        <div class="row">
+            <div class="col-md-12">
+                <!-- BEGIN EXAMPLE TABLE PORTLET-->
+                <div class="portlet light bordered">
+                    <div class="portlet-title">
+                        <div class="caption font-dark">
+                            <span class="caption-subject bold uppercase"> 邮件群发列表 </span>
+                        </div>
+                        <div class="actions">
+                            <div class="btn-group btn-group-devided">
+                                <button class="btn sbold blue" onclick="send()"> 群发邮件 </button>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="portlet-body">
+                        <div class="row">
+                            <div class="col-md-2 col-sm-2">
+                                <select class="form-control input-sm" name="status" id="status" onChange="doSearch()">
+                                    <option value="" @if(Request::get('status') == '') selected @endif>状态</option>
+                                    <option value="0" @if(Request::get('status') == '0') selected @endif>等待发送</option>
+                                    <option value="-1" @if(Request::get('status') == '-1') selected @endif>失败</option>
+                                    <option value="1" @if(Request::get('status') == '1') selected @endif>成功</option>
+                                </select>
+                            </div>
+                            <div class="col-md-2 col-sm-2">
+                                <button type="button" class="btn btn-sm blue" onclick="doSearch();">查询</button>
+                                <button type="button" class="btn btn-sm grey" onclick="doReset();">重置</button>
+                            </div>
+                        </div>
+                        <div class="table-scrollable table-scrollable-borderless">
+                            <table class="table table-hover table-light">
+                                <thead>
+                                <tr>
+                                    <th> # </th>
+                                    <th> 消息标题 </th>
+                                    <th> 消息内容 </th>
+                                    <th> 发送状态 </th>
+                                    <th> 发送时间 </th>
+                                    <th> 错误信息 </th>
+                                </tr>
+                                </thead>
+                                <tbody>
+                                @if ($list->isEmpty())
+                                    <tr>
+                                        <td colspan="6" style="text-align: center;">暂无数据</td>
+                                    </tr>
+                                @else
+                                    @foreach($list as $vo)
+                                        <tr class="odd gradeX">
+                                            <td> {{$vo->id}} </td>
+                                            <td> {{$vo->title}} </td>
+                                            <td> {{$vo->content}} </td>
+                                            <td> {{$vo->status_label}} </td>
+                                            <td> {{$vo->created_at}} </td>
+                                            <td> {{$vo->error}} </td>
+                                        </tr>
+                                    @endforeach
+                                @endif
+                                </tbody>
+                            </table>
+                        </div>
+                        <div class="row">
+                            <div class="col-md-4 col-sm-4">
+                                <div class="dataTables_info" role="status" aria-live="polite">共 {{$list->total()}} 条消息</div>
+                            </div>
+                            <div class="col-md-8 col-sm-8">
+                                <div class="dataTables_paginate paging_bootstrap_full_number pull-right">
+                                    {{ $list->links() }}
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <!-- END EXAMPLE TABLE PORTLET-->
+            </div>
+        </div>
+        <!-- END PAGE BASE CONTENT -->
+    </div>
+    <!-- END CONTENT BODY -->
+@endsection
+@section('script')
+    <script src="/js/layer/layer.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        // 发送邮件
+        function send() {
+            layer.msg("开发中,敬请期待");
+        }
+    </script>
+@endsection

+ 187 - 0
resources/views/marketing/pushList.blade.php

@@ -0,0 +1,187 @@
+@extends('admin.layouts')
+
+@section('css')
+    <link href="/assets/global/plugins/datatables/datatables.min.css" rel="stylesheet" type="text/css" />
+    <link href="/assets/global/plugins/datatables/plugins/bootstrap/datatables.bootstrap.css" rel="stylesheet" type="text/css" />
+@endsection
+@section('title', '控制面板')
+@section('content')
+    <!-- BEGIN CONTENT BODY -->
+    <div class="page-content" style="padding-top:0;">
+        <!-- BEGIN PAGE BASE CONTENT -->
+        <div class="row">
+            <div class="col-md-12">
+                <!-- BEGIN EXAMPLE TABLE PORTLET-->
+                <div class="portlet light bordered">
+                    <div class="portlet-title">
+                        <div class="caption font-dark">
+                            <span class="caption-subject bold uppercase"> 推送消息列表 </span>
+                        </div>
+                        <div class="actions">
+                            <div class="btn-group btn-group-devided">
+                                <button class="btn sbold blue" data-toggle="modal" data-target="#send_modal"> 推送消息 </button>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="portlet-body">
+                        <div class="alert alert-info">
+                            <i class="fa fa-warning"></i> 仅会推送给关注了您的消息通道的用户 (<a href="{{url('admin/system')}}" target="_blank">设置PushBear</a>)
+                        </div>
+                        <div class="row">
+                            <div class="col-md-2 col-sm-2">
+                                <select class="form-control input-sm" name="status" id="status" onChange="doSearch()">
+                                    <option value="" @if(Request::get('status') == '') selected @endif>状态</option>
+                                    <option value="0" @if(Request::get('status') == '0') selected @endif>待发送</option>
+                                    <option value="-1" @if(Request::get('status') == '-1') selected @endif>失败</option>
+                                    <option value="1" @if(Request::get('status') == '1') selected @endif>成功</option>
+                                </select>
+                            </div>
+                            <div class="col-md-2 col-sm-2">
+                                <button type="button" class="btn btn-sm blue" onclick="doSearch();">查询</button>
+                                <button type="button" class="btn btn-sm grey" onclick="doReset();">重置</button>
+                            </div>
+                        </div>
+                        <div class="table-scrollable table-scrollable-borderless">
+                            <table class="table table-hover table-light">
+                                <thead>
+                                    <tr>
+                                        <th> # </th>
+                                        <th> 消息标题 </th>
+                                        <th> 消息内容 </th>
+                                        <th> 推送状态 </th>
+                                        <th> 推送时间 </th>
+                                        <th> 错误信息 </th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                @if ($list->isEmpty())
+                                    <tr>
+                                        <td colspan="6" style="text-align: center;">暂无数据</td>
+                                    </tr>
+                                @else
+                                    @foreach($list as $vo)
+                                        <tr class="odd gradeX">
+                                            <td> {{$vo->id}} </td>
+                                            <td> {{$vo->title}} </td>
+                                            <td> {{$vo->content}} </td>
+                                            <td> {{$vo->status_label}} </td>
+                                            <td> {{$vo->created_at}} </td>
+                                            <td> {{$vo->error}} </td>
+                                        </tr>
+                                    @endforeach
+                                @endif
+                                </tbody>
+                            </table>
+                        </div>
+                        <div class="row">
+                            <div class="col-md-4 col-sm-4">
+                                <div class="dataTables_info" role="status" aria-live="polite">共 {{$list->total()}} 条推送消息</div>
+                            </div>
+                            <div class="col-md-8 col-sm-8">
+                                <div class="dataTables_paginate paging_bootstrap_full_number pull-right">
+                                    {{ $list->links() }}
+                                </div>
+                            </div>
+                        </div>
+
+                        <!-- 推送消息 -->
+                        <div id="send_modal" class="modal fade" tabindex="-1" data-focus-on="input:first" data-backdrop="static" data-keyboard="false">
+                            <div class="modal-dialog">
+                                <div class="modal-content">
+                                    <div class="modal-header">
+                                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true"></button>
+                                        <h4 class="modal-title">推送消息</h4>
+                                    </div>
+                                    <div class="modal-body">
+                                        <div class="alert alert-danger" style="display: none;" id="msg"></div>
+                                        <!-- BEGIN FORM-->
+                                        <form action="#" method="post" class="form-horizontal">
+                                            <div class="form-body">
+                                                <div class="form-group">
+                                                    <label for="title" class="col-md-2 control-label"> 标题 </label>
+                                                    <div class="col-md-9">
+                                                        <input type="text" class="form-control" name="title" id="title" placeholder="">
+                                                    </div>
+                                                </div>
+                                                <div class="form-group">
+                                                    <label for="content" class="col-md-2 control-label"> 内容 </label>
+                                                    <div class="col-md-9">
+                                                        <textarea class="form-control" rows="6" name="content" id="content"></textarea>
+                                                        <span class="help-block"> 内容支持<a href="https://maxiang.io/" target="_blank">Markdown语法</a> </span>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </form>
+                                        <!-- END FORM-->
+                                    </div>
+                                    <div class="modal-footer">
+                                        <button type="button" data-dismiss="modal" class="btn dark btn-outline">取消</button>
+                                        <button type="button" class="btn red btn-outline" onclick="return send();">推送</button>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <!-- END EXAMPLE TABLE PORTLET-->
+            </div>
+        </div>
+        <!-- END PAGE BASE CONTENT -->
+    </div>
+    <!-- END CONTENT BODY -->
+@endsection
+@section('script')
+    <script src="/js/layer/layer.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        // 发送通道消息
+        function send() {
+            var _token = '{{csrf_token()}}';
+            var title = $("#title").val();
+            var content = $("#content").val();
+
+            if (title == '') {
+                $("#msg").show().html("标题不能为空");
+                $("#title").focus();
+                return false;
+            }
+
+            $.ajax({
+                url:'{{url('marketing/addPushMarketing')}}',
+                type:"POST",
+                data:{_token:_token, title:title, content:content},
+                beforeSend:function(){
+                    $("#msg").show().html("正在添加...");
+                },
+                success:function(ret){
+                    if (ret.status == 'fail') {
+                        $("#msg").show().html(ret.message);
+                        return false;
+                    }
+
+                    $("#send_modal").modal("hide");
+
+                },
+                error:function(){
+                    $("#msg").show().html("请求错误,请重试");
+                },
+                complete:function(){}
+            });
+        }
+
+        // 关闭modal触发
+        $('#send_modal').on('hide.bs.modal', function () {
+            window.location.reload();
+        });
+
+
+        function doSearch() {
+            var status = $("#status").val();
+
+            window.location.href = "{{url('marketing/pushList?status=')}}" + status;
+        }
+
+        function doReset() {
+            window.location.href = "{{url('marketing/pushList')}}";
+        }
+    </script>
+@endsection

+ 17 - 0
resources/views/user/index.blade.php

@@ -69,6 +69,18 @@
                 </div>
             </div>
             <div class="col-md-4">
+                @if($is_push_bear && $push_bear_qrcode)
+                <ul class="list-group">
+                    <li class="list-group-item">
+                        <div style="text-align: center">
+                            <span> 微信扫码订阅,获取本站最新资讯 </span>
+                            <br><br>
+                            <div id="subscribe_qrcode" style="text-align: center;"></div>
+                        </div>
+                    </li>
+                </ul>
+                @endif
+
                 <ul class="list-group">
                     <li class="list-group-item">
                         {{trans('home.account_status')}}:{{$info['enable'] ? trans('home.enabled') : trans('home.disabled') }}
@@ -330,5 +342,10 @@
         function show(txt) {
             layer.msg(txt);
         }
+
+        // 生成消息通道订阅二维码
+        @if($is_push_bear && $push_bear_qrcode)
+            $('#subscribe_qrcode').qrcode({render:"canvas", text:"{{$push_bear_qrcode}}", width:170, height:170});
+        @endif
     </script>
 @endsection

+ 2 - 2
resources/views/user/orderList.blade.php

@@ -28,7 +28,7 @@
                                         <th> {{trans('home.invoice_table_name')}} </th>
                                         <th> {{trans('home.invoice_table_price')}} </th>
                                         <th> {{trans('home.invoice_table_create_date')}} </th>
-										<th> {{trans('home.invoice_table_expire_at')}} </th>
+					                    <th> {{trans('home.invoice_table_expire_at')}} </th>
                                         <th> {{trans('home.invoice_table_status')}} </th>
                                     </tr>
                                 </thead>
@@ -45,7 +45,7 @@
                                             <td>{{empty($order->goods) ? '【商品已删除】' : $order->goods->name}}</td>
                                             <td>¥{{$order->amount}}</td>
                                             <td>{{$order->created_at}}</td>
-											<td>{{$order->is_expire ? '已过期' : $order->expire_at}}</td>
+					                        <td>{{$order->expire_at}}</td>
                                             <td>
                                                 @if(!$order->is_expire)
                                                     @if($order->status == -1)

+ 3 - 0
routes/web.php

@@ -91,6 +91,9 @@ Route::group(['middleware' => ['forbidden', 'user', 'admin']], function () {
     Route::post("admin/switchToUser", "AdminController@switchToUser"); // 转换成某个用户的身份
     Route::any("admin/decompile", "AdminController@decompile"); // SS(R)链接反解析
     Route::any("payment/callbackList", "PaymentController@callbackList"); // 有赞云支付回调日志
+    Route::get("marketing/emailList", "MarketingController@emailList"); // 邮件消息列表
+    Route::get("marketing/pushList", "MarketingController@pushList"); // 推送消息列表
+    Route::post("marketing/addPushMarketing", "MarketingController@addPushMarketing"); // 推送消息
 });
 
 Route::group(['middleware' => ['forbidden', 'user']], function () {

+ 15 - 0
sql/db.sql

@@ -329,6 +329,9 @@ INSERT INTO `config` VALUES ('55', 'website_analytics', '');
 INSERT INTO `config` VALUES ('56', 'website_customer_service', '');
 INSERT INTO `config` VALUES ('57', 'register_ip_limit', 5);
 INSERT INTO `config` VALUES ('58', 'goods_purchase_limit_strategy', 'none');
+INSERT INTO `config` VALUES ('59', 'is_push_bear', 0);
+INSERT INTO `config` VALUES ('60', 'push_bear_send_key', '');
+INSERT INTO `config` VALUES ('61', 'push_bear_qrcode', '');
 
 
 -- ----------------------------
@@ -886,6 +889,18 @@ CREATE TABLE `payment_callback` (
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='有赞云回调日志';
 
+CREATE TABLE `marketing` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `type` TINYINT(4) NOT NULL COMMENT '类型:1-邮件群发、2-订阅渠道群发',
+  `receiver` TEXT NOT NULL COMMENT '接收者' COLLATE 'utf8mb4_unicode_ci',
+  `title` VARCHAR(255) NOT NULL COMMENT '标题' COLLATE 'utf8mb4_unicode_ci',
+  `content` TEXT NOT NULL COMMENT '内容' COLLATE 'utf8mb4_unicode_ci',
+  `error` VARCHAR(255) NULL COMMENT '错误信息' COLLATE 'utf8mb4_unicode_ci',
+  `status` TINYINT(4) NOT NULL COMMENT '状态:-1-失败、0-待发送、1-成功',
+  `created_at` DATETIME NOT NULL,
+  `updated_at` DATETIME NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE='utf8mb4_unicode_ci';
 
 
 /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

+ 17 - 0
sql/update/20180626.sql

@@ -0,0 +1,17 @@
+INSERT INTO `config` VALUES ('59', 'is_push_bear', 0);
+INSERT INTO `config` VALUES ('60', 'push_bear_send_key', '');
+INSERT INTO `config` VALUES ('61', 'push_bear_qrcode', '');
+
+
+CREATE TABLE `marketing` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `type` TINYINT(4) NOT NULL COMMENT '类型:1-邮件群发、2-订阅渠道群发',
+  `receiver` TEXT NOT NULL COMMENT '接收者' COLLATE 'utf8mb4_unicode_ci',
+  `title` VARCHAR(255) NOT NULL COMMENT '标题' COLLATE 'utf8mb4_unicode_ci',
+  `content` TEXT NOT NULL COMMENT '内容' COLLATE 'utf8mb4_unicode_ci',
+  `error` VARCHAR(255) NULL COMMENT '错误信息' COLLATE 'utf8mb4_unicode_ci',
+  `status` TINYINT(4) NOT NULL COMMENT '状态:-1-失败、0-待发送、1-成功',
+  `created_at` DATETIME NOT NULL,
+  `updated_at` DATETIME NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE='utf8mb4_unicode_ci';