Browse Source

Merge branch 'master' of http://git.caihongxingqiu.net/rainbow/cms-service

duqinya 5 years ago
parent
commit
12fcb38d65
60 changed files with 2855 additions and 0 deletions
  1. 29 0
      .env.example
  2. 8 0
      .gitignore
  3. 84 0
      .rocketeer/config.php
  4. 40 0
      .rocketeer/hooks.php
  5. 24 0
      .rocketeer/paths.php
  6. 80 0
      .rocketeer/remote.php
  7. 35 0
      .rocketeer/scm.php
  8. 19 0
      .rocketeer/stages.php
  9. 61 0
      .rocketeer/strategies.php
  10. 86 0
      app/Console/Commands/apollo.php
  11. 30 0
      app/Console/Kernel.php
  12. 123 0
      app/Helper/helper.php
  13. 10 0
      app/Http/Controllers/AuthController.php
  14. 9 0
      app/Http/Controllers/BaseController.php
  15. 71 0
      app/Http/Controllers/CmsContentTemplateSetController.php
  16. 54 0
      app/Http/Controllers/Controller.php
  17. 54 0
      app/Http/Controllers/V2/Controller.php
  18. 51 0
      app/Http/Controllers/V2/FloorController.php
  19. 32 0
      app/Http/Controllers/V2/MemberGroupController.php
  20. 77 0
      app/Http/Middleware/JwtAuthMiddleware.php
  21. 12 0
      app/Models/BaseModel.php
  22. 22 0
      app/Models/CmsContentTemplate.php
  23. 26 0
      app/Models/CmsContentTemplateSet.php
  24. 26 0
      app/Models/CmsSubject.php
  25. 26 0
      app/Models/CmsSubjectProduct.php
  26. 16 0
      app/Models/Floor.php
  27. 24 0
      app/Models/MemberGroup.php
  28. 19 0
      app/Models/MemberGroupInfo.php
  29. 24 0
      app/Models/TopicGroup.php
  30. 19 0
      app/Models/TopicGroupInfo.php
  31. 24 0
      app/Models/VideoGroup.php
  32. 19 0
      app/Models/VideoGroupInfo.php
  33. 173 0
      app/Repositories/CmsContentTemplateSetRepository.php
  34. 167 0
      app/Repositories/FloorRepository.php
  35. 58 0
      app/Repositories/MemberGroupRepository.php
  36. 99 0
      app/Traits/UserTrait.php
  37. 30 0
      app/Transformers/FloorTransformer.php
  38. 117 0
      bootstrap/app.php
  39. 56 0
      composer.json
  40. 10 0
      config/apollo.php
  41. 10 0
      config/constants.php
  42. 7 0
      config/customer.tpl
  43. 131 0
      config/database.php
  44. 304 0
      config/jwt.php
  45. 37 0
      database/migrations/2019_04_28_022240_create_table_subject_table.php
  46. 33 0
      database/migrations/2019_04_28_025052_create_cms_subject_product_table.php
  47. 36 0
      database/migrations/2019_04_28_025655_create_cms_content_template_table.php
  48. 33 0
      database/migrations/2019_04_28_030611_create_cms_content_template_set_table.php
  49. 32 0
      database/migrations/2019_04_28_072523_add_cms_subject_product_table.php
  50. 33 0
      database/migrations/2019_04_29_060908_add_template_id_to_cms_content_template_set_table.php
  51. 32 0
      database/migrations/2019_04_30_075256_add_deleted_at_to_cms_content_template_set_table.php
  52. 32 0
      database/migrations/2019_05_22_015511_add_delete_to_cms_content_template_table.php
  53. 32 0
      database/migrations/2019_05_22_085041_add_status_to_cms_content_template_table.php
  54. 20 0
      deploy/nginx/conf.d/app.beta.conf
  55. 20 0
      deploy/nginx/conf.d/app.dev.conf
  56. 2 0
      deploy/php/local.ini
  57. 32 0
      docker-compose-beta.yml
  58. 31 0
      docker-compose-dev.yml
  59. 54 0
      routes/api.php
  60. 0 0
      storage/.gitkeep

+ 29 - 0
.env.example

@@ -0,0 +1,29 @@
+APP_NAME=cms-service
+APP_ENV=local
+APP_KEY=
+APP_DEBUG=true
+APP_URL=http://localhost
+APP_TIMEZONE=UTC
+
+LOG_CHANNEL=stack
+LOG_SLACK_WEBHOOK_URL=
+
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=homestead
+DB_USERNAME=homestead
+DB_PASSWORD=secret
+
+CACHE_DRIVER=file
+QUEUE_CONNECTION=sync
+
+ELASTICSEARCH_HOST=localhost
+ELASTICSEARCH_PORT=9200
+ELASTICSEARCH_USER=
+ELASTICSEARCH_PASS=
+
+APP_ID=chxq-platform
+CLUSTER=default
+APOLLO_NAMESPACES="application,cms-service"
+APOLLO_CONFIG_SERVER=http://127.0.0.1:18080

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+/vendor
+/.idea
+Homestead.json
+Homestead.yaml
+.env
+/storage/*
+composer.lock
+/config/customer.php

+ 84 - 0
.rocketeer/config.php

@@ -0,0 +1,84 @@
+<?php
+
+use Rocketeer\Services\Connections\ConnectionsHandler;
+
+return [
+
+    // The name of the application to deploy
+    // This will create a folder of the same name in the root directory
+    // configured above, so be careful about the characters used
+    'application_name' => 'cms-service',
+
+    // Plugins
+    ////////////////////////////////////////////////////////////////////
+
+    // The plugins to load
+    'plugins'          => [// 'Rocketeer\Plugins\Slack\RocketeerSlack',
+    ],
+
+    // Logging
+    ////////////////////////////////////////////////////////////////////
+
+    // The schema to use to name log files
+    'logs'             => function (ConnectionsHandler $connections) {
+        return sprintf('%s-%s-%s.log', $connections->getConnection(), $connections->getStage(), date('Ymd'));
+    },
+
+    // Remote access
+    //
+    // You can either use a single connection or an array of connections
+    ////////////////////////////////////////////////////////////////////
+
+    // The default remote connection(s) to execute tasks on
+    'default'          => ['production'],
+
+    // The various connections you defined
+    // You can leave all of this empty or remove it entirely if you don't want
+    // to track files with credentials : Rocketeer will prompt you for your credentials
+    // and store them locally
+    'connections'      => [
+        'production' => [
+            'host'      => '47.92.174.125:2345',
+            'username'  => 'root',
+            'password'  => '',
+            'key'       => '/root/.ssh/id_rsa',
+            'keyphrase' => '',
+            'agent'     => '',
+            'db_role'   => true,
+        ],
+    ],
+
+    /*
+     * In most multiserver scenarios, migrations must be run in an exclusive server.
+     * In the event of not having a separate database server (in which case it can
+     * be handled through connections), you can assign a 'db_role' => true to the
+     * server's configuration and it will only run the migrations in that specific
+     * server at the time of deployment.
+     */
+    'use_roles'        => false,
+
+    // Contextual options
+    //
+    // In this section you can fine-tune the above configuration according
+    // to the stage or connection currently in use.
+    // Per example :
+    // 'stages' => array(
+    // 	'staging' => array(
+    // 		'scm' => array('branch' => 'staging'),
+    // 	),
+    //  'production' => array(
+    //    'scm' => array('branch' => 'master'),
+    //  ),
+    // ),
+    ////////////////////////////////////////////////////////////////////
+
+    'on'               => [
+
+        // Stages configurations
+        'stages'      => [],
+        // Connections configuration
+        'connections' => [],
+
+    ],
+
+];

+ 40 - 0
.rocketeer/hooks.php

@@ -0,0 +1,40 @@
+<?php
+
+return [
+
+    // Tasks
+    //
+    // Here you can define in the `before` and `after` array, Tasks to execute
+    // before or after the core Rocketeer Tasks. You can either put a simple command,
+    // a closure which receives a $task object, or the name of a class extending
+    // the Rocketeer\Abstracts\AbstractTask class
+    //
+    // In the `custom` array you can list custom Tasks classes to be added
+    // to Rocketeer. Those will then be available in the command line
+    // with all the other tasks
+    //////////////////////////////////////////////////////////////////////
+
+    // Tasks to execute before the core Rocketeer Tasks
+    'before' => [
+        'setup'   => [],
+        'deploy'  => [],
+        'cleanup' => [],
+    ],
+
+    // Tasks to execute after the core Rocketeer Tasks
+    'after'  => [
+        'setup'   => [],
+        'deploy'  => [
+            'composer dumpautoload',
+            'php artisan route:clear',
+            'chmod -R 777 bootstrap',
+            'chmod -R 777 storage',
+            'chmod -R 777 public'
+        ],
+        'cleanup' => [],
+    ],
+
+    // Custom Tasks to register with Rocketeer
+    'custom' => [],
+
+];

+ 24 - 0
.rocketeer/paths.php

@@ -0,0 +1,24 @@
+<?php
+
+return [
+
+    // Configurable paths
+    //
+    // Here you can manually set paths to some commands Rocketeer
+    // might try to use, if you leave those empty it will try to find them
+    // manually or assume they're in the root folder
+    //
+    // You can also add in this file custom paths for any command or binary
+    // Rocketeer might go looking for
+    ////////////////////////////////////////////////////////////////////
+
+    // Path to the PHP binary
+    'php'      => '/usr/local/php/bin/php',
+
+    // Path to Composer
+    'composer' => '/usr/local/bin/composer',
+
+    // Path to the Artisan CLI
+    'artisan'  => 'artisan',
+
+];

+ 80 - 0
.rocketeer/remote.php

@@ -0,0 +1,80 @@
+<?php
+
+return [
+
+    // Remote server
+    //////////////////////////////////////////////////////////////////////
+
+    // Variables about the servers. Those can be guessed but in
+    // case of problem it's best to input those manually
+    'variables'      => [
+        'directory_separator' => '/',
+        'line_endings'        => "\n",
+    ],
+
+    // The number of releases to keep at all times
+    'keep_releases'  => 4,
+
+    // Folders
+    ////////////////////////////////////////////////////////////////////
+
+    // The root directory where your applications will be deployed
+    // This path *needs* to start at the root, ie. start with a /
+    'root_directory' => '/data/wwwroot/beta',
+
+    // The folder the application will be cloned in
+    // Leave empty to use `application_name` as your folder name
+    'app_directory'  => '',
+
+    // A list of folders/file to be shared between releases
+    // Use this to list folders that need to keep their state, like
+    // user uploaded data, file-based databases, etc.
+    'shared'         => [
+        'storage',
+        'vendor',
+        '.env',
+    ],
+
+    // Execution
+    //////////////////////////////////////////////////////////////////////
+
+    // If enabled will force a shell to be created
+    // which is required for some tools like RVM or NVM
+    'shell'          => false,
+
+    // An array of commands to run under shell
+    'shelled'        => ['which', 'ruby', 'npm', 'bower', 'bundle', 'grunt'],
+
+    // Enable use of sudo for some commands
+    // You can specify a sudo user by doing
+    // 'sudo' => 'the_user'
+    'sudo' => false,
+
+    // An array of commands to run under sudo
+    'sudoed' => [],
+
+    // Permissions$
+    ////////////////////////////////////////////////////////////////////
+
+    'permissions'    => [
+
+        // The folders and files to set as web writable
+        'files'    => [
+            'bootstrap/cache',
+            'storage',
+        ],
+
+        // Here you can configure what actions will be executed to set
+        // permissions on the folder above. The Closure can return
+        // a single command as a string or an array of commands
+        'callback' => function ($task, $file) {
+            return [
+                sprintf('chmod -R 777 %s', $file),
+                sprintf('chmod -R g+s %s', $file),
+                sprintf('chown -R www:www %s', $file),
+            ];
+        },
+
+    ],
+
+];

+ 35 - 0
.rocketeer/scm.php

@@ -0,0 +1,35 @@
+<?php
+
+return [
+
+    // SCM repository
+    //////////////////////////////////////////////////////////////////////
+
+    // The SCM used (supported: "git", "svn")
+    'scm'        => 'git',
+
+    // The SSH/HTTPS address to your repository
+    // Example: https://github.com/vendor/website.git
+    'repository' => 'http://caihongxingqiu:Ch20151002@git.caihongxingqiu.net/rainbow/cms-service.git',
+
+    // The repository credentials : you can leave those empty
+    // if you're using SSH or if your repository is public
+    // In other cases you can leave this empty too, and you will
+    // be prompted for the credentials on deploy. If you don't want
+    // to be prompted (public repo, etc) set the values to null
+    'username'   => 'null',
+    'password'   => 'null',
+
+    // The branch to deploy
+    'branch'     => 'master',
+
+    // Whether your SCM should do a "shallow" clone of the repository
+    // or not – this means a clone with just the latest state of your
+    // application (no history)
+    // If you're having problems cloning, try setting this to false
+    'shallow'    => true,
+
+    // Recursively pull in submodules. Works only with GIT.
+    'submodules' => true,
+
+];

+ 19 - 0
.rocketeer/stages.php

@@ -0,0 +1,19 @@
+<?php
+
+return [
+
+    // Stages
+    //
+    // The multiples stages of your application
+    // if you don't know what this does, then you don't need it
+    //////////////////////////////////////////////////////////////////////
+
+    // Adding entries to this array will split the remote folder in stages
+    // Like /var/www/yourapp/staging and /var/www/yourapp/production
+    'stages'  => [],
+
+    // The default stage to execute tasks on when --stage is not provided
+    // Falsey means all of them
+    'default' => '',
+
+];

+ 61 - 0
.rocketeer/strategies.php

@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of Rocketeer
+ *
+ * (c) Maxime Fabre <ehtnam6@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Rocketeer\Binaries\PackageManagers\Composer;
+use Rocketeer\Tasks\Subtasks\Primer;
+
+return [
+
+    // Task strategies
+    //
+    // Here you can configure in a modular way which tasks to use to
+    // execute various core parts of your deployment's flow
+    //////////////////////////////////////////////////////////////////////
+
+    // Which strategy to use to check the server
+    'check'        => 'Php',
+
+    // Which strategy to use to create a new release
+    'deploy'       => 'Clone',
+
+    // Which strategy to use to test your application
+    'test'         => 'Phpunit',
+
+    // Which strategy to use to migrate your database
+    'migrate'      => 'Artisan',
+
+    // Which strategy to use to install your application's dependencies
+    'dependencies' => 'Composer',
+
+    // Execution hooks
+    //////////////////////////////////////////////////////////////////////
+
+    'composer'     => [
+        'install' => function (Composer $composer, $task) {
+            return $composer->install([], ['--no-interaction' => null, '--prefer-dist' => null]);
+        },
+        'update'  => function (Composer $composer) {
+            return $composer->update();
+        },
+    ],
+
+    // Here you can configure the Primer tasks
+    // which will run a set of commands on the local
+    // machine, determining whether the deploy can proceed
+    // or not
+    'primer'       => function (Primer $task) {
+        return [
+            // $task->executeTask('Test'),
+            // $task->binary('grunt')->execute('lint'),
+        ];
+    },
+
+];

+ 86 - 0
app/Console/Commands/apollo.php

@@ -0,0 +1,86 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Log;
+use Org\Multilinguals\Apollo\Client\ApolloClient;
+
+class Apollo extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'apollo';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Apollo配置服务';
+
+
+    private $apollo;
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+
+        $this->save_dir = storage_path('apollo');
+        $this->config_tpl = config_path() . '/customer.tpl';
+        $this->config_file = config_path() . '/customer.php';
+
+        $this->apollo = new ApolloClient(
+            config('apollo.config_server'),
+            config('apollo.app_id'),
+            config('apollo.namespaces')
+        );
+        $this->apollo->save_dir = $this->save_dir;
+        if(!is_dir($this->save_dir)){
+            mkdir($this->save_dir);
+        }
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $this->line('apollo配置服务开启');
+        $restart = true; //失败自动重启
+        do {
+            $error = $this->apollo->start(function () {
+                $list = glob($this->save_dir . DIRECTORY_SEPARATOR . 'apolloConfig.*');
+                $apollo = [];
+                foreach ($list as $l) {
+                    $config = require $l;
+                    if (is_array($config) && isset($config['configurations'])) {
+                        $apollo = array_merge($apollo, $config['configurations']);
+                    }
+                }
+                if (!$apollo) {
+                    Log::error('Load Apollo Config Failed, no config available');
+                }
+                $tpl = file_get_contents($this->config_tpl);
+                foreach ($apollo as $key =>$value){
+                    $tpl = str_replace('{'.$key.'}',$value,$tpl);
+                }
+                file_put_contents($this->config_file,$tpl);
+            }); //此处传入回调
+            if ($error) {
+                Log::info("Apollo Hand error :" . $error);
+            }
+            sleep(60);
+        } while ($error && $restart);
+    }
+}

+ 30 - 0
app/Console/Kernel.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Console;
+
+use App\Console\Commands\Apollo;
+use Illuminate\Console\Scheduling\Schedule;
+use Laravel\Lumen\Console\Kernel as ConsoleKernel;
+
+class Kernel extends ConsoleKernel
+{
+    /**
+     * The Artisan commands provided by your application.
+     *
+     * @var array
+     */
+    protected $commands = [
+        Apollo::class
+    ];
+
+    /**
+     * Define the application's command schedule.
+     *
+     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
+     * @return void
+     */
+    protected function schedule(Schedule $schedule)
+    {
+        //
+    }
+}

+ 123 - 0
app/Helper/helper.php

@@ -0,0 +1,123 @@
+<?php
+/**
+ * 添加自定义辅助函数
+ */
+
+if ( ! function_exists('config_path'))
+{
+    /**
+     * Get the configuration path.
+     *
+     * @param string $path
+     * @return string
+     */
+    function config_path($path = '')
+    {
+        return app()->basePath() . '/config' . ($path ? '/' . $path : $path);
+    }
+}
+
+/**
+ * 参数签名校验
+ * @param array $params
+ * @return string
+ */
+function generateSign(array $params, $secret_key)
+{
+    unset($params['sign']);
+    // 将删除参数组中所有等值为FALSE的参数(包括:NULL, 空字符串,0, false)
+    // $params = array_filter($params);
+
+    // 按照键名对参数数组进行升序排序
+    ksort($params);
+
+    // 给参数数组追加密钥,键名为 key, 值为签名配置中配置的 secret_key 的值
+    $params['chxq_key'] = $secret_key;
+    \Illuminate\Support\Facades\Log::debug($params);
+    // 生成 URL-encode 之后的请求字符串
+    $str = http_build_query($params);
+    $str = urldecode($str);
+    \Illuminate\Support\Facades\Log::debug($str);
+    //$str = "address=计算机啊手机壳阿看见手机卡&address_type=1&area_id=2&area_name=西安市&city_id=2&city_name=西安市&contact_mobile
+    //=18458881890&contact_name=刘德华&province_id=1&province_name=陕西省&uid=0&zipcode=1000000";
+    // 将请求字符串使用MD5加密后,再转换成大写,并返回
+    return strtoupper(MD5($str));
+}
+
+/**
+ * 验证签名
+ * @param $sign
+ * @param $params
+ * @return bool
+ */
+function verifySign($sign, $params, $secret_key)
+{
+    if ($sign == generateSign($params, $secret_key)) {
+        return true;
+    } else {
+        return false;
+    };
+
+}
+
+
+function http($url, $param, $method = 'post')
+{
+    try {
+        $client = new \GuzzleHttp\Client();
+        $response = $client->request($method, $url, $param);
+        $result = json_decode($response->getBody()->getContents(), true);
+        if ($result['code'] == 0) {
+            return $result['data'];
+        } else {
+            return [];
+        }
+    } catch (\Exception $exception) {
+        return [];
+    }
+
+}
+
+function jsonSuccess($data = [], $msg = "成功")
+{
+    $response = array(
+        'code' => 0,
+        'msg' => $msg,
+        'data' => new stdClass()
+    );
+    if ($data) {
+        if (is_array($data)) {
+            //带有分页格式转换
+            if (isset($data['meta'])) {
+                // 更改元数据格式,全部包含在data下
+                $temp = array(
+                    'data' => array(
+                        'data' => $data['data'],
+                        'pagination' => $data['meta']['pagination']
+                    )
+                );
+                $response = array_merge($response, $temp);
+            } elseif(isset($data['data'])) {
+                $response = array_merge($response, $data);
+            }else{
+                $temp = array(
+                    'data' => $data
+                );
+                $response = array_merge($response, $temp);
+            }
+        } else {
+            $response['data'] = $data;
+        }
+    }
+    return $response;
+}
+
+function jsonError($msg)
+{
+    $response = array(
+        'code' => 1,
+        'msg' => $msg,
+        'data' => ""
+    );
+    return $response;
+}

+ 10 - 0
app/Http/Controllers/AuthController.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App\Http\Controllers;
+
+
+class  AuthController extends BaseController
+{
+
+
+}

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

@@ -0,0 +1,9 @@
+<?php
+
+namespace App\Http\Controllers;
+
+
+class BaseController extends Controller
+{
+
+}

+ 71 - 0
app/Http/Controllers/CmsContentTemplateSetController.php

@@ -0,0 +1,71 @@
+<?php
+namespace App\Http\Controllers;
+
+use App\Repositories\CmsContentTemplateSetRepository;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Validator;
+/**
+ * Created by PhpStorm.
+ * User: qinyaer
+ * Date: 2019/5/5
+ * Time: 上午9:30
+ */
+
+class CmsContentTemplateSetController extends BaseController
+{
+    public function __construct(CmsContentTemplateSetRepository $cmsContentTemplateSetRepository)
+    {
+        $this->cmsContentSetTemplate = $cmsContentTemplateSetRepository;
+    }
+
+    //内容预览
+    public function preview(Request $request)
+    {
+        $validator = Validator::make($request->all(), [
+            'city_id' => 'required|integer',
+            'type' => 'required|integer',
+        ]);
+        if ($validator->fails()) {
+            return $this->jsonError($validator->errors()->first());
+        }
+
+        return  $this->jsonSuccess($this->cmsContentSetTemplate->preview($request->all()));
+
+    }
+
+    /**
+     * 获取城市模板名称及类型
+     * @param Request $request
+     * @return array
+     */
+    public function template(Request $request){
+        $validator = Validator::make($request->all(), [
+            'city_id' => 'required|integer'
+        ]);
+        if ($validator->fails()) {
+            return $this->jsonError($validator->errors()->first());
+        }
+        return $this->jsonSuccess($this->cmsContentSetTemplate->getTemplate($request->only('city_id')));
+    }
+
+    //获取商品列表
+    public function productList(Request $request)
+    {
+        $validator = Validator::make($request->all(), [
+            'subject_id' => 'required|integer',
+        ]);
+        if ($validator->fails()) {
+            return $this->jsonError($validator->errors()->first());
+        }
+
+        $product_list = $this->cmsContentSetTemplate->productList($request->all());
+        if ($product_list){
+            return $this->jsonSuccess($product_list);
+        }else{
+            return $this->jsonError('没有找到对应商品');
+        }
+    }
+
+
+
+}

+ 54 - 0
app/Http/Controllers/Controller.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Dingo\Api\Routing\Helpers;
+use Illuminate\Routing\Controller as BaseController;
+
+class Controller extends BaseController
+{
+    use Helpers;
+    public function jsonSuccess($data = [], $msg = "成功")
+    {
+        $response = array(
+            'code' => 0,
+            'msg' => $msg,
+            'data' => []
+        );
+        if ($data) {
+            if (is_array($data)) {
+                //带有分页格式转换
+                if (isset($data['meta'])) {
+                    // 更改元数据格式,全部包含在data下
+                    $temp = array(
+                        'data' => array(
+                            'data' => $data['data'],
+                            'pagination' => $data['meta']['pagination']
+                        )
+                    );
+                    $response = array_merge($response, $temp);
+                } elseif (isset($data['data'])) {
+                    $response = array_merge($response, $data);
+                } else {
+                    $temp = array(
+                        'data' => $data
+                    );
+                    $response = array_merge($response, $temp);
+                }
+            } else {
+                $response['data'] = $data;
+            }
+        }
+        return $response;
+    }
+
+    public function jsonError($msg)
+    {
+        $response = array(
+            'code' => 1,
+            'msg' => $msg,
+            'data' => ""
+        );
+        return $response;
+    }
+}

+ 54 - 0
app/Http/Controllers/V2/Controller.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace App\Http\Controllers\V2;
+
+use Dingo\Api\Routing\Helpers;
+use Illuminate\Routing\Controller as BaseController;
+
+class Controller extends BaseController
+{
+    use Helpers;
+    public function jsonSuccess($data = [], $msg = "成功")
+    {
+        $response = array(
+            'code' => 0,
+            'msg' => $msg,
+            'data' => []
+        );
+        if ($data) {
+            if (is_array($data)) {
+                //带有分页格式转换
+                if (isset($data['meta'])) {
+                    // 更改元数据格式,全部包含在data下
+                    $temp = array(
+                        'data' => array(
+                            'data' => $data['data'],
+                            'pagination' => $data['meta']['pagination']
+                        )
+                    );
+                    $response = array_merge($response, $temp);
+                } elseif (isset($data['data'])) {
+                    $response = array_merge($response, $data);
+                } else {
+                    $temp = array(
+                        'data' => $data
+                    );
+                    $response = array_merge($response, $temp);
+                }
+            } else {
+                $response['data'] = $data;
+            }
+        }
+        return $response;
+    }
+
+    public function jsonError($msg)
+    {
+        $response = array(
+            'code' => 1,
+            'msg' => $msg,
+            'data' => ""
+        );
+        return $response;
+    }
+}

+ 51 - 0
app/Http/Controllers/V2/FloorController.php

@@ -0,0 +1,51 @@
+<?php
+namespace App\Http\Controllers\V2;
+use App\Http\Controllers\BaseController;
+use App\Repositories\FloorRepository;
+use App\Transformers\FloorTransformer;
+use Illuminate\Http\Request;
+use League\Fractal\Resource\Collection;
+use League\Fractal\Manager;
+use League\Fractal\Pagination\IlluminatePaginatorAdapter;
+/**
+ * Created by PhpStorm.
+ * User: durong
+ * Date: 2019/6/15
+ * Time: 下午4:30
+ */
+
+class FloorController extends BaseController
+{
+    public function __construct(FloorRepository $floorRepository)
+    {
+        $this->floorRepository = $floorRepository;
+    }
+
+    //楼层列表
+    public function index(Request $request)
+    {
+        $floor = $this->floorRepository->index($request->all());
+        $fractal = new Manager();
+        $resource = new Collection($floor , new FloorTransformer());
+        $resource->setPaginator(new IlluminatePaginatorAdapter($floor));
+        $data = $fractal->createData($resource)->toArray();
+        return $this->jsonSuccess($data);
+    }
+
+    /**
+     * 推荐内容列表
+     */
+    public function info()
+    {
+        return $this->floorRepository->info();
+    }
+
+    /**
+     * 热门视频ids
+     */
+    public function hotVideoIds()
+    {
+        return $this->floorRepository->hotVideoIds();
+    }
+
+}

+ 32 - 0
app/Http/Controllers/V2/MemberGroupController.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Administrator
+ * Date: 2019-06-20
+ * Time: 14:18
+ */
+
+namespace App\Http\Controllers\V2;
+
+
+use App\Repositories\MemberGroupRepository;
+use Illuminate\Support\Facades\Validator;
+use Illuminate\Http\Request;
+
+
+class MemberGroupController extends Controller {
+    public function __construct(MemberGroupRepository $memberGroupRepository) {
+        $this->memberGroupRepository = $memberGroupRepository;
+    }
+    //获取推荐用户列表
+    public function memberList(Request $request){
+        $data = $request->all();
+        $validator = Validator::make($data, [
+            'take_count' => 'integer|max:50',
+        ]);
+        if ($validator->fails()) {
+            return $this->jsonError($validator->errors()->first());
+        }
+       return $this->memberGroupRepository->isSuggestMember($data['take_count']);
+    }
+}

+ 77 - 0
app/Http/Middleware/JwtAuthMiddleware.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Support\Facades\Redis;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Exceptions\TokenExpiredException;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+use Tymon\JWTAuth\Facades\JWTAuth;
+
+class JwtAuthMiddleware
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request $request
+     * @param  \Closure $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+
+        try {
+            $token = JWTAuth::getToken();
+            if(empty($token)){
+                $error = [
+                    'message' => 'token is required',
+                    'code' => 401,
+                ];
+                return response()->json($error)->setStatusCode(401);
+            }
+            $data = JWTAuth::decode($token)['user'];
+            //检测token是否存在黑名单中
+            if(Redis::zscore('blacklist_token', $token->get())){
+                $error = [
+                    'message' => 'token in blacklist',
+                    'code' => 401,
+                ];
+                return response()->json($error)->setStatusCode(401);
+            }
+            if ($data->sign !== md5($data->uid . config('customer.jwt_secret'))) {
+                $error = [
+                    'message' => 'request is not allow',
+                    'code' => 401,
+                ];
+                return response()->json($error);
+            }
+        } catch (TokenExpiredException $e) {
+            $error = [
+                'message' => 'Token is Expired',
+                'code' => 401,
+            ];
+            return response()->json($error)->setStatusCode(401);
+        } catch (TokenInvalidException $e) {
+            $error = [
+                'message' => $e->getMessage(),
+                'code' => 401,
+            ];
+            return response()->json($error)->setStatusCode(401);
+        } catch (JWTException $e) {
+            $error = [
+                'message' => $e->getMessage(),
+                'code' => 401,
+            ];
+
+            return response()->json($error)->setStatusCode(401);
+        }catch (\Exception $e){
+            $error = [
+                'message' => $e->getMessage(),
+                'code' => 401,
+            ];
+            return response()->json($error)->setStatusCode(401);
+        }
+        return $next($request);
+    }
+}

+ 12 - 0
app/Models/BaseModel.php

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

+ 22 - 0
app/Models/CmsContentTemplate.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Models;
+
+class CmsContentTemplate extends BaseModel
+{
+    protected  $table = 'cms_content_template';
+
+
+    /**
+     * 可被批量赋值的字段
+     * @var array
+     */
+    protected $fillable = ['city_id','city_name','title','apply_type','is_open'];
+
+    //一对多关联模板内容表
+    public function cmsContentTemplateSet()
+    {
+        return $this->hasMany('App\Models\CmsContentTemplateSet','tpl_id');
+    }
+
+}

+ 26 - 0
app/Models/CmsContentTemplateSet.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Models;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class CmsContentTemplateSet extends BaseModel
+{
+    use SoftDeletes;
+
+    protected $dates = ['deleted_at'];
+    protected  $table = 'cms_content_template_set';
+
+
+    /**
+     * 可被批量赋值的字段
+     * @var array
+     */
+    protected $fillable = ['rule','tpl_id','area_type'];
+
+    //一对多关联模板内容表
+    public function cmsContentTemplate()
+    {
+        return $this->belongsTo('App\Models\CmsContentTemplate','tpl_id');
+    }
+
+}

+ 26 - 0
app/Models/CmsSubject.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Models;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class CmsSubject extends BaseModel
+{
+    use SoftDeletes;
+
+    protected $dates = ['deleted_at'];
+    protected  $table = 'cms_subject';
+
+
+    /**
+     * 可被批量赋值的字段
+     * @var array
+     */
+    protected $fillable = ['city_id','city_name','title','show_type','is_open'];
+
+    //一对多关联专题商品表
+    public function cmsSubjectProduct()
+    {
+        return $this->hasMany('App\Models\CmsSubjectProduct','subject_id');
+    }
+
+}

+ 26 - 0
app/Models/CmsSubjectProduct.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Models;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class CmsSubjectProduct extends BaseModel
+{
+    use SoftDeletes;
+
+    protected $dates = ['deleted_at'];
+    protected  $table = 'cms_subject_product';
+
+
+    /**
+     * 可被批量赋值的字段
+     * @var array
+     */
+    protected $fillable = ['product_id','sort','subject_id'];
+
+    //相对关联到专题表
+    public function cmsSubject()
+    {
+        return $this->belongsTo('App\Models\CmsSubject','subject_id');
+    }
+
+}

+ 16 - 0
app/Models/Floor.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Models;
+
+
+class Floor extends BaseModel
+{
+    protected  $table = 'cms_floor';
+    /**
+     * 可被批量赋值的字段
+     * @var array
+     */
+    protected $fillable = ['name','floor_location','group_ids','floor_type','is_open'];
+
+
+}

+ 24 - 0
app/Models/MemberGroup.php

@@ -0,0 +1,24 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Administrator
+ * Date: 2019/6/20
+ * Time: 9:45
+ */
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class MemberGroup extends Model
+{
+    //
+    use SoftDeletes;
+    protected $table = 'member_group';
+    protected $guarded = [];
+
+    public function info()
+    {
+        return $this->hasMany('App\Models\MemberGroupInfo', 'id', 'member_group_id');
+    }
+}

+ 19 - 0
app/Models/MemberGroupInfo.php

@@ -0,0 +1,19 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Administrator
+ * Date: 2019/6/20
+ * Time: 9:45
+ */
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class MemberGroupInfo extends Model
+{
+    //
+    use SoftDeletes;
+    protected $table = 'member_group_info';
+    protected $guarded = [];
+}

+ 24 - 0
app/Models/TopicGroup.php

@@ -0,0 +1,24 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Administrator
+ * Date: 2019/6/19
+ * Time: 14:34
+ */
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class TopicGroup extends Model
+{
+    //
+    use SoftDeletes;
+    protected $table = 'topic_group';
+    protected $guarded = [];
+
+    public function info()
+    {
+        return $this->hasMany('App\Models\TopicGroupInfo', 'id', 'topic_group_id');
+    }
+}

+ 19 - 0
app/Models/TopicGroupInfo.php

@@ -0,0 +1,19 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Administrator
+ * Date: 2019/6/19
+ * Time: 14:35
+ */
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class TopicGroupInfo extends Model
+{
+    //
+    use SoftDeletes;
+    protected $table = 'topic_group_info';
+    protected $guarded = [];
+}

+ 24 - 0
app/Models/VideoGroup.php

@@ -0,0 +1,24 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Administrator
+ * Date: 2019/6/19
+ * Time: 15:26
+ */
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class VideoGroup extends Model
+{
+    //
+    use SoftDeletes;
+    protected $table = 'video_group';
+    protected $guarded = [];
+
+    public function info()
+    {
+        return $this->hasMany('App\Models\VideoGroupInfo', 'id', 'video_group_id');
+    }
+}

+ 19 - 0
app/Models/VideoGroupInfo.php

@@ -0,0 +1,19 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Administrator
+ * Date: 2019/6/19
+ * Time: 15:27
+ */
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class VideoGroupInfo extends Model
+{
+    //
+    use SoftDeletes;
+    protected $table = 'video_group_info';
+    protected $guarded = [];
+}

+ 173 - 0
app/Repositories/CmsContentTemplateSetRepository.php

@@ -0,0 +1,173 @@
+<?php
+
+namespace App\Repositories;
+
+use App\Models\CmsContentTemplate;
+use App\Models\CmsContentTemplateSet;
+use App\Models\CmsSubjectProduct;
+use App\Models\CmsSubject;
+use Illuminate\Support\Facades\Cache;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+use Tymon\JWTAuth\Facades\JWTAuth;
+
+class CmsContentTemplateSetRepository
+{
+    public function __construct(CmsContentTemplateSet $cmsContentTemplateSet, CmsContentTemplate $cmsContentTemplate, CmsSubjectProduct $cmsSubjectProduct, CmsSubject $cmsSubject)
+    {
+
+        $this->cmsContentTemplateSet = $cmsContentTemplateSet;
+        $this->cmsContentTemplate = $cmsContentTemplate;
+        $this->cmsSubjectProduct = $cmsSubjectProduct;
+        $this->cmsSubject = $cmsSubject;
+    }
+
+    /**
+     * 内容预览
+     */
+    public function preview($request)
+    {
+        $where = [
+            'city_id' => $request['city_id'],
+            'apply_type' => $request['type'],
+            'is_open' => 1,
+            'status'=>1,
+            'deleted_at'=>null
+        ];
+        $temalates = $this->cmsContentTemplate->select('title', 'id')->where($where)->orderBy('id','desc')->first();
+        if (!$temalates) {
+            throw new HttpException(500, '没有找到对应模板');
+        }
+
+        $group_array = [];
+        $group_key = config('constants.CMS_GROUP');
+        $market_key = config('constants.CMS_MARKET');
+        //团购首页
+        if ($request['type'] == 0) {
+            if (Cache::has($group_key)) {
+              return Cache::store('redis')->get($group_key);
+            }
+            $group_array['apply_type'] = "group";
+            $group_array['title'] = $temalates->title;
+            $group_array['content'] = [];
+        }else {//菜市场首页
+            if (Cache::has($market_key)) {
+                return Cache::store('redis')->get($market_key);
+            }
+            $group_array['apply_type'] = "market";
+            $group_array['title'] = $temalates->title;
+            $group_array['content'] = [];
+        }
+
+        $group_array['content'][0]['area_type'] = "banner";
+
+        $banner_rule = $this->cmsContentTemplateSet->select('id', 'rule')->where(['tpl_id' => $temalates->id, 'area_type' => 0,'status'=>1])->get();
+        $new_rule = [];
+        foreach ($banner_rule->toArray() as $k => $v) {
+            $decode_banner = \GuzzleHttp\json_decode($v['rule'], true);
+            if (count($decode_banner)>0) {
+                foreach ($decode_banner as $key=>$value){
+                    $new_rule[$key]['id'] = $key;
+                    $new_rule[$key]['rule'] = $value;
+                    $new_rule[$key]['rule']['b_id'] = strval($v['id']);
+                    $new_rule[$key]['rule']['link_url'] = strval($value['link_url']);//强转link_url类型
+                }
+            }
+        }
+        $group_array['content'][0]['rule'] = $new_rule;
+
+        $subject_rule = $this->cmsContentTemplateSet->select('id', 'rule')->where(['tpl_id' => $temalates->id, 'area_type' => 1,'status'=>1])->get();
+
+        $new_rule1 = [];
+        foreach ($subject_rule->toArray() as $k => $v) {
+            $new_rule1[$k]['id'] = $v['id'];
+            $decode_subject = \GuzzleHttp\json_decode($v['rule'], true);
+            $decode_subject['link_url'] = strval($decode_subject['link_url']);//强转link_url类型
+            $new_rule1[$k]['rule'] = $decode_subject;
+        }
+
+        $floor_rule = $this->cmsContentTemplateSet->select('id', 'rule')->where(['tpl_id' => $temalates->id, 'area_type' => 2,'status'=>1])->get();
+        $new_rule2 = [];
+        foreach ($floor_rule->toArray() as $k => $v) {
+            $new_rule2[$k]['id'] = $v['id'];
+            $decode_floor = \GuzzleHttp\json_decode($v['rule'], true);
+            $decode_floor['link_url'] = strval($decode_floor['link_url']);//强转link_url类型
+            $new_rule2[$k]['rule'] = $decode_floor;
+        }
+
+        foreach ($new_rule2 as $k => $v) {
+            if ($v) {
+                $rules = $v['rule'];
+                $show_num = intval($rules['show_num']);
+                $show_type = $this->cmsSubject->select('show_type')->where('id', $rules['link_url'])->first();
+                $product = $this->cmsSubjectProduct->where('subject_id', $rules['link_url'])->orderBy('sort', 'asc')->limit($show_num)->get();
+                $pro_array = $product->toArray();
+                $res_id = implode(",", array_column($pro_array, 'product_id'));
+                $new_rule2[$k]['product_id'] = $res_id;
+                $new_rule2[$k]['subject_id'] = strval($rules['link_url']);
+                $new_rule2[$k]['show_type'] = $show_type->show_type ?? '';
+                unset($v['url']);
+                unset($v['show_num']);
+                unset($rules['link_url']);
+                unset($v['link_type']);
+            }
+        }
+
+        if ($request['type'] == 1) {
+            $group_array['content'][1]['area_type'] = "category";
+            $category_rule = $this->cmsContentTemplateSet->select('id', 'rule')->where(['tpl_id' => $temalates->id, 'area_type' => 3,'status'=>1])->get();
+            $new_rule3 = [];
+            foreach ($category_rule->toArray() as $k => $v) {
+                $new_rule3[$k]['id'] = $v['id'];
+                $decode_category = \GuzzleHttp\json_decode($v['rule'], true);
+                $decode_category['link_url'] = strval($decode_category['link_url']);//强转link_url类型
+                $new_rule3[$k]['rule'] = $decode_category;
+            }
+
+            $group_array['content'][1]['rule'] = $new_rule3;
+            $group_array['content'][2]['area_type'] = "special";
+            $group_array['content'][2]['rule'] = $new_rule1;
+            $group_array['content'][3]['area_type'] = "floor";
+            $group_array['content'][3]['rule'] = $new_rule2;
+
+            if (!Cache::has($market_key)) {
+                Cache::store('redis')->put($market_key, $group_array, 600);//10分钟过期
+            }
+
+        } else {
+            $group_array['content'][1]['area_type'] = "special";
+            $group_array['content'][1]['rule'] = $new_rule1;
+            $group_array['content'][2]['area_type'] = "floor";
+            $group_array['content'][2]['rule'] = $new_rule2;
+
+            if (!Cache::has($group_key)) {
+                Cache::store('redis')->put($group_key, $group_array, 600);
+            }
+        }
+        return $group_array;
+    }
+
+    public function getTemplate($cityId)
+    {
+        return $this->cmsContentTemplate->where(['city_id' => $cityId, 'is_open' => 1,'status'=>1])->where('deleted_at', null)->select('id','title', 'apply_type')->orderBy('apply_type', 'asc')->get();
+    }
+
+    public function productList($request)
+    {
+        $product = $this->cmsSubjectProduct->where('subject_id', $request['subject_id'])->orderBy('sort', 'asc')->get();
+        $pro_array = $product->toArray();
+        $res_id = implode(",", array_column($pro_array, 'product_id'));
+
+        try {
+            $sign = generateSign(['ids' => $res_id], config('customer.app_secret'));
+            $url = config("customer.app_service_url") . '/product/homeProduct';
+            $array = [
+                'json' => ['sign' => $sign, 'ids' => $res_id], 'query' => [], 'http_errors' => false, 'headers' => ['Authorization' => "Bearer " . JWTAuth::getToken()]
+            ];
+
+            return http($url, $array, 'get');
+        } catch (\Exception $e) {
+            return [];
+        }
+
+    }
+}

+ 167 - 0
app/Repositories/FloorRepository.php

@@ -0,0 +1,167 @@
+<?php
+namespace App\Repositories;
+use App\Models\Floor;
+use App\Models\MemberGroup;
+use App\Models\MemberGroupInfo;
+use App\Models\TopicGroup;
+use App\Models\TopicGroupInfo;
+use App\Models\VideoGroup;
+use App\Models\VideoGroupInfo;
+use App\Traits\UserTrait;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Redis;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+use Dingo\Api\Http\Response;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Database\QueryException;
+
+/**
+ * Created by PhpStorm.
+ * User: durong
+ * Date: 2019/6/15
+ * Time: 下午4:36
+ */
+
+class FloorRepository
+{
+    use UserTrait;
+    public function __construct(Floor $floor,
+                                MemberGroup $memberGroup,
+                                MemberGroupInfo $memberGroupInfo,
+                                VideoGroup $videoGroup,
+                                VideoGroupInfo $videoGroupInfo,
+                                TopicGroup $topicGroup,
+                                TopicGroupInfo $topicGroupInfo)
+    {
+        $this->floor = $floor;
+        $this->memberGroup = $memberGroup;
+        $this->memberGroupInfo = $memberGroupInfo;
+        $this->videoGroup = $videoGroup;
+        $this->videoGroupInfo = $videoGroupInfo;
+        $this->topicGroup = $topicGroup;
+        $this->topicGroupInfo = $topicGroupInfo;
+    }
+
+    public function index($request)
+    {
+        $perPage = isset($request['per_page']) ? $request['per_page'] : 20;
+        $where = [];
+        $where[] = ['is_open', 1];
+
+        return $this->floor->where($where)->orderBy('floor_location', 'asc')->paginate($perPage);
+    }
+
+    public function info()
+    {
+        Log::debug('推荐内容楼层-开始请求');
+        $userInfo = $this->getUserInfo();
+        if(!$userInfo){
+            return jsonError('获取用户信息失败');
+        }
+        Log::debug('推荐内容楼层-用户信息'.json_encode($userInfo));
+        $floor = $this->floor
+            ->where('is_open', 1)
+            ->whereIn('floor_type', [0,1,2,3])
+            ->whereBetween('floor_location', [1,20])
+            ->get();
+        Log::debug('推荐内容楼层-楼层列表'.json_encode($floor));
+        $data = [];
+        foreach($floor as $item){
+            if($item->floor_type == 0){
+                //banner
+                $banner = $this->getBanner($item->group_ids);
+                Log::debug('推荐内容楼层-banner'.json_encode($banner));
+                if($banner){
+                    $data[$item->floor_location] = [
+                        'show_type' => 'banner',
+                        'data' => $banner
+                    ];
+                }
+            }elseif($item->floor_type == 1){
+                //user
+                $memberIds = $this->memberGroup
+                    ->join('member_group_info', '.member_group_id', '=', 'member_group.id')
+                    ->where('member_group.id', $item->group_ids)
+                    ->orderBy('member_group_info.sort', 'asc')
+                    ->limit(20)
+                    ->pluck('member_group_info.uid')
+                    ->toArray();
+                if(!$memberIds) continue;
+                $memberIds = implode($memberIds, ',');
+                $member = $this->getMemberGroup($memberIds);
+                Log::debug('推荐内容楼层-user'.json_encode($member));
+                if(!$member) continue;
+                $data[$item->floor_location] = [
+                    'show_type' => 'user',
+                    'data' => $member
+                ];
+            }elseif($item->floor_type == 2){
+                //video
+                $videoIds = $this->videoGroup
+                    ->join('video_group_info', 'video_group_info.video_group_id', '=', 'video_group.id')
+                    ->where('video_group.id', $item->group_ids)
+                    ->orderBy('video_group_info.sort', 'asc')
+                    ->limit(20)
+                    ->pluck('video_group_info.post_id')
+                    ->toArray();
+                if(!$videoIds) continue;
+                $videoIds = implode($videoIds, ',');
+                $video = $this->getPostVideo($videoIds);
+                Log::debug('推荐内容楼层-video'.json_encode($video));
+                if(!$video) continue;
+                $data[$item->floor_location] = [
+                    'show_type' => 'video',
+                    'data' => $video
+                ];
+            }elseif($item->floor_type == 3){
+                //topic
+                $topicIds = $this->topicGroup
+                    ->join('topic_group_info', 'topic_group_info.topic_group_id', '=', 'topic_group.id')
+                    ->where('topic_group.id', $item->group_ids)
+                    ->limit(20)
+                    ->pluck('topic_group_info.topic_id')
+                    ->toArray();
+                if(!$topicIds) continue;
+                $topicIds = implode($topicIds, ',');
+                Log::debug('推荐内容楼层-topicIds'.$topicIds);
+                $topic = $this->getTopic($topicIds);
+                Log::debug('推荐内容楼层-topic'.json_encode($topic));
+                if(!$topic) continue;
+                $data[$item->floor_location] = [
+                    'show_type' => 'topic',
+                    'data' => $topic
+                ];
+            }
+        }
+        Log::debug('推荐内容楼层-data'.json_encode($data));
+        return jsonSuccess($data);
+    }
+
+    public function hotVideoIds()
+    {
+        $id = $this->floor
+            ->where('is_open', 1)
+            ->where('floor_type', 2)
+            ->value('group_ids');
+        $data = '';
+        if($id){
+            //video
+            $videoIds = $this->videoGroup
+                ->join('video_group_info', 'video_group_info.video_group_id', '=', 'video_group.id')
+                ->where('video_group.id', $id)
+                ->orderBy('video_group_info.sort', 'asc')
+                ->pluck('video_group_info.post_id')
+                ->toArray();
+            if($videoIds){
+                $data = implode($videoIds, ',');
+                $key = 'hotVideoIds';
+                Redis::SET($key, $data);
+                Redis::EXPIRE($key, 600);
+            }
+        }
+        Log::debug('热门视频ids'.$data);
+        return jsonSuccess($data);
+    }
+
+}
+

+ 58 - 0
app/Repositories/MemberGroupRepository.php

@@ -0,0 +1,58 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Administrator
+ * Date: 2019-06-20
+ * Time: 14:23
+ */
+
+namespace App\Repositories;
+
+
+use App\Models\MemberGroup;
+use App\Models\MemberGroupInfo;
+use App\Traits\UserTrait;
+
+class MemberGroupRepository {
+    use UserTrait;
+    public function __construct(MemberGroup $memberGroup,MemberGroupInfo $memberGroupInfo) {
+        $this->memberGroup = $memberGroup;
+        $this->memberGroupInfo = $memberGroupInfo;
+    }
+
+    /**
+     * @param $request
+     * @return array
+     * 获取推荐用户
+     */
+    public function isSuggestMember($request){
+       $group = $this->memberGroup->where('is_suggest',1)->first();
+       $take = 50;
+       if(!empty($request['take_count'])){
+           $take = $request['take_count'];
+       }
+       if($group){
+           $groupInfo = $this->memberGroupInfo
+               ->where('member_group_id',$group->id)
+               ->orderBy('sort')
+               ->take($take)
+               ->select('uid','sort')
+               ->get();
+           if($groupInfo){
+               $array  = $groupInfo->toArray();
+               $userData = [];
+               foreach ($array as $key=>$value){
+                   $userData[] = $value['uid'];
+               }
+               //请求用户接口获取用户信息
+               $uids = implode(',',$userData);
+               $data = $this->getMemberSortIdList($uids);
+               if($data){
+                   return jsonSuccess(['list'=>$data]);
+               }
+               return jsonSuccess();
+           }
+       }
+       return jsonSuccess();
+    }
+}

+ 99 - 0
app/Traits/UserTrait.php

@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * Created by PhpStorm.
+ * User: wangzhiqiang
+ * Date: 2019/5/5
+ * Time: 17:11
+ */
+namespace App\Traits;
+use Tymon\JWTAuth\Facades\JWTAuth;
+
+trait UserTrait
+{
+
+    public function getUserInfo() {
+        try {
+            $sign = generateSign([], config('customer.app_secret'));
+            $url = config("customer.app_service_url").'/user/userInfo';
+            $array = [
+                'json' => ['sign' => $sign], 'query' => [], 'http_errors' => false,'headers'=>['Authorization'=>"Bearer ".JWTAuth::getToken()]
+            ];
+            return http($url,$array);
+        } catch (\Exception $e) {
+            return [];
+        }
+
+    }
+
+    public function getBanner($ids) {
+        try {
+            $sign = generateSign(['ids' => $ids], config('customer.app_secret'));
+            $url = config("customer.app_service_url").'/config/v2/bannerSet/lists';
+            $array = [
+                'json' => ['sign' => $sign, 'ids' => $ids], 'query' => [], 'http_errors' => false,'headers'=>['Authorization'=>"Bearer ".JWTAuth::getToken()]
+            ];
+            return http($url,$array,'get');
+        } catch (\Exception $e) {
+            return [];
+        }
+
+    }
+
+    public function getTopic($ids) {
+        try {
+            $sign = generateSign(['ids' => $ids], config('customer.app_secret'));
+            $url = config("customer.app_service_url").'/community/topic/group';
+            $array = [
+                'json' => ['sign' => $sign, 'ids' => $ids], 'query' => [], 'http_errors' => false,'headers'=>['Authorization'=>"Bearer ".JWTAuth::getToken()]
+            ];
+            return http($url,$array,'get');
+        } catch (\Exception $e) {
+            return [];
+        }
+
+    }
+
+    public function getPostVideo($ids) {
+        try {
+            $sign = generateSign(['ids' => $ids], config('customer.app_secret'));
+            $url = config("customer.app_service_url").'/community/post/video/group';
+            $array = [
+                'json' => ['sign' => $sign, 'ids' => $ids], 'query' => [], 'http_errors' => false,'headers'=>['Authorization'=>"Bearer ".JWTAuth::getToken()]
+            ];
+            return http($url,$array,'get');
+        } catch (\Exception $e) {
+            return [];
+        }
+
+    }
+
+    public function getMemberGroup($ids) {
+        try {
+            $sign = generateSign(['ids' => $ids], config('customer.app_secret'));
+            $url = config("customer.app_service_url").'/user/userGroup';
+            $array = [
+                'json' => ['sign' => $sign, 'ids' => $ids], 'query' => [], 'http_errors' => false,'headers'=>['Authorization'=>"Bearer ".JWTAuth::getToken()]
+            ];
+            return http($url,$array,'get');
+        } catch (\Exception $e) {
+            return [];
+        }
+
+    }
+    //获取推荐用户根据id排序
+    public function getMemberSortIdList($ids) {
+        try {
+            $sign = generateSign(['uids' => $ids], config('customer.app_secret'));
+            $url = config("customer.app_service_url").'/user/v2/member/memberSortIdList';
+            //$url = 'http://localhost:8080/v2/member/memberSortIdList';
+            $array = [
+                'json' => ['sign' => $sign, 'uids' => $ids], 'query' => [], 'http_errors' => false,'headers'=>['Authorization'=>"Bearer ".JWTAuth::getToken()]
+            ];
+            return http($url,$array,'get');
+        } catch (\Exception $e) {
+            return [];
+        }
+
+    }
+}

+ 30 - 0
app/Transformers/FloorTransformer.php

@@ -0,0 +1,30 @@
+<?php
+namespace App\Transformers;
+/**
+ * Created by PhpStorm.
+ * User: durong
+ * Date: 2019/6/15
+ * Time: 下午4:32
+ */
+
+use App\Models\Floor;
+use League\Fractal\TransformerAbstract;
+
+class FloorTransformer extends TransformerAbstract
+{
+
+    public function transform(Floor $floor)
+    {
+        $showType = [
+            0 => 'banner',
+            1 => 'user',
+            2 => 'video',
+            3 => 'topic',
+        ];
+        return [
+            'floor_location'    => $floor['floor_location'],
+            'group_ids'    => $floor['group_ids'],
+            'show_type'    => $showType[$floor['floor_type']],
+        ];
+    }
+}

+ 117 - 0
bootstrap/app.php

@@ -0,0 +1,117 @@
+<?php
+
+require_once __DIR__.'/../vendor/autoload.php';
+
+(new Laravel\Lumen\Bootstrap\LoadEnvironmentVariables(
+    dirname(__DIR__)
+))->bootstrap();
+
+/*
+|--------------------------------------------------------------------------
+| Create The Application
+|--------------------------------------------------------------------------
+|
+| Here we will load the environment and create the application instance
+| that serves as the central piece of this framework. We'll use this
+| application as an "IoC" container and router for this framework.
+|
+*/
+
+$app = new Laravel\Lumen\Application(
+    dirname(__DIR__)
+);
+
+$app->withFacades();
+
+$app->withEloquent();
+
+$app->configure('api');
+$app->configure('apollo');
+$app->configure('auth');
+$app->configure('customer');
+$app->configure('jwt');
+$app->configure('elasticsearch');
+$app->configure('database');
+$app->configure('constants');
+/*
+|--------------------------------------------------------------------------
+| Register Container Bindings
+|--------------------------------------------------------------------------
+|
+| Now we will register a few bindings in the service container. We will
+| register the exception handler and the console kernel. You may add
+| your own bindings here if you like or you can make another file.
+|
+*/
+
+$app->singleton(
+    Illuminate\Contracts\Debug\ExceptionHandler::class,
+    App\Exceptions\Handler::class
+);
+
+$app->singleton(
+    Illuminate\Contracts\Console\Kernel::class,
+    App\Console\Kernel::class
+);
+
+/*
+|--------------------------------------------------------------------------
+| Register Middleware
+|--------------------------------------------------------------------------
+|
+| Next, we will register the middleware with the application. These can
+| be global middleware that run before and after each request into a
+| route or middleware that'll be assigned to some specific routes.
+|
+*/
+
+$app->routeMiddleware([
+    'auth' => App\Http\Middleware\Authenticate::class,
+    'chxq_jwt_auth' => App\Http\Middleware\JwtAuthMiddleware::class,
+    'chxq_sign' => App\Http\Middleware\SignAuthMiddleware::class,
+]);
+
+/*
+|--------------------------------------------------------------------------
+| Register Service Providers
+|--------------------------------------------------------------------------
+|
+| Here we will register all of the application's service providers which
+| are used to bind services into the container. Service providers are
+| totally optional, so you are not required to uncomment this line.
+|
+*/
+
+$app->register(App\Providers\AppServiceProvider::class);
+$app->register(App\Providers\AuthServiceProvider::class);
+
+$app->register(Dingo\Api\Provider\LumenServiceProvider::class);
+$app->register(Tymon\JWTAuth\Providers\LumenServiceProvider::class);
+$app->register(\Cviebrock\LaravelElasticsearch\ServiceProvider::class);
+$app->register(\Illuminate\Redis\RedisServiceProvider::class);
+
+class_alias('\Cviebrock\LaravelElasticsearch\Facade','Elasticsearch');
+/*
+|--------------------------------------------------------------------------
+| Load The Application Routes
+|--------------------------------------------------------------------------
+|
+| Next we will include the routes file so that they can all be added to
+| the application. This will provide all of the URLs the application
+| can respond to, as well as the controllers that may handle them.
+|
+*/
+
+$app->router->group([
+    'namespace' => 'App\Http\Controllers',
+], function ($router) {
+    require __DIR__.'/../routes/api.php';
+    require __DIR__.'/../routes/web.php';
+});
+
+
+$app->register(Hhxsv5\LaravelS\Illuminate\LaravelSServiceProvider::class);
+
+$app->register(Yansongda\LaravelPay\PayServiceProvider::class);
+
+return $app;

+ 56 - 0
composer.json

@@ -0,0 +1,56 @@
+{
+    "name": "laravel/lumen",
+    "description": "The Laravel Lumen Framework.",
+    "keywords": ["framework", "laravel", "lumen"],
+    "license": "MIT",
+    "type": "project",
+    "require": {
+        "php": ">=7.1.3",
+        "cviebrock/laravel-elasticsearch": "^3.5",
+        "dingo/api": "^2.2",
+        "guzzlehttp/guzzle": "^6.3",
+        "hhxsv5/laravel-s": "~3.4.0",
+        "illuminate/redis": "^5.8",
+        "laravel/lumen-framework": "5.8.*",
+        "league/fractal": "^0.17.0",
+        "predis/predis": "^1.1",
+        "tymon/jwt-auth": "1.0.0-rc.4.1",
+        "vlucas/phpdotenv": "^3.3",
+        "multilinguals/apollo-client": "^0.1.2",
+        "yansongda/laravel-pay": "^2.0"
+    },
+    "require-dev": {
+        "fzaninotto/faker": "^1.4",
+        "phpunit/phpunit": "^7.0",
+        "mockery/mockery": "^1.0"
+    },
+    "autoload": {
+        "classmap": [
+            "database/seeds",
+            "database/factories"
+        ],
+        "psr-4": {
+            "App\\": "app/"
+        },
+        "files" : [
+            "app/Helper/helper.php"
+        ]
+    },
+    "autoload-dev": {
+        "classmap": [
+            "tests/"
+        ]
+    },
+    "scripts": {
+        "post-root-package-install": [
+            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
+        ]
+    },
+    "config": {
+        "preferred-install": "dist",
+        "sort-packages": true,
+        "optimize-autoloader": true
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true
+}

+ 10 - 0
config/apollo.php

@@ -0,0 +1,10 @@
+<?php
+
+return [
+    'namespaces' => explode(',', env('APOLLO_NAMESPACES')),
+    'cluster' => env('APOLLO_CLUSTER'),
+    'save_dir' => storage_path('apollo'),
+    'config_server' => env('APOLLO_CONFIG_SERVER'),
+    'app_id' => env('APP_ID'),
+    'timeout_interval' => 70
+];

+ 10 - 0
config/constants.php

@@ -0,0 +1,10 @@
+<?php
+
+/**
+ * 常量配置
+ */
+
+return [
+    'CMS_GROUP' => env('REDIS_CMS_GROUP_KEY', 'cms_shop_group'),
+    'CMS_MARKET' => env('REDIS_CMS_MARKET_KEY', 'cms_shop_market'),
+];

+ 7 - 0
config/customer.tpl

@@ -0,0 +1,7 @@
+<?php
+//此文件为apollo配置文件模板,禁止修改移动
+return [
+    'app_secret' => '{app_secret}',
+    'jwt_secret' => '{jwt_secret}',
+    'app_service_url' => '{app_service_url}',
+];

+ 131 - 0
config/database.php

@@ -0,0 +1,131 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Database Connection Name
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify which of the database connections below you wish
+    | to use as your default connection for all database work. Of course
+    | you may use many connections at once using the Database library.
+    |
+    */
+
+    'default' => env('DB_CONNECTION', 'mysql'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Database Connections
+    |--------------------------------------------------------------------------
+    |
+    | Here are each of the database connections setup for your application.
+    | Of course, examples of configuring each database platform that is
+    | supported by Laravel is shown below to make development simple.
+    |
+    |
+    | All database work in Laravel is done through the PHP PDO facilities
+    | so make sure you have the driver for your particular database of
+    | choice installed on your machine before you begin development.
+    |
+    */
+
+    'connections' => [
+
+        'sqlite' => [
+            'driver' => 'sqlite',
+            'database' => env('DB_DATABASE', database_path('database.sqlite')),
+            'prefix' => env('DB_PREFIX', ''),
+        ],
+
+        'mysql' => [
+            'driver' => 'mysql',
+            'host' => env('DB_HOST', '127.0.0.1'),
+            'port' => env('DB_PORT', 3306),
+            'database' => env('DB_DATABASE', 'forge'),
+            'username' => env('DB_USERNAME', 'forge'),
+            'password' => env('DB_PASSWORD', ''),
+            'unix_socket' => env('DB_SOCKET', ''),
+            'charset' => env('DB_CHARSET', 'utf8mb4'),
+            'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
+            'prefix' => env('DB_PREFIX', ''),
+            'strict' => env('DB_STRICT_MODE', true),
+            'engine' => env('DB_ENGINE', null),
+            'timezone' => env('DB_TIMEZONE', '+08:00'),
+        ],
+
+        'pgsql' => [
+            'driver' => 'pgsql',
+            'host' => env('DB_HOST', '127.0.0.1'),
+            'port' => env('DB_PORT', 5432),
+            'database' => env('DB_DATABASE', 'forge'),
+            'username' => env('DB_USERNAME', 'forge'),
+            'password' => env('DB_PASSWORD', ''),
+            'charset' => env('DB_CHARSET', 'utf8'),
+            'prefix' => env('DB_PREFIX', ''),
+            'schema' => env('DB_SCHEMA', 'public'),
+            'sslmode' => env('DB_SSL_MODE', 'prefer'),
+        ],
+
+        'sqlsrv' => [
+            'driver' => 'sqlsrv',
+            'host' => env('DB_HOST', 'localhost'),
+            'port' => env('DB_PORT', 1433),
+            'database' => env('DB_DATABASE', 'forge'),
+            'username' => env('DB_USERNAME', 'forge'),
+            'password' => env('DB_PASSWORD', ''),
+            'charset' => env('DB_CHARSET', 'utf8'),
+            'prefix' => env('DB_PREFIX', ''),
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Migration Repository Table
+    |--------------------------------------------------------------------------
+    |
+    | This table keeps track of all the migrations that have already run for
+    | your application. Using this information, we can determine which of
+    | the migrations on disk haven't actually been run in the database.
+    |
+    */
+
+    'migrations' => 'migrations',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Redis Databases
+    |--------------------------------------------------------------------------
+    |
+    | Redis is an open source, fast, and advanced key-value store that also
+    | provides a richer set of commands than a typical key-value systems
+    | such as APC or Memcached. Laravel makes it easy to dig right in.
+    |
+    */
+
+    'redis' => [
+
+        'client' => 'predis',
+
+        'cluster' => env('REDIS_CLUSTER', false),
+
+        'default' => [
+            'host' => env('REDIS_HOST', '127.0.0.1'),
+            'password' => env('REDIS_PASSWORD', null),
+            'port' => env('REDIS_PORT', 6379),
+            'database' => env('REDIS_DB', 0),
+            'read_write_timeout' => 0,
+        ],
+
+        'cache' => [
+            'host' => env('REDIS_HOST', '127.0.0.1'),
+            'password' => env('REDIS_PASSWORD', null),
+            'port' => env('REDIS_PORT', 6379),
+            'database' => env('REDIS_CACHE_DB', 1),
+        ],
+
+    ],
+
+];

+ 304 - 0
config/jwt.php

@@ -0,0 +1,304 @@
+<?php
+
+/*
+ * This file is part of jwt-auth.
+ *
+ * (c) Sean Tymon <tymon148@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | JWT Authentication Secret
+    |--------------------------------------------------------------------------
+    |
+    | Don't forget to set this in your .env file, as it will be used to sign
+    | your tokens. A helper command is provided for this:
+    | `php artisan jwt:secret`
+    |
+    | Note: This will be used for Symmetric algorithms only (HMAC),
+    | since RSA and ECDSA use a private/public key combo (See below).
+    |
+    */
+
+    'secret' => env('JWT_SECRET',config('customer.jwt_secret')),
+
+    /*
+    |--------------------------------------------------------------------------
+    | JWT Authentication Keys
+    |--------------------------------------------------------------------------
+    |
+    | The algorithm you are using, will determine whether your tokens are
+    | signed with a random string (defined in `JWT_SECRET`) or using the
+    | following public & private keys.
+    |
+    | Symmetric Algorithms:
+    | HS256, HS384 & HS512 will use `JWT_SECRET`.
+    |
+    | Asymmetric Algorithms:
+    | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
+    |
+    */
+
+    'keys' => [
+
+        /*
+        |--------------------------------------------------------------------------
+        | Public Key
+        |--------------------------------------------------------------------------
+        |
+        | A path or resource to your public key.
+        |
+        | E.g. 'file://path/to/public/key'
+        |
+        */
+
+        'public' => env('JWT_PUBLIC_KEY'),
+
+        /*
+        |--------------------------------------------------------------------------
+        | Private Key
+        |--------------------------------------------------------------------------
+        |
+        | A path or resource to your private key.
+        |
+        | E.g. 'file://path/to/private/key'
+        |
+        */
+
+        'private' => env('JWT_PRIVATE_KEY'),
+
+        /*
+        |--------------------------------------------------------------------------
+        | Passphrase
+        |--------------------------------------------------------------------------
+        |
+        | The passphrase for your private key. Can be null if none set.
+        |
+        */
+
+        'passphrase' => env('JWT_PASSPHRASE'),
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | JWT time to live
+    |--------------------------------------------------------------------------
+    |
+    | Specify the length of time (in minutes) that the token will be valid for.
+    | Defaults to 1 hour.
+    |
+    | You can also set this to null, to yield a never expiring token.
+    | Some people may want this behaviour for e.g. a mobile app.
+    | This is not particularly recommended, so make sure you have appropriate
+    | systems in place to revoke the token if necessary.
+    | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.
+    |
+    */
+
+    'ttl' => env('JWT_TTL', 60 * 24 * 7),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Refresh time to live
+    |--------------------------------------------------------------------------
+    |
+    | Specify the length of time (in minutes) that the token can be refreshed
+    | within. I.E. The user can refresh their token within a 2 week window of
+    | the original token being created until they must re-authenticate.
+    | Defaults to 2 weeks.
+    |
+    | You can also set this to null, to yield an infinite refresh time.
+    | Some may want this instead of never expiring tokens for e.g. a mobile app.
+    | This is not particularly recommended, so make sure you have appropriate
+    | systems in place to revoke the token if necessary.
+    |
+    */
+
+    'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
+
+    /*
+    |--------------------------------------------------------------------------
+    | JWT hashing algorithm
+    |--------------------------------------------------------------------------
+    |
+    | Specify the hashing algorithm that will be used to sign the token.
+    |
+    | See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL
+    | for possible values.
+    |
+    */
+
+    'algo' => env('JWT_ALGO', 'HS256'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Required Claims
+    |--------------------------------------------------------------------------
+    |
+    | Specify the required claims that must exist in any token.
+    | A TokenInvalidException will be thrown if any of these claims are not
+    | present in the payload.
+    |
+    */
+
+    'required_claims' => [
+        'iss',
+        'iat',
+        'exp',
+        'nbf',
+        'sub',
+        'jti',
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Persistent Claims
+    |--------------------------------------------------------------------------
+    |
+    | Specify the claim keys to be persisted when refreshing a token.
+    | `sub` and `iat` will automatically be persisted, in
+    | addition to the these claims.
+    |
+    | Note: If a claim does not exist then it will be ignored.
+    |
+    */
+
+    'persistent_claims' => [
+        // 'foo',
+        // 'bar',
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Lock Subject
+    |--------------------------------------------------------------------------
+    |
+    | This will determine whether a `prv` claim is automatically added to
+    | the token. The purpose of this is to ensure that if you have multiple
+    | authentication models e.g. `App\User` & `App\OtherPerson`, then we
+    | should prevent one authentication request from impersonating another,
+    | if 2 tokens happen to have the same id across the 2 different models.
+    |
+    | Under specific circumstances, you may want to disable this behaviour
+    | e.g. if you only have one authentication model, then you would save
+    | a little on token size.
+    |
+    */
+
+    'lock_subject' => true,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Leeway
+    |--------------------------------------------------------------------------
+    |
+    | This property gives the jwt timestamp claims some "leeway".
+    | Meaning that if you have any unavoidable slight clock skew on
+    | any of your servers then this will afford you some level of cushioning.
+    |
+    | This applies to the claims `iat`, `nbf` and `exp`.
+    |
+    | Specify in seconds - only if you know you need it.
+    |
+    */
+
+    'leeway' => env('JWT_LEEWAY', 0),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Blacklist Enabled
+    |--------------------------------------------------------------------------
+    |
+    | In order to invalidate tokens, you must have the blacklist enabled.
+    | If you do not want or need this functionality, then set this to false.
+    |
+    */
+
+    'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
+
+    /*
+    | -------------------------------------------------------------------------
+    | Blacklist Grace Period
+    | -------------------------------------------------------------------------
+    |
+    | When multiple concurrent requests are made with the same JWT,
+    | it is possible that some of them fail, due to token regeneration
+    | on every request.
+    |
+    | Set grace period in seconds to prevent parallel request failure.
+    |
+    */
+
+    'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cookies encryption
+    |--------------------------------------------------------------------------
+    |
+    | By default Laravel encrypt cookies for security reason.
+    | If you decide to not decrypt cookies, you will have to configure Laravel
+    | to not encrypt your cookie token by adding its name into the $except
+    | array available in the middleware "EncryptCookies" provided by Laravel.
+    | see https://laravel.com/docs/master/responses#cookies-and-encryption
+    | for details.
+    |
+    | Set it to true if you want to decrypt cookies.
+    |
+    */
+
+    'decrypt_cookies' => false,
+
+    /*
+    |--------------------------------------------------------------------------
+    | Providers
+    |--------------------------------------------------------------------------
+    |
+    | Specify the various providers used throughout the package.
+    |
+    */
+
+    'providers' => [
+
+        /*
+        |--------------------------------------------------------------------------
+        | JWT Provider
+        |--------------------------------------------------------------------------
+        |
+        | Specify the provider that is used to create and decode the tokens.
+        |
+        */
+
+        'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
+
+        /*
+        |--------------------------------------------------------------------------
+        | Authentication Provider
+        |--------------------------------------------------------------------------
+        |
+        | Specify the provider that is used to authenticate users.
+        |
+        */
+
+        'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
+
+        /*
+        |--------------------------------------------------------------------------
+        | Storage Provider
+        |--------------------------------------------------------------------------
+        |
+        | Specify the provider that is used to store tokens in the blacklist.
+        |
+        */
+
+        'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
+
+    ],
+
+];

+ 37 - 0
database/migrations/2019_04_28_022240_create_table_subject_table.php

@@ -0,0 +1,37 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class CreateTableSubjectTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('cms_subject', function (Blueprint $table) {
+            $table->bigIncrements('id');
+            $table->integer('city_id');
+            $table->string('city_name',20)->comment('城市名称'); //城市名称
+            $table->string('title',70)->comment('专题名称');
+            $table->tinyInteger('show_type')->default(0)->comment('商品展示方式:0:左图右字;1:通栏大图;2:左右滑动');
+            $table->tinyInteger('is_open')->default(1)->comment('是否启用:0:关闭;1:开启');
+            $table->timestamps();
+            $table->softDeletes();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+            Schema::dropIfExists('cms_subject');
+    }
+}

+ 33 - 0
database/migrations/2019_04_28_025052_create_cms_subject_product_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class CreateCmsSubjectProductTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('cms_subject_product', function (Blueprint $table) {
+            $table->bigIncrements('id');
+            $table->integer('sort')->comment('商品排序');
+            $table->timestamps();
+            $table->softDeletes();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('cms_subject_product');
+    }
+}

+ 36 - 0
database/migrations/2019_04_28_025655_create_cms_content_template_table.php

@@ -0,0 +1,36 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class CreateCmsContentTemplateTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('cms_content_template', function (Blueprint $table) {
+            $table->bigIncrements('id');
+            $table->integer('city_id')->comment('城市ID');
+            $table->string('city_name',20)->comment('城市名称');
+            $table->string('title',200)->comment('模板名称');
+            $table->tinyInteger('apply_type')->default(1)->comment('对应模板:0:团购首页;1:菜市场首页');
+            $table->tinyInteger('is_open')->default(0)->comment('是否启用:0:禁用;1:启用');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('cms_content_template');
+    }
+}

+ 33 - 0
database/migrations/2019_04_28_030611_create_cms_content_template_set_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class CreateCmsContentTemplateSetTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('cms_content_template_set', function (Blueprint $table) {
+            $table->bigIncrements('id');
+            $table->json('rule')->comment('设置项');
+            $table->tinyInteger('status')->default(0)->comment('状态:0:草稿;1:发布');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('cms_content_template_set');
+    }
+}

+ 32 - 0
database/migrations/2019_04_28_072523_add_cms_subject_product_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class AddCmsSubjectProductTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('cms_subject_product', function (Blueprint $table) {
+            $table->integer('subject_id')->comment('专题ID');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('cms_subject_product', function (Blueprint $table) {
+            //
+        });
+    }
+}

+ 33 - 0
database/migrations/2019_04_29_060908_add_template_id_to_cms_content_template_set_table.php

@@ -0,0 +1,33 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class AddTemplateIdToCmsContentTemplateSetTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('cms_content_template_set', function (Blueprint $table) {
+            $table->integer('tpl_id')->comment('模板ID');
+            $table->tinyInteger('area_type')->default(0)->comment('状态:0:banner;1:专题广告;2.商品楼层;3.分类专题(菜市场)');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('cms_content_template_set', function (Blueprint $table) {
+            //
+        });
+    }
+}

+ 32 - 0
database/migrations/2019_04_30_075256_add_deleted_at_to_cms_content_template_set_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class AddDeletedAtToCmsContentTemplateSetTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('cms_content_template_set', function (Blueprint $table) {
+            $table->softDeletes();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('cms_content_template_set', function (Blueprint $table) {
+            //
+        });
+    }
+}

+ 32 - 0
database/migrations/2019_05_22_015511_add_delete_to_cms_content_template_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class AddDeleteToCmsContentTemplateTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('cms_content_template', function (Blueprint $table) {
+            $table->softDeletes();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('cms_content_template', function (Blueprint $table) {
+            //
+        });
+    }
+}

+ 32 - 0
database/migrations/2019_05_22_085041_add_status_to_cms_content_template_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class AddStatusToCmsContentTemplateTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('cms_content_template', function (Blueprint $table) {
+            $table->tinyInteger('status')->default(0)->comment('状态:0:草稿;1:发布');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('cms_content_template', function (Blueprint $table) {
+            //
+        });
+    }
+}

+ 20 - 0
deploy/nginx/conf.d/app.beta.conf

@@ -0,0 +1,20 @@
+server {
+    listen 80;
+    index index.php index.html;
+    error_log  /var/log/nginx/error.log;
+    access_log /var/log/nginx/access.log;
+    root /var/www/public;
+    location ~ \.php$ {
+        try_files $uri =404;
+        fastcgi_split_path_info ^(.+\.php)(/.+)$;
+        fastcgi_pass app_cms_service_beta:9000;
+        fastcgi_index index.php;
+        include fastcgi_params;
+        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+        fastcgi_param PATH_INFO $fastcgi_path_info;
+    }
+    location / {
+        try_files $uri $uri/ /index.php?$query_string;
+        gzip_static on;
+    }
+}

+ 20 - 0
deploy/nginx/conf.d/app.dev.conf

@@ -0,0 +1,20 @@
+server {
+    listen 80;
+    index index.php index.html;
+    error_log  /var/log/nginx/error.log;
+    access_log /var/log/nginx/access.log;
+    root /var/www/public;
+    location ~ \.php$ {
+        try_files $uri =404;
+        fastcgi_split_path_info ^(.+\.php)(/.+)$;
+        fastcgi_pass app_cms_service_dev:9000;
+        fastcgi_index index.php;
+        include fastcgi_params;
+        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+        fastcgi_param PATH_INFO $fastcgi_path_info;
+    }
+    location / {
+        try_files $uri $uri/ /index.php?$query_string;
+        gzip_static on;
+    }
+}

+ 2 - 0
deploy/php/local.ini

@@ -0,0 +1,2 @@
+upload_max_filesize=40M
+post_max_size=40M

+ 32 - 0
docker-compose-beta.yml

@@ -0,0 +1,32 @@
+version: '3'
+
+services:
+  app_cms_service_beta:
+    image: harbor.caihongxingqiu.com:9401/library/php:latest
+    tty: true
+    working_dir: /var/www
+    volumes:
+      - ./:/var/www
+      - ./deploy/php/local.ini:/usr/local/etc/php/conf.d/local.ini
+      - /data/wwwroot/beta/cms-service:/data/wwwroot/beta/cms-service
+    networks:
+      - chxq
+
+  server_cms_service_beta:
+    image: harbor.caihongxingqiu.com:9401/library/nginx:latest
+    tty: true
+    depends_on:
+      - app_cms_service_beta
+    ports:
+      - "28211:80"
+    volumes:
+      - ./:/var/www
+      - ./deploy/nginx/conf.d/app.beta.conf:/etc/nginx/conf.d/app.beta.conf
+    networks:
+      - chxq
+
+#docker networks
+#docker network create --driver overlay chxq
+networks:
+  chxq:
+    external: true

+ 31 - 0
docker-compose-dev.yml

@@ -0,0 +1,31 @@
+version: '3'
+
+services:
+  app_cms_service_dev:
+    image: harbor.caihongxingqiu.com:9401/library/php:latest
+    tty: true
+    working_dir: /var/www
+    volumes:
+      - ./:/var/www
+      - ./deploy/php/local.ini:/usr/local/etc/php/conf.d/local.ini
+    networks:
+      - chxq
+
+  server_cms_service_dev:
+    image: harbor.caihongxingqiu.com:9401/library/nginx:latest
+    tty: true
+    depends_on:
+      - app_cms_service_dev
+    ports:
+      - "18211:80"
+    volumes:
+      - ./:/var/www
+      - ./deploy/nginx/conf.d/app.dev.conf:/etc/nginx/conf.d/app.dev.conf
+    networks:
+      - chxq
+
+#docker networks
+#docker network create --driver overlay chxq
+networks:
+  chxq:
+    external: true

+ 54 - 0
routes/api.php

@@ -0,0 +1,54 @@
+<?php
+
+/*
+|--------------------------------------------------------------------------
+| Application Routes
+|--------------------------------------------------------------------------
+|
+| Here is where you can register all of the routes for an application.
+| It is a breeze. Simply tell Lumen the URIs it should respond to
+| and give it the Closure to call when that URI is requested.
+|
+*/
+
+$api = app('Dingo\Api\Routing\Router');
+
+$api->version('v1', [
+    'namespace' => 'App\Http\Controllers',
+], function ($api) {
+    //登录+验签
+    $api->group(['middleware' => ['chxq_jwt_auth','chxq_sign']], function ($api) {
+
+    });
+    //仅验签
+    $api->group(['middleware' => 'chxq_sign'], function ($api) {
+        //内容预览
+        $api->post('/templateSet/preview', 'CmsContentTemplateSetController@preview');
+        //获取首页模板名称
+        $api->get('/templateSet/templateName', 'CmsContentTemplateSetController@template');
+        //首页商品列表
+        $api->get('/templateSet/productList', 'CmsContentTemplateSetController@productList');
+
+    });
+
+});
+
+$api->version('v1', [
+    'namespace' => 'App\Http\Controllers\V2',
+], function ($api) {
+
+    $api->group([
+        'prefix' => 'v2'
+    ], function ($api) {
+        $api->group(['middleware' => 'chxq_jwt_auth'], function ($api) {
+            //楼层列表
+            $api->get('/floor/index', 'FloorController@index');
+            //获取楼层信息
+            $api->get('/floor/info', 'FloorController@info');
+            //热门视频ids
+            $api->get('/floor/hotVideoIds', 'FloorController@hotVideoIds');
+            //获取推荐用户组内容
+            $api->get('memberList', 'MemberGroupController@memberList');
+        });
+    });
+});

+ 0 - 0
storage/.gitkeep