Browse Source

first commit

523013183@qq.com 2 years ago
commit
f68a52b6be
85 changed files with 7570 additions and 0 deletions
  1. 29 0
      .env.example
  2. 6 0
      .gitignore
  3. 81 0
      README.md
  4. 40 0
      app/Api/Controllers/ApiController.php
  5. 36 0
      app/Api/Controllers/IndexController.php
  6. 18 0
      app/Api/Facades/ApiFacade.php
  7. 37 0
      app/Api/Providers/ApiServiceProvider.php
  8. 46 0
      app/Api/Services/ApiService.php
  9. 23 0
      app/Api/routes.php
  10. 17 0
      app/Base/Controllers/ApiBaseController.php
  11. 15 0
      app/Base/Controllers/Controller.php
  12. 10 0
      app/Base/Events/Event.php
  13. 141 0
      app/Base/Exceptions/ApiException.php
  14. 125 0
      app/Base/Exceptions/Handler.php
  15. 330 0
      app/Base/Library/Shorty.php
  16. 33 0
      app/Base/Middleware/ApiPermission.php
  17. 48 0
      app/Base/Middleware/Authenticate.php
  18. 58 0
      app/Base/Middleware/Localization.php
  19. 59 0
      app/Base/Middleware/Permission.php
  20. 185 0
      app/Base/Middleware/Response.php
  21. 95 0
      app/Base/Models/ApiSoftDeletes.php
  22. 150 0
      app/Base/Models/ApiSoftDeletingScope.php
  23. 536 0
      app/Base/Models/BaseModel.php
  24. 11 0
      app/Base/Models/BaseMongoModel.php
  25. 30 0
      app/Base/Models/ComOssETagModel.php
  26. 228 0
      app/Base/Models/Criteria.php
  27. 39 0
      app/Base/Models/EloquentBuilder.php
  28. 12 0
      app/Base/Models/QueryBuilder.php
  29. 103 0
      app/Base/Providers/AppServiceProvider.php
  30. 91 0
      app/Base/Providers/AuthServiceProvider.php
  31. 29 0
      app/Base/Providers/EventServiceProvider.php
  32. 563 0
      app/Base/Services/AbstractBaseService.php
  33. 211 0
      app/Base/Services/ApiAuthUser.php
  34. 17 0
      app/Base/Services/ApiBaseService.php
  35. 65 0
      app/Base/Services/AuthUser.php
  36. 50 0
      app/Base/Services/BaseService.php
  37. 53 0
      app/Base/Services/CacheTrait.php
  38. 1633 0
      app/Base/helpers.php
  39. 13 0
      app/Basic/Facades/UrlsFacade.php
  40. 13 0
      app/Basic/Models/UrlsModel.php
  41. 79 0
      app/Basic/Providers/BasicServiceProvider.php
  42. 15 0
      app/Basic/Services/UrlsService.php
  43. 0 0
      app/Console/Commands/.gitkeep
  44. 59 0
      app/Console/Commands/CacheCommand.php
  45. 29 0
      app/Console/Kernel.php
  46. 26 0
      app/Console/README.MD
  47. 21 0
      app/Console/Template/ApiController.tpl
  48. 13 0
      app/Console/Template/ApiFacade.tpl
  49. 21 0
      app/Console/Template/ApiService.tpl
  50. 89 0
      app/Console/Template/Controller.tpl
  51. 13 0
      app/Console/Template/Facade.tpl
  52. 12 0
      app/Console/Template/Model.tpl
  53. 123 0
      app/Console/Template/Service.tpl
  54. 23 0
      app/Console/Template/ServiceProvider.tpl
  55. 31 0
      app/Crontab/Controllers/TaskInfoController.php
  56. 22 0
      app/Crontab/Providers/TaskServiceProvider.php
  57. 0 0
      app/Crontab/README.md
  58. 50 0
      app/Crontab/Services/TaskInfoService.php
  59. 11 0
      app/Crontab/routes.php
  60. 134 0
      app/Doc/Controllers/CountController.php
  61. 45 0
      app/Doc/Controllers/DocController.php
  62. 17 0
      app/Doc/Providers/DocServiceProvider.php
  63. 185 0
      app/Doc/Services/AnnotationService.php
  64. 189 0
      app/Doc/Services/DocService.php
  65. 12 0
      app/Doc/routes.php
  66. 26 0
      app/Jobs/ExampleJob.php
  67. 52 0
      app/Jobs/IntegrateJob.php
  68. 24 0
      app/Jobs/Job.php
  69. 35 0
      artisan
  70. 124 0
      bootstrap/app.php
  71. 53 0
      composer.json
  72. 64 0
      config/app.php
  73. 91 0
      config/auth.php
  74. 105 0
      config/cache.php
  75. 136 0
      config/database.php
  76. 25 0
      public/.htaccess
  77. BIN
      public/assets/image/img_def_404.png
  78. BIN
      public/favicon.ico
  79. 24 0
      public/index.php
  80. 2 0
      public/robots.txt
  81. 101 0
      resources/README.md
  82. 0 0
      resources/views/.gitkeep
  83. 65 0
      resources/views/errors/404.blade.php
  84. 70 0
      tests/ExampleTest.php
  85. 75 0
      tests/TestCase.php

+ 29 - 0
.env.example

@@ -0,0 +1,29 @@
+APP_ENV=local
+APP_DEBUG=true
+APP_KEY=07732BADF56B0801080F49AE7B312232
+APP_TIMEZONE=PRC
+
+DB_CONNECTION=mysql
+
+DB_HOST=127.0.0.1
+DB_DATABASE=shorty
+DB_USERNAME=root
+DB_PASSWORD=root
+DB_PORT=3306
+
+#数据库连接最大空闲时间,单位:秒
+DB_MAX_IDLE_TIME=30
+
+REDIS_HOST=127.0.0.1
+REDIS_PORT=6379
+REDIS_DATABASE=5
+
+CACHE_DRIVER=redis
+QUEUE_DRIVER=redis
+
+APP_LOCALE = zh-cn
+
+# cache redis前缀
+CACHE_PREFIX=matchexpo
+
+DOMAIN=http://s.juye.cn

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+/.idea
+/.env
+/storage
+/public/upload
+/composer.lock
+/vendor

+ 81 - 0
README.md

@@ -0,0 +1,81 @@
+# 框架文档
+## 模型
+### 查询
+    UserFacade::getModel()->buildQuery([
+        'user_name' => 'zmx',
+        'nick_name' => [['like','zhuang'],['in',['zmx','zhuang123']],['<>','1234']],
+        '_string'   => 'status=0 and company_id=1'
+    ])->selectRaw('id,user_id,user_name,nick_name')->get();
+执行后的sql:select id,user_id,user_name,nick_name from user where user_name='zmx' and nick_name like '%zhuang%' and nick_name in ('zmx','zhuang123') and nick_name<>'1234' and status=0 and company_id=1
+
+buildQuery 只支持Model调用,不支持 `DB::table('company_user')->buildQuery([])`
+### 根据主键直接获取单条记录
+    UserFacade::findOneById(1,'id,user_id,user_name,nick_name')
+### 根据条件获取单条记录
+    UserFacade::findOneBy(['user_name'=>'zmx'],'id,user_id,nick_name')
+### 根据条件查询获取多条记录
+    UserFacade::findBy(['user_name'=>'zmx'],'id,user_id,nick_name')
+### 使用原生SQL查询
+    UserFacade::query('select * from company_user where id=1')
+### 获取某个字段值
+    UserFacade::getFieldBy('nick_name',['user_name'=>'zmx'])    
+### 获取数据模型
+    UserFacade::getModel()
+### 获取Table名称
+    UserFacade::getTable()
+### 验证字段值是否唯一
+    UserFacade::checkFieldUnique('user_name','zmx',['id'=>[
+        ['notIn',[12]]
+    ]])
+### 新增记录
+    UserFacade::save(['user_name'=>'zmx','nick_name'=>'zmx'])
+### 新增多条记录
+    UserFacade::saveAll([
+        ['user_name'=>'zmx','nick_name'=>'zmx'],
+        ['user_name'=>'yunnan','nick_name'=>'yunnan']
+    ])
+### 更新记录
+    UserFacade::update(1,['user_name'=>'zmx','nick_name'=>'zhuang123'])
+### 根据条件更改记录
+    UserFacade::updateBy(['user_name'=>'zmx'],['nick_name'=>'zhuang234'])
+### 删除记录
+    UserFacade::delete(1)
+### 根据条件删除记录
+    UserFacade::deleteBy(['user_name'=>'zmx'])
+### 软删除
+    class UserModel extends BaseModel {
+        use ApiSoftDeletes;
+        protected $table = 'company_user'; //表名
+        protected $hidden = ['password']; //格式化成json时隐藏的字段
+        protected $casts = [
+            'permissions' => 'array', //该字段自动格式化成数组
+            'role_id'   => 'int'    //该字段自动格式化成整形
+        ];
+        const STATUS_ENABLED = 1; //定义软删除字段status为正常使用
+        const STATUS_DELETED = 2; //定义软删除字段status为删除状态
+        const STATUS_DISABLED = 3; //定义软删除字段status为禁用状态
+    }
+添加软删除后,在查询时 UserFacade::findOneById(1) 会自动添加 status=1 的条件
+
+如果要查询出所有状态值得数据需使用 `UserModel::where('id',1)->withAll()->first()`
+
+
+##创建模块
+    php artisan module moduleName
+moduleName:模块名
+
+##创建模型文件
+    php artisan make:tpl moduleName ActionName
+moduleName:模块名
+
+actionName:文件名称
+
+例:php artisan make:tpl User CompanyUser
+
+以上命令会再User目录下的Facades、Services、Models新建CompanyUser对应的类,已经存在的文件不会再创建,但provider需要手动处理
+## 不能使用的路由
+```
+/dist
+/static
+/sitemap
+```

+ 40 - 0
app/Api/Controllers/ApiController.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Api\Controllers;
+
+use App\Api\Services\ApiService;
+use App\Base\Controllers\ApiBaseController;
+use App\Base\Library\Shorty;
+use Illuminate\Http\Request;
+
+class ApiController extends ApiBaseController
+{
+    private $apiService;
+    private $shorty;
+
+    public function __construct(ApiService $apiService)
+    {
+        $this->apiService = $apiService;
+        $this->shorty = new Shorty(config("app.domain"));
+    }
+
+    /**
+     * @param string url 生成短地址的url
+     * @return array
+     * @throws \App\Base\Exceptions\ApiException
+     * @throws
+     * @api get /api/short 获取短地址
+     * @group 短地址
+     * @successExample
+     * {"ret":0,"msg":"success.","data":{"id":2,"name":"物性任光保","remark":"nisi nostrud velit culpa ad","sort":100}}
+     */
+    public function getShortUrl(Request $request)
+    {
+        $this->validate($request, [
+            'url' => 'required'
+        ], [
+            'url.required' => "url不能为空"
+        ]);
+        return $this->apiService->getShortUrl($request->input("url"));
+    }
+}

+ 36 - 0
app/Api/Controllers/IndexController.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Api\Controllers;
+
+use App\Api\Services\ApiService;
+use App\Base\Controllers\ApiBaseController;
+use App\Base\Library\Shorty;
+use App\Basic\Facades\UrlsFacade;
+use Illuminate\Http\Request;
+
+class IndexController extends ApiBaseController
+{
+    private $apiService;
+    private $shorty;
+
+    public function __construct(ApiService $apiService)
+    {
+        $this->apiService = $apiService;
+        $this->shorty = new Shorty(config("app.domain"));
+    }
+
+    /**
+     * 短地址自动跳转
+     */
+    public function index(Request $request)
+    {
+        $plink = $request->route("plink", ""); //详情页面分配的地址
+        $id = $this->shorty->decode($plink);
+        $info = UrlsFacade::findOneById($id, "url");
+        if (empty($info['url'])) {
+            abort(404);
+        }
+        UrlsFacade::incrementBy(["id" => $id], "hits");
+        $this->shorty->redirect($info['url']);
+    }
+}

+ 18 - 0
app/Api/Facades/ApiFacade.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Api\Facades;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * @method static \App\Api\Services\ApiService getClientUserInfoById($userId) 根据id获取客户端用户详细数据
+ * Class ApiFacade
+ * @package App\Api\Facades
+ */
+class ApiFacade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+}

+ 37 - 0
app/Api/Providers/ApiServiceProvider.php

@@ -0,0 +1,37 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: fangx
+ * Date: 2021/7/23
+ * Time: 9:42
+ */
+
+namespace App\Api\Providers;
+
+use App\Api\Facades\ApiFacade;
+use App\Api\Services\ApiService;
+use App\Base\Providers\AppServiceProvider;
+
+class ApiServiceProvider extends AppServiceProvider
+{
+    public function boot()
+    {
+        parent::boot();
+    }
+
+    /**
+     * 注册绑定门面
+     */
+    public function register()
+    {
+        //注册Api
+        $this->registerApi();
+    }
+
+    public function registerApi(){
+
+        $this->app->bind(ApiFacade::class, function () {
+            return app()->make(ApiService::class);
+        });
+    }
+}

+ 46 - 0
app/Api/Services/ApiService.php

@@ -0,0 +1,46 @@
+<?php
+
+
+namespace App\Api\Services;
+
+use App\Base\Library\Shorty;
+use App\Base\Services\ApiBaseService;
+use Illuminate\Support\Facades\Cache;
+
+class ApiService extends ApiBaseService
+{
+    private $shorty;// 短地址服务
+
+    public function __construct()
+    {
+        $this->shorty = new Shorty(config("app.domain"));
+    }
+
+    /**
+     * 获取短地址
+     */
+    public function getShortUrl($urls)
+    {
+        $urls = is_array($urls) ? $urls : [$urls];
+        $list = [];
+        foreach ($urls as $url) {
+            $key = "shortUrl_" . md5($url);
+            $value = Cache::get($key, '');
+            if ($value) {
+                $list[$url] = $value;
+                continue;
+            }
+            try {
+                $list[$url] = $this->shorty->run($url);
+                Cache::put($key, $list[$url], 86400 * 7);
+            } catch (\Exception $e) {
+                continue;
+            }
+        }
+        if (count($urls) == 1) {
+            return $list[$urls[0]];
+        } else {
+            return $list;
+        }
+    }
+}

+ 23 - 0
app/Api/routes.php

@@ -0,0 +1,23 @@
+<?php
+$app = app()->router;
+
+/**
+ * pc 无需auth路由
+ * */
+$app->group([
+    'namespace' => 'App\Api\Controllers',
+    'prefix' => 'api',
+], function () use ($app) {
+    // 生成短地址
+    $app->get('/short', ['uses' => 'ApiController@getShortUrl']);
+    $app->post('/short', ['uses' => 'ApiController@getShortUrl']);
+});
+
+$app->group([
+    'namespace' => 'App\Api\Controllers',
+    'prefix' => ''
+], function () use ($app) {
+    $app->get('/{plink:[A-Za-z0-9]+}',[
+        'as' => 'moduleIndex', 'uses' => 'IndexController@index'
+    ]);
+});

+ 17 - 0
app/Base/Controllers/ApiBaseController.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Base\Controllers;
+
+use App\Base\Services\ApiAuthUser;
+use Illuminate\Http\Request;
+use Laravel\Lumen\Routing\Controller;
+
+class ApiBaseController extends Controller
+{
+    use ApiAuthUser;
+
+    public function getPageSize(Request $request)
+    {
+        return $request->input('page_size', config('app.app_rows'));
+    }
+}

+ 15 - 0
app/Base/Controllers/Controller.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Base\Controllers;
+
+use Illuminate\Http\Request;
+use Laravel\Lumen\Routing\Controller as BaseController;
+
+class Controller extends BaseController
+{
+    public function getPageSize(Request $request)
+    {
+        return $request->input('page_size', config('app.app_rows'));
+    }
+
+}

+ 10 - 0
app/Base/Events/Event.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App\Base\Events;
+
+use Illuminate\Queue\SerializesModels;
+
+abstract class Event
+{
+    use SerializesModels;
+}

+ 141 - 0
app/Base/Exceptions/ApiException.php

@@ -0,0 +1,141 @@
+<?php
+namespace App\Base\Exceptions;
+
+
+use App\Base\Services\AuthUser;
+
+class ApiException extends \Exception
+{
+    use AuthUser;
+
+    /**
+     * 错误信息参数
+     * @var array
+     */
+    protected $params = [];
+    /**
+     * 携带的参数
+     * @var array
+     */
+    protected $data = [];
+
+    protected $langs = [];
+
+    /** 处理异常错误
+     * @param string $id 语言包中的key or 错误代码
+     * @param null $message 错误信息 or 替换参数
+     * @param array $params 替换参数 or 附加数据
+     * @param int $code 错误代码 or 无用
+     * @param null $locale 语言 or 无用
+     * @param array $data or 无用
+     */
+    public function __construct($id, $message = null, $params = [], $code = 0, $locale = null, $data = [])
+    {
+        if (empty($id)) {
+            return parent::__construct($message, $code, null);
+        }
+        $locale = $locale ?: app('translator')->getLocale();
+        if (empty($locale)) {
+            $locale = "zh-cn";
+        }
+        $this->params = $params;
+        $this->data = $data;
+        if (is_numeric($id)) {
+            //兼容原本的调用方式和顺序
+            //$id, $params=[], $data=[], $message = null
+            $this->params = $message ? (array)$message : [];
+            $this->data = $params;
+            if (!$code) {
+                //根据中间件设置的语言处理多语言消息
+                $message = $this->parseMessage($id);
+            } else {
+                $message = $code;
+            }
+            $code = $id;
+        } else {
+            $message = $this->parseNewMessage($id, $message, $code, $locale);
+        }
+        parent::__construct($message, $code, null);
+    }
+
+    /**
+     * 解析错误信息
+     * @param $code
+     * @return string
+     */
+    protected function parseMessage($code)
+    {
+        $errors = config('error');
+        if (!isset($errors[$code])) {
+            return '服务器繁忙,请稍候重试.';
+        }
+        $message = trans($errors[$code], $this->params);
+        if ($message != $code && !empty($this->params)) {
+            foreach ($this->params as $key => $item) {
+                $message = str_replace('{' . $key . '}', $item, $message);
+            }
+        }
+        return $message;
+    }
+
+    /**
+     * 获取参数数据
+     * @return array
+     */
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    /**
+     * 解析语言包文件
+     * @param $id
+     * @param $message
+     * @param $code
+     * @param null $locale
+     * @return array
+     */
+    public function parseNewMessage($id, $message, &$code, $locale = null)
+    {
+        $code = -1;
+        if (is_null($id)) {
+            return $message ?: $id;
+        }
+        $keys = explode('.', $id);
+        $this->getLang($keys[0], $locale);
+
+        if (!$this->langs[$locale][$keys[0]]) {
+            return $message ?: $id;
+        }
+
+        $data = isset($this->langs[$locale][$keys[0]][$keys[1]]) ? $this->langs[$locale][$keys[0]][$keys[1]] : [];
+        if ($data) {
+            if (is_array($data)) {
+                $code = $data[0];
+                $message = $data[1];
+            } else {
+                $message = $data;
+            }
+        } else {
+            $message = $message ?: $id;
+        }
+
+        if (!empty($this->params)) {
+            foreach ($this->params as $key => $item) {
+                $message = str_replace('{' . $key . '}', $item, $message);
+            }
+        }
+        return $message;
+    }
+
+    /*
+     * 取语言包文件
+     */
+    public function getLang($key, $locale = 'zh-cn')
+    {
+        $path = 'lang/' . $locale . '/' . $key;
+        if (!isset($this->langs[$locale]) || !isset($this->langs[$locale][$key])) {
+            $this->langs[$locale][$key] = include resource_path($path . '.php');
+        }
+    }
+}

+ 125 - 0
app/Base/Exceptions/Handler.php

@@ -0,0 +1,125 @@
+<?php
+
+namespace App\Base\Exceptions;
+
+use Exception;
+use Illuminate\Validation\ValidationException;
+use Illuminate\Auth\Access\AuthorizationException;
+use Illuminate\Database\Eloquent\ModelNotFoundException;
+use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+use Illuminate\Http\Response;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+class Handler extends ExceptionHandler
+{
+    /**
+     * A list of the exception types that should not be reported.
+     *
+     * @var array
+     */
+    protected $dontReport = [
+        AuthorizationException::class,
+        HttpException::class,
+        ModelNotFoundException::class,
+        ValidationException::class,
+    ];
+
+    /**
+     * Report or log an exception.
+     *
+     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
+     *
+     * @param  \Exception  $e
+     * @return void
+     */
+    public function report(\Throwable $e)
+    {
+        parent::report($e);
+    }
+
+    /**
+     * Render an exception into an HTTP response.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Exception  $e
+     * @return \Illuminate\Http\Response
+     */
+    public function render($request, \Throwable $e)
+    {
+        $content = [
+            'ret' => $e->getCode() == 0 ? '500' : $e->getCode(),
+            'msg' => $e->getMessage()
+        ];
+        $status = 200;
+        if ($e instanceof \HttpResponseException) {
+            $content['data'] = $e->getResponse();
+        } elseif ($e instanceof ModelNotFoundException || ($e instanceof NotFoundHttpException && $request->ajax())) {
+            $content['ret'] = 404;
+            $content['msg'] = '您访问的页面地址不存在';
+        } elseif ($e instanceof NotFoundHttpException) {
+            $content['ret'] = 404;
+            $content['msg'] = '您访问的页面地址不存在';
+            $status = 404;
+        } elseif ($e instanceof AuthorizationException) {
+            $content['ret'] = 403;
+        } elseif ($e instanceof ValidationException && $e->getResponse()) {
+            $content['ret'] = 422;
+            $content['data'] = json_decode($e->getResponse()->getContent(), true);
+            //处理多语言中的[11000,'xxx']格式
+            $msg = [];
+            $ret = [];
+            foreach ($content['data'] as &$item) {
+                if (is_array($item)) {
+                    foreach ($item as &$item2) {
+                        if (is_array($item2) && count($item2) == 2) {
+                            $msg = array_merge($msg, [$item2[1]]);
+                            $ret = array_merge($ret, [$item2[0]]);
+                            $item2 = $item2[1];
+                        } else {
+                            $msg = array_merge($msg, [$item2]);
+                        }
+                    }
+                } else {
+                    $msg[] = $item;
+                }
+            }
+            $content['msg'] = reset($msg);
+            $content['ret'] = reset($ret) ?: 422;
+            if (is_array($content['msg'])) {
+                if (count($content['msg']) == 2) {
+                    $content['ret'] = $content['msg'][0];
+                    $content['msg'] = $content['msg'][1];
+                }
+            }
+        } elseif ($e instanceof ApiException) {
+            $content['data'] = $e->getData();
+        }
+
+        $trace = $e->getTrace();
+        if (isset($content['msg']) && $content['msg']) {
+            $track = json_encode($trace, JSON_UNESCAPED_UNICODE);
+            if (stristr($content['msg'], 'connection')
+                || stristr($content['msg'], 'SQLSTATE')
+                || stristr($track, 'PDOException: SQLSTATE')
+            ) {
+                $content['ret'] = -100;
+            }
+        }
+
+        if (env('APP_DEBUG')) { // 开发模式增加track输出
+            $content['track'] = $trace[0];
+        }
+
+        $response = new Response(json_encode($content, JSON_UNESCAPED_UNICODE), $status);
+        $response->header('Content-Type', 'application/json;charset:UTF-8');
+        $response->header('Access-Control-Allow-Origin', '*');
+        $response->header('Access-Control-Allow-Credentials', 'true');
+        $response->header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
+        $response->header('Access-Control-Allow-Headers', 'Content-Type,token,api-token,x-requested-with,Language-Set');
+        $response->header('Access-Control-Expose-Headers', '*');
+        $response->exception = $e;
+
+        return $response;
+    }
+}

+ 330 - 0
app/Base/Library/Shorty.php

@@ -0,0 +1,330 @@
+<?php
+namespace App\Base\Library;
+
+use App\Base\Exceptions\ApiException;
+use App\Basic\Facades\UrlsFacade;
+
+class Shorty {
+    /**
+     * Default characters to use for shortening.
+     *
+     * @var string
+     */
+    private $chars = 'DUuW9Pd0lGE2xZp4aIwOrHYkMA7fCi1Te53VzL6gRKnmtSsFoyNQvcjBqJhX8b';
+
+    /**
+     * Salt for id encoding.
+     *
+     * @var string
+     */
+    private $salt = '';
+
+    /**
+     * Length of number padding.
+     */
+    private $padding = 1;
+
+    /**
+     * Hostname
+     */
+    private $hostname = '';
+
+    /**
+     * PDO database connection.
+     *
+     * @var object
+     */
+    private $connection = null;
+
+    /**
+     * Whitelist of IPs allowed to save URLs.
+     * If the list is empty, then any IP is allowed.
+     *
+     * @var array
+     */
+    private $whitelist = array();
+
+    /**
+     * Constructor
+     *
+     * @param string $hostname Hostname
+     * @param object $connection Database connection
+     */
+    public function __construct($hostname) {
+        $this->hostname = $hostname;
+    }
+
+    /**
+     * Gets the character set for encoding.
+     *
+     * @return string Set of characters
+     */
+    public function get_chars() {
+        return $this->chars;
+    }
+
+    /**
+     * Sets the character set for encoding.
+     *
+     * @param string $chars Set of characters
+     */
+    public function set_chars($chars) {
+        if (!is_string($chars) || empty($chars)) {
+            throw new Exception('Invalid input.');
+        }
+        $this->chars = $chars;
+    }
+
+    /**
+     * Gets the salt string for encoding.
+     *
+     * @return string Salt
+     */
+    public function get_salt() {
+        return $this->salt;
+    }
+
+    /**
+     * Sets the salt string for encoding.
+     *
+     * @param string $salt Salt string
+     */
+    public function set_salt($salt) {
+        $this->salt = $salt;
+    }
+
+    /**
+     * Gets the padding length.
+     *
+     * @return int Padding length
+     */
+    public function get_padding() {
+        return $this->padding;
+    }
+
+    /**
+     * Sets the padding length.
+     *
+     * @param int $padding Padding length
+     */
+    public function set_padding($padding) {
+        $this->padding = $padding;
+    }
+
+    /**
+     * Converts an id to an encoded string.
+     *
+     * @param int $n Number to encode
+     * @return string Encoded string
+     */
+    public function encode($n) {
+        $k = 0;
+
+        if ($this->padding > 0 && !empty($this->salt)) {
+            $k = self::get_seed($n, $this->salt, $this->padding);
+            $n = (int)($k.$n);
+        }
+
+        return self::num_to_alpha($n, $this->chars);
+    }
+
+    /**
+     * Converts an encoded string into a number.
+     *
+     * @param string $s String to decode
+     * @return int Decoded number
+     */
+    public function decode($s) {
+        $n = self::alpha_to_num($s, $this->chars);
+
+        return (!empty($this->salt)) ? substr($n, $this->padding) : $n;
+    }
+
+    /**
+     * Gets a number for padding based on a salt.
+     *
+     * @param int $n Number to pad
+     * @param string $salt Salt string
+     * @param int $padding Padding length
+     * @return int Number for padding
+     */
+    public static function get_seed($n, $salt, $padding) {
+        $hash = md5($n.$salt);
+        $dec = hexdec(substr($hash, 0, $padding));
+        $num = $dec % pow(10, $padding);
+        if ($num == 0) $num = 1;
+        $num = str_pad($num, $padding, '0');
+
+        return $num;
+    }
+
+    /**
+     * Converts a number to an alpha-numeric string.
+     *
+     * @param int $num Number to convert
+     * @param string $s String of characters for conversion
+     * @return string Alpha-numeric string
+     */
+    public static function num_to_alpha($n, $s) {
+        $b = strlen($s);
+        $m = $n % $b;
+
+        if ($n - $m == 0) return substr($s, $n, 1);
+
+        $a = '';
+
+        while ($m > 0 || $n > 0) {
+            $a = substr($s, $m, 1).$a;
+            $n = ($n - $m) / $b;
+            $m = $n % $b;
+        }
+
+        return $a;
+    }
+
+    /**
+     * Converts an alpha numeric string to a number.
+     *
+     * @param string $a Alpha-numeric string to convert
+     * @param string $s String of characters for conversion
+     * @return int Converted number
+     */
+    public static function alpha_to_num($a, $s) {
+        $b = strlen($s);
+        $l = strlen($a);
+
+        for ($n = 0, $i = 0; $i < $l; $i++) {
+            $n += strpos($s, substr($a, $i, 1)) * pow($b, $l - $i - 1);
+        }
+
+        return $n;
+    }
+
+    /**
+     * Looks up a URL in the database by id.
+     *
+     * @param string $id URL id
+     * @return array URL record
+     */
+    public function fetch($id) {
+        $statement = $this->connection->prepare(
+            'SELECT * FROM urls WHERE id = ?'
+        );
+        $statement->execute(array($id));
+
+        return $statement->fetch(PDO::FETCH_ASSOC);
+    }
+
+    /**
+     * Attempts to locate a URL in the database.
+     *
+     * @param string $url URL
+     * @return array URL record
+     */
+    public function find($url) {
+        return UrlsFacade::findOneBy([
+            "url" => $url,
+            "status" => 0
+        ], "id,url");
+    }
+
+    /**
+     * Stores a URL in the database.
+     *
+     * @param string $url URL to store
+     * @return int Insert id
+     */
+    public function store($url) {
+        $info = UrlsFacade::save([
+            "url" => $url,
+            "status" => 0
+        ]);
+        return $info->id;
+    }
+
+    /**
+     * Updates statistics for a URL.
+     *
+     * @param int $id URL id
+     */
+    public function update($id, $key) {
+        return UrlsFacade::update($id, [
+            "key" => $key
+        ]);
+    }
+
+    /**
+     * Sends a redirect to a URL.
+     *
+     * @param string $url URL
+     */
+    public function redirect($url) {
+        header("Location: $url", true, 301);
+        exit();
+    }
+
+    /**
+     * Sends a 404 response.
+     */
+    public function not_found() {
+        header('Status: 404 Not Found');
+        exit(
+            '<h1>404 Not Found</h1>'.
+            str_repeat(' ', 512)
+        );
+    }
+
+    /**
+     * Sends an error message.
+     *
+     * @param string $message Error message
+     */
+    public function error($message) {
+        exit("<h1>$message</h1>");
+    }
+
+    /**
+     * Adds an IP to allow saving URLs.
+     *
+     * @param string|array $ip IP address or array of IP addresses
+     */
+    public function allow($ip) {
+        if (is_array($ip)) {
+            $this->whitelist = array_merge($this->whitelist, $ip);
+        }
+        else {
+            array_push($this->whitelist, $ip);
+        }
+    }
+
+    /**
+     * Starts the program.
+     */
+    public function run($url)
+    {
+        $url = urldecode($url);
+        // If adding a new URL
+        if (!empty($url)) {
+            if (!empty($this->whitelist) && !in_array($_SERVER['REMOTE_ADDR'], $this->whitelist)) {
+                throw new ApiException("", "请求地址不在白名单中");
+            }
+            if (preg_match('/^http[s]?\:\/\/[\w]+/', $url)) {
+                $result = $this->find($url); //查询是否存在
+                // Not found, so save it
+                if (empty($result)) {
+                    $id = $this->store($url); // 新增
+                    $code = $this->encode($id);
+                    $url = $this->hostname . '/' . $code;
+                    $this->update($id, $code);
+                } else {
+                    $url = $this->hostname . '/' . $this->encode($result['id']);
+                }
+                return $url;
+            } else {
+                throw new ApiException("", "地址错误");
+            }
+        } else {
+            throw new ApiException("", "地址错误");
+        }
+    }
+}

+ 33 - 0
app/Base/Middleware/ApiPermission.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Base\Middleware;
+
+use App\Api\Facades\ApiFacade;
+use App\Base\Exceptions\ApiException;
+use Closure;
+
+class ApiPermission
+{
+    public function handle($request, Closure $next, $guard = null)
+    {
+        $user = $request->user();
+        if (!isset($user['id']) || !$user['id']) {
+            throw new ApiException('common.auth_fail', '认证失败');
+        }
+        if (is_object($user)) {
+            $user = $user->toArray();
+        }
+        $userId = $user['id'];
+        $tk = $request->header('api_token');
+        if(empty($tk)){
+            $tk = $request->input('api_token');
+        }
+        $login_singleton=config('app.login_singleton');
+        //如果不是最近登录的token
+        if ($login_singleton&&!empty($tk)&& !ApiFacade::isLastToken($userId, $tk)) {
+            ApiFacade::logout($tk);
+            throw new ApiException('common.user_other_login', '您的账号已在其他地方登录!');
+        }
+        return $next($request);
+    }
+}

+ 48 - 0
app/Base/Middleware/Authenticate.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace App\Base\Middleware;
+
+use Closure;
+use Illuminate\Contracts\Auth\Factory as Auth;
+use App\Base\Exceptions\ApiException;
+
+class Authenticate
+{
+    /**
+     * The authentication guard factory instance.
+     * @var \Illuminate\Contracts\Auth\Factory
+     */
+    protected $auth;
+
+    /**
+     * Create a new middleware instance.
+     * @param  \Illuminate\Contracts\Auth\Factory $auth
+     */
+    public function __construct(Auth $auth)
+    {
+        $this->auth = $auth;
+    }
+
+    /**
+     * Handle an incoming request.
+     * @param  \Illuminate\Http\Request $request
+     * @param  \Closure $next
+     * @param  string|null $guard
+     * @return mixed
+     * @throws ApiException
+     */
+    public function handle($request, Closure $next, $guard = null)
+    {
+        $user = $request->user();
+        if (!isset($user['id']) || !$user['id']) {
+            throw new ApiException('common.auth_fail', '认证失败');
+        }
+        if (is_object($user)) {
+            $user = $user->toArray();
+        }
+//        if(!checkApiPermission($user['permissions'],$request->path(), ($request->method() == 'GET' ? $request->all() : []))){
+//            throw new ApiException('common.no_permission', '没有权限');
+//        }
+        return $next($request);
+    }
+}

+ 58 - 0
app/Base/Middleware/Localization.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace App\Base\Middleware;
+
+use Closure;
+use App\Base\Exceptions\ApiException;
+
+class Localization
+{
+
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request $request
+     * @param  \Closure $next
+     * @param  string|null $guard
+     * @return mixed
+     * @throws ApiException
+     */
+    public function handle($request, Closure $next, $guard = null)
+    {
+        //设置多语言
+        $isOverseas = boolval(config('app.overseas_edition'));
+        $siteName = 'inside';
+        if ($isOverseas) {
+            $siteName = 'overseas';
+        }
+        $lang = $request->input('lang');
+        if(empty($lang)) {
+            //?lang=en-us
+            // 使用客户端的语言设置
+            $lang = $request->header("Language-Set");
+            if (empty($lang)) {
+                // 使用cookie语言设置
+                $globalLocaleCookieKey = $siteName.'_'.config('app.global_locale_cookie_key');
+                $lang = $request->cookie($globalLocaleCookieKey);
+            }
+        }
+        $translator = app('translator');
+        $curLang = $translator->getLocale();
+        $allowSet = false;
+        if (!empty(config('app.overseas_edition'))) {
+            // 海外版支持的语言
+            if (in_array($lang, ['en-us', 'zh-tw'])) {
+                $allowSet = true;
+            }
+        } else {
+            if ($lang == 'zh-cn') {
+                $allowSet = true;
+            }
+        }
+        if ($allowSet && $lang && $lang != $curLang) {
+            $translator->setLocale($lang);
+        }
+        $response = $next($request);
+        return $response;
+    }
+}

+ 59 - 0
app/Base/Middleware/Permission.php

@@ -0,0 +1,59 @@
+<?php
+
+namespace App\Base\Middleware;
+
+use App\Base\Exceptions\ApiException;
+use Closure;
+
+class Permission
+{
+    public function handle($request, Closure $next, $guard = null)
+    {
+//        $user = $request->user();
+//        if ($user['role_id'] != 1) {
+//            $path = $request->path();
+//            if (strpos($path, '/') !== 0) {
+//                $path = '/' . $path;
+//            }
+//
+//            if (!isset($user['rules'][$path]) && !isset($user['rules_params'][$path])) {
+//                throw new ApiException('common.no_permission', '您没有权限');
+//            }
+//
+//            //如果有字段参数需要判断
+//            if (!empty($user['rules_params'][$path])) {
+//                $params = $request->all();
+//                $check = 0;
+//                $check2 = 1;
+//                foreach ($user['rules_params'][$path] as $key => $rp) {
+//                    if (!empty($rp['params'])) {
+//                        foreach ($rp['params'] as $k => $p) {
+//                            if (isset($params[$k])) {
+//                                if ($params[$k] == $p || empty($p)) {
+//                                    $check = 1;
+//                                    $check2 = 1;
+//                                    break;
+//                                }
+//                                $check2 = 0;
+//                            }
+//                        }
+//                    }
+//                    if ($check) {
+//                        break;
+//                    }
+//                }
+//                //如果参数没有对上,有可能是不需要参数
+//                if ($check2 && !$check) {
+//                    if (isset($user['rules_params'][$path][-1])) {
+//                        $check = 1;
+//                    }
+//                }
+//                if (!$check) {
+////                    Log::info('user222'.json_encode($user['rules_params'][$path]));
+//                    throw new ApiException('common.no_permission', '您没有权限');
+//                }
+//            }
+//        }
+        return $next($request);
+    }
+}

+ 185 - 0
app/Base/Middleware/Response.php

@@ -0,0 +1,185 @@
+<?php
+
+
+namespace App\Base\Middleware;
+
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 数据返回中间件
+ * Class Response
+ * @package App\Base\Middleware\Middleware
+ */
+class Response
+{
+    /**
+     * @param Request $request
+     * @param Closure $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        $curTime = gettimeofday();
+        $beginTime = $curTime['sec'] * 1000000 + $curTime['usec'];
+        if (strtolower($request->getMethod()) == 'options') {
+            $response = new \Illuminate\Http\Response();
+            $response->withHeaders([
+                'Content-Type' => $request->ajax() ? 'application/json; charset:UTF-8' : 'text/html; charset=UTF-8',
+                'Access-Control-Allow-Origin' => '*',
+                'Access-Control-Allow-Credentials' => 'true',
+                'Access-Control-Allow-Methods' => 'PUT, GET, POST, DELETE, OPTIONS',
+                'Access-Control-Allow-Headers' => 'Content-Type,token,api-token,X-Requested-With,Language-Set',
+                'Access-Control-Expose-Headers' => '*'
+            ]);
+            return $response;
+        }
+        //去除请求参数左右两边空格
+        $params = $request->all();
+        foreach ($params as $key => $value) {
+            if (!is_array($value)) {
+                $params[$key] = trim($value);
+            }
+        }
+        $request->replace($params);
+        if ($this->mTrans($request)) {
+            DB::beginTransaction();
+        }
+
+
+        //增加redis的连接判断,默认如果连接错误会抛出异常,这边捕获异常,下次会重新连接
+//        checkRedisPing();
+
+//        DB::enableQueryLog();
+        //在有auth中间件认证时,包括中间件里也有调用request->user(),但其实是只有调用一次的
+        $request->attributes->set('_is_check_auth', 1);
+        $user = $request->user();
+        $request->attributes->set('_is_check_auth', 0);
+
+        $response = $next($request);
+        if ($response->getStatusCode() == 200 && (!isset($response->exception) || $response->exception == null)) {
+            if ($this->mTrans($request)) {
+                DB::commit();
+            }
+
+            if ($request->ajax() && !($response instanceof StreamedResponse)) {
+                $content = $response->getContent();
+                $content = json_encode([
+                    'ret' => 0,
+                    'msg' => 'success.',
+                    'data' => ($this->isJson($content) ? json_decode($content, true) : $content)
+                ], JSON_UNESCAPED_UNICODE);
+                $response->setContent($content);
+            }
+        } elseif ($response->getStatusCode() == 404 && !$request->ajax()) {
+            //不是ajax请求 跳转到404页面
+            return response(view("errors.404"), 404);
+        } else {
+            if ($this->mTrans($request) || DB::transactionLevel()) {
+                DB::rollBack();
+            }
+            if (!$request->ajax() && !($response instanceof StreamedResponse)) {
+                $content = json_decode($response->getContent(), true);
+                $response->setContent($content['msg']??'');
+            }
+        }
+        if (method_exists($response,'withHeaders')) {
+            $curTime = gettimeofday();
+            $costTime = ($curTime['sec'] * 1000000 + $curTime['usec']) - $beginTime;
+            $costTime = $costTime / 1000000.0;
+//            $this->saveAccessLog($request->method(), $request->path(), $params, $response, $request, $costTime);
+            $response->withHeaders([
+                'Access-Control-Allow-Origin' => '*',
+                'Access-Control-Allow-Credentials' => 'true',
+                'Access-Control-Allow-Methods' => 'PUT, GET, POST, DELETE, OPTIONS',
+                'Access-Control-Allow-Headers' => 'Content-Type,token,api_token,X-Requested-With,Language-Set',
+                'Access-Control-Expose-Headers' => '*'
+            ]);
+            if ($request->ajax()) {
+                $response->withHeaders(['Content-Type' => 'application/json; charset:UTF-8']);
+            } elseif (!$response->headers->get('content-type')) {
+                $response->withHeaders(['Content-Type' => 'text/html; charset=UTF-8']);
+            }
+            $response->withHeaders(['Language' => app('translator')->getLocale()]);
+        }
+        DB::disconnect();
+        return $response;
+    }
+
+    /**
+     * 是否为json格式的字符串
+     * @param $string
+     * @return bool
+     */
+    private function isJson($string)
+    {
+        json_decode($string);
+        return (json_last_error() == JSON_ERROR_NONE);
+    }
+
+    /**
+     * 是否开启事务
+     * @param Request $request
+     * @return bool
+     */
+    private function mTrans($request)
+    {
+        if (/*strtolower($request->method())!='get' && */
+        config('database.transaction')
+        ) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 添加访问日志
+     * @param $response
+     */
+    private function saveAccessLog($method, $route, $params, $response, $request, $costTime = 0)
+    {
+        try {
+            $user = Auth::user();
+            if (!isset($user['id']) || !$user['id']) {
+                return;
+            }
+
+            // 过滤掉常规路由,主要是定时请求
+            $route = trim($route, '/');
+            if (in_array($route, ['notice/news', 'checkLogin'])) {
+                return;
+            }
+
+            $type = 0;
+            $serverIp = env('SERVER_IP', ''); // 增加服务器IP,便于快速定位日志
+            $data = [
+                'method' => strtolower($method),
+                'route' => '/' . $route, // 屏蔽掉$request->root()
+                'params' => $params,
+                'status_code' => $response->getStatusCode(),
+                'response' => json_decode($response->getContent(), true),
+                'error_code' => $response->exception ? $response->exception->getCode() : 0,
+                'error_message' => $response->exception ? $response->exception->getMessage() : '',
+                'company_id' => $user->company_id ?? '',
+                'user_id' => $user->id ?? '',
+                'is_marketing' => 0,
+                'type' => $type,  //值:0为cdp,1为marketing,2为dmp,3为小程序
+                'ip' => $request->getClientIp(),
+                'date' => date('Y-m-d'),
+                'create_time' => date('Y-m-d H:i:s'),
+                'cost_time' => $costTime,
+                'server_ip' => $serverIp,
+            ];
+
+//            checkRedisPing('es_log');
+//            $redis = app('redis')->connection('es_log');
+//            $redis->rpush('queue:api_log', json_encode($data, JSON_UNESCAPED_UNICODE));
+        } catch (\Exception $e) {
+            Log::info('es_log:' . print_r($e->getMessage(), true));
+        }
+    }
+}

+ 95 - 0
app/Base/Models/ApiSoftDeletes.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace App\Base\Models;
+
+
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+trait ApiSoftDeletes
+{
+    use SoftDeletes ;
+
+    public static function bootSoftDeletes()
+    {
+        static::addGlobalScope(new ApiSoftDeletingScope);
+    }
+
+    /**
+     * Determine if the model instance has been soft-deleted.
+     *
+     * @return bool
+     */
+    public function trashed()
+    {
+        return $this->{$this->getDeletedAtColumn()} == static::STATUS_DELETED;
+    }
+
+    /**
+     * Perform the actual delete query on this model instance.
+     *
+     * @return void
+     */
+    protected function runSoftDelete()
+    {
+        $query = $this->newQueryWithoutScopes()->where($this->getKeyName(), $this->getKey());
+
+        $this->{$this->getDeletedAtColumn()} = $this->getDeletedColumnValue();
+
+        $query->update([$this->getDeletedAtColumn() => $this->getDeletedColumnValue()]);
+    }
+
+    /**
+     * Restore a soft-deleted model instance.
+     *
+     * @return bool|null
+     */
+    public function restore()
+    {
+        // If the restoring event does not return false, we will proceed with this
+        // restore operation. Otherwise, we bail out so the developer will stop
+        // the restore totally. We will clear the deleted timestamp and save.
+        if ($this->fireModelEvent('restoring') === false) {
+            return false;
+        }
+
+        $this->{$this->getDeletedAtColumn()} = static::STATUS_ENABLED;
+
+        // Once we have saved the model, we will fire the "restored" event so this
+        // developer will do anything they need to after a restore operation is
+        // totally finished. Then we will return the result of the save call.
+        $this->exists = true;
+
+        $result = $this->save();
+
+        $this->fireModelEvent('restored', false);
+
+        return $result;
+    }
+
+    /**
+     * 获取删除字段值
+     * @return string|int
+     */
+    public function getDeletedColumnValue(){
+        return self::STATUS_DELETED;
+    }
+
+    /**
+     * Get the fully qualified "deleted at" column.
+     *
+     * @return string
+     */
+    public function getQualifiedDeletedAtColumn()
+    {
+        $alias = empty($this->getAliasName())?$this->getTable():$this->getAliasName();
+        return $alias.'.'.$this->getDeletedAtColumn();
+    }
+
+    /**
+     * 该方法纯粹为了覆盖SoftDeletes里面指定delete_at类型不正确的问题
+     */
+    public function initializeSoftDeletes()
+    {
+    }
+
+}

+ 150 - 0
app/Base/Models/ApiSoftDeletingScope.php

@@ -0,0 +1,150 @@
+<?php
+/**
+ * 软删除 socpe
+ */
+
+namespace App\Base\Models;
+
+
+use Illuminate\Database\Eloquent\SoftDeletingScope;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Model;
+
+class ApiSoftDeletingScope extends SoftDeletingScope
+{
+    /**
+     * All of the extensions to be added to the builder.
+     * restore:恢复
+     * WithTrashed:带有删除状态和正常状态的数据
+     * OnlyTrashed:只有删除状态的数据
+     * WithDisabled:带有禁用状态的数据 即正常状态+禁用状态
+     * OnlyDisabled:只查询出禁用状态
+     * WithAll:所有
+     * @var array
+     */
+    protected $extensions = ['Restore', 'WithTrashed', 'OnlyTrashed','WithDisabled','OnlyDisabled','WithAll'];
+
+    /**
+     * Apply the scope to a given Eloquent query builder.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @param  \Illuminate\Database\Eloquent\Model  $model
+     * @return void
+     */
+    public function apply(Builder $builder, Model $model)
+    {
+        if(is_null($model::STATUS_ENABLED)){
+            $builder->whereNull($model->getQualifiedDeletedAtColumn());
+        }else{
+//            $builder->where($model->getQualifiedDeletedAtColumn(),$model::STATUS_ENABLED);
+        }
+    }
+
+    /**
+     * Extend the query builder with the needed functions.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    public function extend(Builder $builder)
+    {
+        foreach ($this->extensions as $extension) {
+            $this->{"add{$extension}"}($builder);
+        }
+
+        $builder->onDelete(function (Builder $builder) {
+            $column = $this->getDeletedAtColumn($builder);
+            $model = $builder->getModel();
+            return $builder->update([
+                $column => $model->getDeletedColumnValue(),
+            ]);
+        });
+    }
+
+    /**
+     * Add the restore extension to the builder.
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    protected function addRestore(Builder $builder)
+    {
+        $builder->macro('restore', function (Builder $builder) {
+            $builder->withAll();
+            $model = $builder->getModel();
+            return $builder->update([$model->getDeletedAtColumn() => $model::STATUS_ENABLED]);
+        });
+    }
+
+    /**
+     * 筛选出正常状态和删除状态的数据
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    protected function addWithTrashed(Builder $builder)
+    {
+        $builder->macro('withTrashed', function (Builder $builder) {
+            $builder->withoutGlobalScope($this);
+            $model = $builder->getModel();
+            return $builder->whereIn($model::DELETED_AT,[$model::STATUS_DELETED,$model::STATUS_ENABLED]);
+        });
+    }
+
+    /**
+     * 只筛选出删除的数据
+     *
+     * @param  \Illuminate\Database\Eloquent\Builder  $builder
+     * @return void
+     */
+    protected function addOnlyTrashed(Builder $builder)
+    {
+        $builder->macro('onlyTrashed', function (Builder $builder) {
+            $model = $builder->getModel();
+
+            $builder->withoutGlobalScope($this)->where(
+                $model->getQualifiedDeletedAtColumn(),$model::STATUS_DELETED
+            );
+
+            return $builder;
+        });
+    }
+
+    /**
+     * 筛选出正常状态和禁用状态的数据
+     * @param Builder $builder
+     */
+    protected function addWithDisabled(Builder $builder){
+        $builder->macro('withDisabled', function (Builder $builder) {
+            $model = $builder->getModel();
+            $builder->withoutGlobalScope($this)->whereIn(
+                $model->getQualifiedDeletedAtColumn(),[$model::STATUS_DISABLED,$model::STATUS_ENABLED]
+            );
+            return $builder;
+        });
+    }
+
+    /**
+     * 只筛选出禁用状态的数据
+     * @param Builder $builder
+     */
+    protected function addOnlyDisabled(Builder $builder){
+        $builder->macro('onlyDisabled', function (Builder $builder) {
+            $model = $builder->getModel();
+            $builder->withoutGlobalScope($this)->where(
+                $model->getQualifiedDeletedAtColumn(),$model::STATUS_DISABLED
+            );
+            return $builder;
+        });
+    }
+
+    /**
+     * 筛选出所有数据
+     * @param Builder $builder
+     */
+    protected function addWithAll(Builder $builder){
+        $builder->macro('withAll', function (Builder $builder) {
+            return $builder->withoutGlobalScope($this);
+        });
+    }
+}

+ 536 - 0
app/Base/Models/BaseModel.php

@@ -0,0 +1,536 @@
+<?php
+namespace App\Base\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Date;
+use Illuminate\Support\Facades\DB;
+
+class BaseModel extends Model
+{
+    use Criteria;
+
+
+    /**
+     * 插入时间字段
+     */
+    const CREATED_AT = 'create_time';
+
+    /**
+     * 更新时间字段
+     */
+    const UPDATED_AT = 'update_time';
+
+    /**
+     * 状态字段
+     */
+    const DELETED_AT = 'status';
+
+    /**
+     * 正常状态
+     */
+    const STATUS_ENABLED = 0;
+
+    /**
+     * 禁用状态
+     */
+    const STATUS_DISABLED = 1;
+
+    /**
+     * 删除状态
+     */
+    const STATUS_DELETED = 2;
+
+    /**
+     * 审核状态:未提交审核
+     */
+    const AUDIT_STATUS_NOT_SUBMITTED = 0;
+
+    /**
+     * 审核状态:已提交/审核中
+     */
+    const AUDIT_STATUS_SUBMITTED = 1;
+
+    /**
+     * 审核状态:审核通过
+     */
+    const AUDIT_STATUS_SUCCESSFUL = 2;
+
+    /**
+     * 审核状态:审核失败
+     */
+    const AUDIT_STATUS_FAIL = 3;
+
+    /**
+     * 该模型是否被自动维护时间戳
+     *
+     * @var bool
+     */
+    public $timestamps = true;
+
+    /**
+     * 别名名称
+     * @var null
+     */
+    protected $aliasName = null;
+
+    /**
+     * 模型的日期字段保存格式。
+     *
+     * @var string
+     */
+    protected $dateFormat = 'Y-m-d H:i:s';
+
+
+    /**
+     * 新的连接方式,用于分库后可指定连接哪个库的,因原有的connection在分库中已失效,所以需要重新加此变量
+     * @var null
+     */
+    protected $connectionNew = null;
+
+    public function __construct(array $attributes = [])
+    {
+        parent::__construct($attributes);
+    }
+
+    /**
+     * 自动获取数据库表字段
+     */
+    public function setFillable()
+    {
+        $key = get_class($this);
+        $cache = Cache::store('file')->get($key);
+        if (empty($cache)) {
+            //            $columns = Schema::getColumnListing($this->table);
+            //如果是公共库的,会读取默认库,而默认库没有表的情况下会为空,所以改成以下语句
+            $columns = $this->getConnection()->getSchemaBuilder()->getColumnListing($this->table);
+            $cache = $columns;
+            Cache::store('file')->put($key, $columns, config('cache.columns'));
+        }
+        $this->fillable = $cache;
+    }
+
+    /**
+     * 获取所有列
+     * @return array
+     */
+    public function getColumns()
+    {
+        $this->setFillable();
+        return $this->getFillable();
+    }
+
+    /**
+     * 过滤掉非数据库字段的数据
+     * @param $data
+     * @return array
+     */
+    public function filter($data)
+    {
+        if (empty($this->fillable)) {
+            $this->setFillable();
+        }
+        $result = [];
+        if (empty($data) || !is_array($data)) {
+            return $result;
+        }
+        foreach ($this->fillable as $item) {
+            if (isset($data[$item])) {
+                $result[$item] = $data[$item];
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * 覆盖setCreatedAt 不添加createAt字段
+     *
+     * @param  mixed $value
+     * @return $this
+     */
+    public function setCreatedAt($value)
+    {
+        if (!empty(static::CREATED_AT)) {
+            $this->{static::CREATED_AT} = $value;
+        }
+        if (!empty(static::UPDATED_AT)) {
+            $this->{static::UPDATED_AT} = $value;
+        }
+        return $this;
+    }
+
+    /**
+     * Set the value of the "updated at" attribute.
+     *
+     * @param  mixed $value
+     * @return $this
+     */
+    public function setUpdatedAt($value)
+    {
+        if (!empty(static::UPDATED_AT)) {
+            $this->{static::UPDATED_AT} = $value;
+        }
+        return $this;
+    }
+
+    /**
+     * 覆盖父类方法 新建连接
+     * @return \Illuminate\Database\Query\Builder|QueryBuilder
+     */
+    protected function newBaseQueryBuilder()
+    {
+        $connection = $this->getConnection();
+
+        return new QueryBuilder(
+            $connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
+        );
+    }
+
+    /**
+     * 覆盖父类方法
+     * @param \Illuminate\Database\Query\Builder $query
+     * @return \App\Base\Models\EloquentBuilder|Model|Builder
+     */
+    public function newEloquentBuilder($query)
+    {
+        return new EloquentBuilder($query);
+    }
+
+    /**
+     * 覆盖父类方法 获取连接名称
+     * @return null|string
+     */
+    public function getConnectionName()
+    {
+        if (!config('app.database_split', false)) { // 未开启分库
+            return null;
+        }
+
+        //判断是否在model中有指定连接哪个库,如果有,直接返回此指定库,没有则继续往下按公司连接判断
+        if ($this->connectionNew) {
+            return $this->connectionNew;
+        }
+        return $this->connection;
+    }
+
+    /**
+     * 设置全局DB Config
+     * @param $dbConnectionName
+     * @param $db
+     */
+    public static function setConfigDBConnection($dbConnectionName, $db)
+    {
+        config([
+            $dbConnectionName => [
+                'driver' => 'mysql',
+                'host' => $db['host'],
+                'port' => $db['port'],
+                'database' => $db['database'],
+                'username' => $db['username'],
+                'password' => $db['password'],
+                'charset' => config('database.connections.mysql.charset') ?? 'utf8mb4',
+                'collation' => config('database.connections.mysql.collation') ?? 'utf8mb4_general_ci',
+                'prefix' => '',
+                'timezone' => '+08:00',
+                'strict' => false
+            ]]);
+    }
+
+    /**
+     * 生成DBConnectionName
+     * @param $connectionName
+     * @return string
+     */
+    public static function getDBConnectionName($connectionName)
+    {
+        $dbConnectionName = 'database.connections.' . $connectionName;
+        return $dbConnectionName;
+    }
+
+    /**
+     * 别名
+     * @param $name
+     * @return
+     */
+    public function alias($name)
+    {
+        $this->aliasName = $name;
+        return $this->from($this->getTable() . ' as ' . $name);
+    }
+
+    /**
+     * 获取别名名称
+     * @return null
+     */
+    public function getAliasName()
+    {
+        return $this->aliasName;
+    }
+
+    /**
+     * Set the table associated with the model.
+     *
+     * @param  string $table
+     * @return $this
+     */
+    public function setTable($table)
+    {
+        $table = trim($table);
+        $this->table = $table;
+        if (strpos($table, ' as ')) {
+            list($table, $alias) = explode(' as ', $table);
+            if (!empty($alias)) {
+                $this->aliasName = trim($alias);
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * 返回正常状态的值
+     * @return int
+     */
+    public function getStatusEnabled()
+    {
+        return self::STATUS_ENABLED;
+    }
+
+    /**
+     * 返回禁用状态的值
+     * @return int
+     */
+    public function getStatusDisabled()
+    {
+        return self::STATUS_DISABLED;
+    }
+
+    /**
+     * 返回删除状态的值
+     * @return int
+     */
+    public function getStatusDeleted()
+    {
+        return self::STATUS_DELETED;
+    }
+
+    public function isJsonCastingField($field)
+    {
+        if (isset($this->casts[$field]) && $this->casts[$field] == 'array') {
+            return true;
+        }
+        return false;
+    }
+
+    //重载model里的事件通知
+    public function fireModelEvent($event, $halt = true)
+    {
+        parent::fireModelEvent($event, $halt);
+    }
+
+    /**
+     * 格式化时间
+     * */
+    protected function serializeDate($date)
+    {
+        if ($date instanceof \DateTimeInterface) {
+            return $date->format('Y-m-d H:i:s');
+        }
+
+        return $date;
+    }
+
+    /**
+     * 解析时间值
+     * @param mixed $value
+     * @return \Illuminate\Support\Carbon|mixed
+     */
+    protected function asDateTime($value)
+    {
+        if (null == $value || '0000-00-00 00:00:00' == $value) {
+            return $value;
+        }
+
+        $format = $this->getDateFormat();
+        try {
+            $date = Date::createFromFormat($format, $value);
+        } catch (\InvalidArgumentException $e) {
+            $date = false;
+        }
+
+        return $date ?: Date::parse($value);
+    }
+
+    /**
+     * @param bool $alias
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function db($alias = false)
+    {
+        return DB::table($this->getTable() . ($alias ? " as $alias" : ''));
+    }
+
+    /**
+     * 批量写入数据
+     * @param $datas
+     * @return bool
+     */
+    public function insertAll($datas)
+    {
+        if (empty($datas)) {
+            return false;
+        }
+        foreach ($datas as &$data) {
+            if ($this->timestamps) {
+                $createAt = $this::CREATED_AT;
+                $updateAt = $this::UPDATED_AT;
+                $data[$updateAt] = $data[$createAt] = nowTime();
+            }
+        }
+        return DB::table($this->table)->insert($datas);
+    }
+
+    /**
+     * 保存一条记录或者多条记录
+     * @param array $data
+     * @param mixed $where
+     * @return int
+     */
+    public function updateData($data, $where = false)
+    {
+//        $data = $this->filter($data);
+        if ($this->timestamps) {
+            $updateAt = $this::UPDATED_AT;
+            $data[$updateAt] = nowTime();
+        }
+        if (!$where) {
+            $id = $data[$this->getKeyName()];
+            unset($data[$this->getKeyName()]);
+            $where = [
+                $this->getKeyName() => $id
+            ];
+        }
+        return $this->bindCriteria($this->db(),$where)->update($data);
+    }
+
+    /**
+     * 新增记录
+     * @param $data
+     * @return int
+     */
+    public function insertData($data)
+    {
+//        $data = $this->filter($data);
+        if ($this->timestamps) {
+            $createAt = $this::CREATED_AT;
+            $updateAt = $this::UPDATED_AT;
+            $data[$updateAt] = $data[$createAt] = nowTime();
+        }
+        return $this->db()->insertGetId($data);
+    }
+
+    /**
+     * 获取一条记录
+     * @param $id
+     * @param $field
+     * @return Model|\Illuminate\Database\Query\Builder|null|object
+     */
+    public function findOneById($id, $field = "*")
+    {
+        $db = $this->db()->selectRaw($field);
+        if (method_exists($this, 'runSoftDelete') && !isset($criteria['status'])) {
+            $db = $db->whereIn('status', [0, 1]);
+        }
+        return $db->where([$this->getKeyName() => $id])->first();
+    }
+
+    /**
+     * 根据条件获取一条记录
+     * @param $criteria
+     * @param $field
+     * @return \Illuminate\Database\Eloquent\Model|null|static
+     */
+    public function findOneBy($criteria, $field = "*")
+    {
+        $db = $this->db()->selectRaw($field);
+        if (method_exists($this, 'runSoftDelete') && !isset($criteria['status'])) {
+            $db = $db->whereIn('status', [0, 1]);
+        }
+        return $this->bindCriteria($db, $criteria)->first();
+    }
+
+    /**
+     * 根据条件获取一条记录
+     * @param $criteria
+     * @param $field
+     * @return \Illuminate\Database\Eloquent\Model|null|static
+     */
+    public function countBy($criteria)
+    {
+        $db = $this->db();
+        if (method_exists($this, 'runSoftDelete') && !isset($criteria['status'])) {
+            $db = $db->whereIn('status', [0, 1]);
+        }
+        return $this->bindCriteria($db, $criteria)->count();
+    }
+
+    /**
+     * 绑定查询条件
+     * @param \Illuminate\Database\Query\Builder $db
+     * @param $criteria
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function bindCriteria($db, &$criteria)
+    {
+        $where = [];
+        foreach ($criteria as $k=>$v) {
+            if (!is_numeric($k)) {
+                if (is_array($v)) {
+                    if ($v[0] == 'in') {
+                        $db = $db->whereIn($k, $v[1]);
+                    } else {
+                        $where[] = [$k, $v[0], $v[1]];
+                    }
+                } else {
+                    $where[$k] = $v;
+                }
+            } else if (is_array($v)) {
+                if ($v[1] == 'in') {
+                    $db = $db->whereIn($v[0], $v[2]);
+                } else {
+                    $where[] = $v;
+                }
+            }
+        }
+        return $db->where($where);
+    }
+
+    /**
+     * 根据条件获取多条记录
+     * @param $criteria
+     * @param $field
+     * @return array
+     */
+    public function findBy($criteria, $field = "*")
+    {
+        $db = $this->db()->selectRaw($field);
+        if (method_exists($this, 'runSoftDelete') && !isset($criteria['status'])) {
+            $db = $db->whereIn('status', [0, 1]);
+        }
+        return $this->bindCriteria($db,$criteria)
+                    ->get()->toArray();
+    }
+
+    /**
+     * 根据条件删除
+     * @param $criteria
+     * @return int
+     */
+    public function deleteBy($criteria)
+    {
+        if (method_exists($this, 'runSoftDelete') && !isset($criteria['status'])) {
+            return $this->db()->where($criteria)->update(['status'=>2]);
+        } else {
+            return $this->db()->where($criteria)->delete();
+        }
+    }
+}

+ 11 - 0
app/Base/Models/BaseMongoModel.php

@@ -0,0 +1,11 @@
+<?php
+
+
+namespace App\Base\Models;
+
+use Jenssegers\Mongodb\Eloquent\Model;
+
+class BaseMongoModel extends Model
+{
+    protected $connection = 'mongodb';
+}

+ 30 - 0
app/Base/Models/ComOssETagModel.php

@@ -0,0 +1,30 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2018/4/12
+ * Time: 下午11:18
+ */
+namespace App\Base\Models;
+
+
+class ComOssETagModel extends BaseModel
+{
+    protected $table = 'com_oss_etag';
+
+    /**
+     * 插入时间字段
+     */
+    const CREATED_AT = 'create_time';
+
+    /**
+     * 更新时间字段
+     */
+    const UPDATED_AT = null;
+
+    /**
+     * 状态字段
+     */
+    const DELETED_AT = null;
+
+}

+ 228 - 0
app/Base/Models/Criteria.php

@@ -0,0 +1,228 @@
+<?php
+
+
+namespace App\Base\Models;
+
+
+use Illuminate\Database\Eloquent\Model;
+
+trait Criteria
+{
+    /**
+     * 创建查询条件
+     * @param $condition
+     * @$conditionExample 有用到补充下例子
+     * [
+     *    'id' => [
+     *        ['notIn', [1,2,3]]
+     *     ],
+     *     'score' => [
+     *          ['>', 60]
+     *      ]
+     *     'create_time'    => [
+     *          ['between',['2017-08-01','2017-08-30']
+     *      ]
+     *     'user_id' => 10338,
+     *     '_string' => 'status=0 and type=1',
+     *     'name' => [
+     *         ['like', "huyunnan"]
+     *     ]
+     * ]
+     * @return Model
+     */
+    public function buildQuery($condition)
+    {
+        if (is_string($condition)) {
+            return $this->whereRaw($condition);
+        }
+        if (is_array($condition)) {
+            $model = $this;
+            foreach ($condition as $key => $item) {
+                //laravel多条件写法 [ ['id','>',1],[...] ]
+                if (is_int($key) && is_array($item)) {
+                    $model = $model->where([$item]);
+                    continue;
+                }
+                switch ($key) {
+                    case '_string':
+                        $model = $model->whereRaw($item);
+                        break;
+                    case '_null':
+                        $model = $this->buildNullQuery($model, $item);
+                        break;
+                    case '_notNull':
+                        $model = $this->buildNotNullQuery($model, $item);
+                        break;
+                    default:
+                        if (!is_array($item)) {
+                            $model = $model->where($key, $item);
+                        } else {
+                            $model = $this->buildItemQuery($model, $key, $item);
+                        }
+                }
+            }
+            return $model;
+        }
+        return $this;
+    }
+
+    /**
+     * 查询条件
+     * @param $model
+     * @param $key
+     * @param $query
+     * @return Model|mixed
+     */
+    private function buildItemQuery($model, $key, $query)
+    {
+        // 特别处理下: 'a' => ['!=', 2]这种写法
+        if (isset($query[0]) && !is_array($query[0])) {
+            $query = [$query];
+        }
+
+        foreach ($query as $index => $item) {
+            if (count($item) < 2) {
+                continue;
+            }
+            switch ($item[0]) {
+                case 'in':
+                    $model = $this->buildInQuery($model, $key, $item[1]);
+                    break;
+                case 'notIn':
+                    $model = $this->buildNotInQuery($model, $key, $item[1]);
+                    break;
+                case 'like':
+                    $model = $this->buildLikeQuery($model, $key, $item[1]);
+                    break;
+                case 'between':
+                    $model = $this->buildBetweenQuery($model, $key, $item[1]);
+                    break;
+                case 'neq':
+                    $model = $this->buildNotInQuery($model, $key, is_array($item[1])?$item[1]:[$item[1]]);
+                    break;
+                case '>':
+                case '<':
+                case '=':
+                case '>=':
+                case '<=':
+                    $model = $model->where($key, $item[0], $item[1]);
+                    break;
+                default:
+                    if (!is_array($item[1])) {
+                        $model = $model->where($key, $item[0], $item[1]);
+                    } else {
+                        $model = $model->where($query);
+                    }
+                    break;
+            }
+            unset($query[$index]);
+        }
+        if (!empty($query) && count($query) >= 2) {
+            $model = $model->where($key, $query[0], $query[0] == 'like' ? '%' . $query[1] . '%' : $query[1]);
+        }
+        return $model;
+    }
+
+    /**
+     * in查询条件
+     * @param $model
+     * @param $key
+     * @param $query
+     * @return Model
+     */
+    private function buildInQuery($model, $key, $query)
+    {
+        if (is_array($query)) {
+            $model = $model->whereIn($key, $query);
+        } else {
+            $model = $model->whereIn($key, [$query]);
+        }
+        return $model;
+    }
+
+    /**
+     * not in
+     * @param $model
+     * @param $key
+     * @param $query
+     * @return mixed
+     */
+    private function buildNotInQuery($model, $key, $query)
+    {
+        if (is_array($query)) {
+            $model = $model->whereNotIn($key, $query);
+        } else {
+            $model = $model->whereNotIn($key, [$query]);
+        }
+        return $model;
+    }
+
+    /**
+     * null
+     * @param $model
+     * @param $query
+     * @return mixed
+     */
+    private function buildNullQuery($model, $query)
+    {
+        if (is_array($query)) {
+            foreach ($query as $item) {
+                $model = $model->whereNull($item);
+            }
+        }
+        return $model;
+    }
+
+    /**
+     * not null
+     * @param $model
+     * @param $query
+     * @return mixed
+     */
+    private function buildNotNullQuery($model, $query)
+    {
+        if (is_string($query)) {
+            return $model->whereNotNull($query);
+        }
+        if (is_array($query)) {
+            foreach ($query as $item) {
+                $model = $model->whereNotNull($item);
+            }
+        }
+        return $model;
+    }
+
+    /**
+     * like
+     * @param $model
+     * @param $query
+     * @return mixed
+     */
+    private function buildLikeQuery($model, $key, $query)
+    {
+        if (!is_array($query)) {
+            $model = $model->where($key, 'like', '%' . $query . '%');
+        }
+        return $model;
+    }
+
+    /**
+     * between
+     * @param $model
+     * @param $key
+     * @param $query
+     * @return mixed
+     */
+    private function buildBetweenQuery($model, $key, $query)
+    {
+        if (is_array($query)) {
+            $model = $model->whereBetween($key, $query);
+        }
+        return $model;
+    }
+
+    public function joinRaw()
+    {
+
+    }
+}

+ 39 - 0
app/Base/Models/EloquentBuilder.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace App\Base\Models;
+
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Pagination\Paginator;
+
+/**
+ * @mixin \Illuminate\Database\Query\Builder
+ */
+class EloquentBuilder extends Builder
+{
+    /**
+     * Paginate the given query.
+     *
+     * @param  int  $perPage
+     * @param  array  $columns
+     * @param  string  $pageName
+     * @param  int|null  $page
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
+    {
+        $page = $page ?: Paginator::resolveCurrentPage($pageName);
+
+        $perPage = $perPage ?: $this->model->getPerPage();
+
+        $results = ($total = $this->toBase()->getCountForPagination($columns))
+            ? $this->forPage($page, $perPage)->get($columns)
+            : $this->model->newCollection();
+
+        return $this->paginator($results, $total, $perPage, $page, [
+            'path' => Paginator::resolveCurrentPath(),
+            'pageName' => $pageName,
+        ]);
+    }
+}

+ 12 - 0
app/Base/Models/QueryBuilder.php

@@ -0,0 +1,12 @@
+<?php
+
+
+namespace App\Base\Models;
+
+
+use Illuminate\Database\Query\Builder;
+
+class QueryBuilder extends Builder
+{
+    use Criteria;
+}

+ 103 - 0
app/Base/Providers/AppServiceProvider.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace App\Base\Providers;
+
+
+use App\Activity\Providers\ActivityServiceProvider;
+use App\Admin\Providers\AdminServiceProvider;
+use App\Api\Providers\ApiServiceProvider;
+use App\Attachment\Providers\AttachmentServiceProvider;
+use App\Base\Facades\AliPayFacade;
+use App\Base\Facades\ApiSysUserFacade;
+use App\Base\Facades\ObsFacade;
+use App\Base\Facades\PaypalPayFacade;
+use App\Base\Facades\PinYinFacade;
+use App\Base\Facades\SmsFacade;
+use App\Base\Facades\UploadFacade;
+use App\Base\Facades\WechatPayFacade;
+use App\Base\Models\ComOssETagModel;
+use App\Base\Services\AliPayService;
+use App\Base\Services\ApiSysUserService;
+use App\Base\Services\ObsService;
+use App\Base\Services\PaypalPayService;
+use App\Base\Services\PinYinService;
+use App\Base\Services\SmsService;
+use App\Base\Services\UploadService;
+use App\Base\Services\WechatPayService;
+use App\Basic\Providers\BasicServiceProvider;
+use App\Blog\Providers\BlogServiceProvider;
+use App\Crontab\Providers\TaskServiceProvider;
+use App\Doc\Providers\DocServiceProvider;
+use App\Expert\Providers\ExpertServiceProvider;
+use App\Form\Providers\FormServiceProvider;
+use App\Live\Providers\LiveServiceProvider;
+use App\Log\Providers\LogProvider;
+use App\Order\Providers\OrderServiceProvider;
+use App\RedisDeposit\Providers\RedisDepositServiceProvider;
+use App\Sys\Providers\SysServiceProvider;
+use App\TencentCloud\Providers\TencentServiceProvider;
+use App\User\Providers\UserServiceProvider;
+use App\Web\Providers\WebServiceProvider;
+use Illuminate\Redis\RedisServiceProvider;
+use Intervention\Image\ImageServiceProvider;
+use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
+
+class AppServiceProvider extends ServiceProvider
+{
+    //路由文件名
+    protected $routes = 'routes.php';
+
+    public function boot()
+    {
+        //sql打印 不提交
+       /* \DB::listen(function ($query) {
+            $sql = array_reduce($query->bindings, function($sql, $binding) {
+                return preg_replace('/\?/', is_numeric($binding) ? $binding : sprintf("'%s'", $binding), $sql, 1);
+            }, $query->sql);
+
+            \Log::info($sql);
+        });*/
+        //自动载入路由
+        $func = new \ReflectionClass(get_class($this));
+        $path = str_replace($func->getShortName() . '.php', '', $func->getFileName());
+        $routesFile = $path . '../' . $this->routes;
+        if (file_exists($routesFile)) {
+            require $routesFile;
+        }
+
+        if (! isset($this->app['blade.compiler'])) {
+            $this->app['view'];
+        }
+        parent::boot();
+    }
+
+
+    /**
+     * 注册
+     */
+    public function register()
+    {
+        //基础服务
+        $this->registerBaseService();
+        //文档模块
+        $this->app->register(DocServiceProvider::class);
+
+        // 接口模块
+        $this->app->register(ApiServiceProvider::class);
+        //注册定时任务模块
+        $this->app->register(TaskServiceProvider::class);
+        $this->app->register(BasicServiceProvider::class);
+
+    }
+
+    // 注册基础服务
+    public function registerBaseService()
+    {
+        // redis服务
+        $this->app->register(RedisServiceProvider::class);
+        // 短信服务
+//        $this->app->register(AliyunsmsServiceProvider::class);
+        // 授权验证
+        $this->app->register(AuthServiceProvider::class);
+    }
+}

+ 91 - 0
app/Base/Providers/AuthServiceProvider.php

@@ -0,0 +1,91 @@
+<?php
+
+namespace App\Base\Providers;
+
+use App\Admin\Facades\AdminUserFacade;
+use App\Api\Facades\ApiFacade;
+use App\Base\Facades\ApiSysUserFacade;
+use Illuminate\Support\ServiceProvider;
+use App\Base\Exceptions\ApiException;
+
+
+class AuthServiceProvider extends ServiceProvider
+{
+    /**
+     * Register any application services.
+     *
+     * @return void
+     */
+    public function register()
+    {
+    }
+
+    /**
+     * Boot the authentication services for the application.
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        // Here you may define how you wish users to be authenticated for your Lumen
+        // application. The callback which receives the incoming request instance
+        // should return either a User instance or null. You're free to obtain
+        // the User instance via an API token or any other method necessary.
+        //处理swoole框架中带来的认证重复问题
+        $auth = app('auth');
+        $reflClass = new \ReflectionClass($auth);
+        $reflProp = $reflClass->getProperty('guards');
+        $reflProp->setAccessible(true);
+        $reflProp->setValue($auth, []);
+
+        $reflProp = $reflClass->getProperty('customCreators');
+        $reflProp->setAccessible(true);
+        $reflProp->setValue($auth, []);
+
+        $this->app['auth']->viaRequest('api', function ($request) {
+//            checkRedisPing();
+            $token = $request->header('token');
+            if (empty($token)) {
+                $token = $request->input('token');
+            }
+            $apiToken = $request->header('api_token');
+            if(empty($apiToken)){
+                $apiToken = $request->input('api_token');
+            }
+            if ($apiToken) {
+                $data = ApiFacade::findUserInfoByApiToken($apiToken);
+                if (!empty($data)) {
+                    //1天做一次缓存更新
+                    $time=time();
+                    $heartBeatTime= config('cache.heart_beat_time');//心跳时间
+                    if (!empty($data['heart_beat_time'])&&($data['heart_beat_time']+$heartBeatTime) <$time) {
+                        $res= ApiSysUserFacade::heartBeat($data['sys_api_token']);
+                        if(!$res){
+                            ApiFacade::forgotToken($apiToken);
+                            throw new ApiException('common.auth_fail', '认证失败');
+                        }
+                        $data['heart_beat_time']=$time;
+                    }
+                    $data =   ApiFacade::updateUserInfoCache($apiToken, $data);
+                    return $data;
+                }
+            } else if ($token) {
+                $data = AdminUserFacade::getTokenData($token);
+                if (!empty($data)) {
+                    $expires = $data['expiration_time'];
+                    //token时间是否过期
+                    if ($expires && time() + (30 * 60) > $expires) {
+                        //快过期时,自动延长
+                        $cacheTokenTimeMinute = config('cache.token');
+                        $nowTime = time();
+                        $expiration_time = $nowTime + $cacheTokenTimeMinute;
+                        $userInfo['expiration_time'] = $expiration_time;//过期时间
+                        AdminUserFacade::setToken($token, $data);
+                    }
+                }
+                return $data;
+            }
+            return null;
+        });
+    }
+}

+ 29 - 0
app/Base/Providers/EventServiceProvider.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Base\Providers;
+
+use Illuminate\Database\Events\StatementPrepared;
+use Illuminate\Support\Facades\Event;
+use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
+
+class EventServiceProvider extends ServiceProvider
+{
+    /**
+     * The event listener mappings for the application.
+     *
+     * @var array
+     */
+    protected $listen = [
+        'App\Events\SomeEvent' => [
+            'App\Listeners\EventListener',
+        ],
+    ];
+
+    public function boot()
+    {
+        Event::listen(StatementPrepared::class, function ($event) {
+            $event->statement->setFetchMode(\PDO::FETCH_ASSOC);
+        });
+
+    }
+}

+ 563 - 0
app/Base/Services/AbstractBaseService.php

@@ -0,0 +1,563 @@
+<?php
+
+
+namespace App\Base\Services;
+
+
+use App\Base\Models\BaseModel;
+use Illuminate\Http\Request;
+use Illuminate\Pagination\LengthAwarePaginator;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+
+class AbstractBaseService
+{
+    use CacheTrait;
+    /**
+     * BaseModel
+     * @var BaseModel
+     */
+    protected $model;
+    /**
+     * 是否缓存数据
+     * @var bool
+     */
+    protected $cache = false;
+    /**
+     * 缓存空间
+     * @var string
+     */
+    protected $cacheBucket = '';
+
+    /**
+     * Service constructor.
+     * @param BaseModel $model
+     */
+    public function __construct(BaseModel $model)
+    {
+        $this->model = $model;
+    }
+
+    /**
+     * DB:select对象转数组
+     * @param $list
+     */
+    public function toArray(&$list) {
+        $res = [];
+        foreach ($list as &$v) {
+            $res[] = (array)$v;
+        }
+        return $res;
+    }
+
+    /**
+     * 获取url地址
+     *
+     * @return string
+     */
+    public function getBaseUrl()
+    {
+        return app()->make(Request::class)->root();
+    }
+
+    /**
+     * 缓存key
+     * @param $id
+     * @return string
+     */
+    protected function getCacheId($id)
+    {
+        return $this->cacheBucket . '-' . md5($id);
+    }
+
+    /**
+     * 获取绑定的model
+     * @return BaseModel
+     */
+    public function getModel(){
+        return $this->model;
+    }
+
+    /**
+     * 开启事务
+     */
+    public function beginTransaction()
+    {
+        $this->model->getConnection()->beginTransaction();
+    }
+
+    /**
+     * 提交事务
+     */
+    public function commit()
+    {
+        $this->model->getConnection()->commit();
+    }
+
+    /**
+     * 回滚事务
+     */
+    public function rollback()
+    {
+        $this->model->getConnection()->rollBack();
+    }
+
+    /**
+     * 保存数据
+     * @param $data
+     * @return BaseModel
+     */
+    public function save($data)
+    {
+        $this->model = $this->model->newInstance();
+        $data = $this->model->filter($data);
+        foreach ($data as $key => $item) {
+            $this->model->$key = $item;
+        }
+        $this->model->save();
+        return $this->model;
+    }
+
+    /**
+     * 批量保存数据
+     * @param $data
+     */
+    public function saveAll($data)
+    {
+        if (empty($data)) {
+            return;
+        }
+        //过滤字段数据
+        foreach ($data as &$item) {
+            $item = $this->model->filter($item);
+            //            self::save($item);
+        }
+        //更改为批量插入
+        if($this->model->timestamps && $this->model::CREATED_AT && $this->model::UPDATED_AT) {
+            $time = nowTime();
+            foreach ($data as &$item) {
+                $item[$this->model::CREATED_AT] = $item[$this->model::UPDATED_AT] = $time;
+                foreach ($item as $key => $itemValue) {
+                    if ($this->model->isJsonCastingField($key) && is_array($itemValue)) {
+                        $item[$key] = json_encode($itemValue);
+                    }
+                }
+            }
+        }
+        return $this->model->insert($data);
+        //批量插入
+
+        //        $this->model->newInstance()->getConnection()->table($this->model->getTable())->insert($data);
+    }
+
+    /**
+     * 根据主键判断保存或者更新
+     * @param $data
+     * @return BaseModel
+     */
+    public function saveOrUpdate($data)
+    {
+        $key = $this->model->getKeyName();
+        if (isset($data[$key]) && !empty($data[$key])) {
+            return $this->update($data[$key], $data);
+        } else {
+            return $this->save($data);
+        }
+    }
+
+    /**
+     * 详情
+     * @param $id
+     * @return BaseModel
+     */
+    public function show($id)
+    {
+        return $this->findOneById($id);
+    }
+
+    /**
+     * 更新数据
+     * @param int $id
+     * @param array $data 更新数据
+     * @return BaseModel
+     */
+    public function update($id, array $data)
+    {
+        if ($this->cache) {
+            Cache::forget($this->getCacheId($id));
+        }
+
+        $res = $this->model->newInstance()->whereKey($id)->update($data);
+        if ($res) {
+            $data['id'] = $id;
+            $this->model->options = [
+                'where' => ['id' => $id]
+            ];
+            $this->model->data = $data;
+            $this->model->fireModelEvent('updated', false);
+        }
+        return $res;
+    }
+
+    /**
+     * 根据多个条件更新数据
+     * @param array $criteria
+     * @param array $data
+     * @return bool
+     */
+    public function updateBy($criteria, array $data)
+    {
+        if ($this->cache) {
+            if (method_exists($this->model, 'runSoftDelete')) {
+                $list = $this->model->newInstance()->buildQuery($criteria)->withAll()->get();
+            } else {
+                $list = $this->model->newInstance()->buildQuery($criteria)->get();
+            }
+            foreach ($list as $item) {
+                Cache::forget($this->getCacheId($item[$this->model->getKeyName()]));
+            }
+        }
+        $data = $this->model->filter($data);
+        if (method_exists($this->model, 'runSoftDelete')) {
+            $res = $this->model->newInstance()->buildQuery($criteria)->withAll()->update($data);
+        } else {
+            $res = $this->model->newInstance()->buildQuery($criteria)->update($data);
+        }
+        if ($res) {
+            if(isset($criteria['id'])){
+                $data['id'] = $criteria['id'];
+            }
+            $this->model->options = ['where'=>$criteria];
+            $this->model->data = $data;
+            $this->model->fireModelEvent('updated', false);
+        }
+
+        return $res;
+    }
+
+    /**
+     * 列表查询
+     * @param $data
+     * @return Collection
+     */
+    public function index($data)
+    {
+        return $this->model->whereRaw($this->getCondition($data))->paginate($this->getPageSize($data));
+    }
+
+    /**
+     * 获取查询条件
+     * @param $data
+     * @return string
+     */
+    protected function getCondition($data)
+    {
+        if (!is_array($data)) {
+            return '';
+        }
+        $data = $this->model->filter($data);
+        $condition = '1=1';
+        foreach ($data as $key => $item) {
+            $condition .= " and " . $key . "='" . $item . "'";
+        }
+        return $condition;
+    }
+
+    /**
+     * 获取分页行数
+     * @param $data
+     * @return int
+     */
+    protected function getPageSize($data)
+    {
+        return $data['page_size']??config('app.app_rows');
+    }
+
+    /**
+     * 删除数据
+     * @param $id int|array
+     * @return bool|null
+     */
+    public function delete($id)
+    {
+        if (is_array($id)) {
+            return $this->deleteBy([
+                $this->model->getKeyName() => [
+                    ['in', $id]
+                ]
+            ]);
+        } else {
+            return $this->deleteBy([
+                $this->model->getKeyName() => $id
+            ]);
+        }
+    }
+
+    /**
+     * 根据条件删除数据
+     * @param $criteria
+     * @return bool|null
+     */
+    public function deleteBy($criteria)
+    {
+        if ($this->cache) {
+            if (method_exists($this->model, 'runSoftDelete')) {
+                $res = $this->model->newInstance()->buildQuery($criteria)->withAll()->get();
+            } else {
+                $res = $this->model->newInstance()->buildQuery($criteria)->get();
+            }
+            foreach ($res as $item) {
+                Cache::forget($this->getCacheId($item[$this->model->getKeyName()]));
+            }
+        }
+        if (method_exists($this->model, 'runSoftDelete')) {
+            return $this->model->newInstance()->buildQuery($criteria)->withAll()->delete();
+        } else {
+            return $this->model->newInstance()->buildQuery($criteria)->delete();
+        }
+    }
+
+    /**
+     * 根据条件恢复数据
+     * @param $criteria
+     * @return bool|null
+     */
+    public function restoreBy($criteria)
+    {
+        if ($this->cache) {
+            $res = $this->model->newInstance()->buildQuery($criteria)->get();
+            foreach ($res as $item) {
+                Cache::forget($this->getCacheId($item[$this->model->getKeyName()]));
+            }
+        }
+        return $this->model->newInstance()->buildQuery($criteria)->restore();
+    }
+
+    /**
+     * 启用
+     * @param $id
+     * @return bool|null
+     */
+    public function enabled($id)
+    {
+        if ($this->cache) {
+            Cache::forget($this->getCacheId($id));
+        }
+        $model = $this->model->newInstance();
+        return $model->whereKey($id)->restore();
+    }
+
+    /**
+     * 禁用
+     * @param $id
+     * @return mixed
+     */
+    public function disabled($id)
+    {
+        if ($this->cache) {
+            Cache::forget($this->getCacheId($id));
+        }
+        $model = $this->model->newInstance();
+        return $model->whereKey($id)->update(['status' => $model::STATUS_DISABLED]);
+    }
+
+    /**
+     * id获取详情
+     * @param $id
+     * @return BaseModel
+     */
+    public function findOneById($id, $fields = '*')
+    {
+        $model = $this->model->newInstance();
+        if ($this->cache && $fields == '*') {
+            $info = Cache::remember($this->getCacheId($id), config('cache.time'), function () use ($model, $id, $fields) {
+                if (method_exists($model, 'runSoftDelete')) {
+                    return $model->whereKey($id)->withAll()->select(DB::raw($fields))->first();
+                }else {
+                    return $model->whereKey($id)->select(DB::raw($fields))->first();
+                }
+            });
+        } else {
+            if (method_exists($model, 'runSoftDelete')) {
+                $info = $model->whereKey($id)->withAll()->selectRaw($fields)->first();
+            }else {
+                $info = $model->whereKey($id)->select(DB::raw($fields))->first();
+            }
+        }
+        return $info;
+    }
+
+    /**
+     * 查找数据
+     * @param array|string $criteria
+     * @return BaseModel
+     */
+    public function findOneBy($criteria, $fields = '*')
+    {
+        $model = $this->model->newInstance();
+        if (method_exists($model, 'runSoftDelete') && !isset($criteria['status'])) {
+            $model = $model->whereRaw('status <> 2');
+        }
+        return $model->buildQuery($criteria)->select(DB::raw($fields))->first();
+    }
+
+    /**
+     * 查找数据
+     * @param array|string $criteria
+     * @return Collection
+     */
+    public function findBy($criteria, $fields = '*')
+    {
+        $model = $this->model->newInstance();
+        if (method_exists($model, 'runSoftDelete') && !isset($criteria['status'])) {
+            $model = $model->whereRaw('status <> 2');
+        }
+        return $model->buildQuery($criteria)->select(DB::raw($fields))->get();
+    }
+
+    /**
+     * 查询符合条件的行数
+     * @param $criteria
+     * @return int
+     */
+    public function count($criteria)
+    {
+        $model = $this->model->newInstance();
+        if (method_exists($model, 'runSoftDelete') && !isset($criteria['status'])) {
+            $model = $model->whereRaw('status <> 2');
+        }
+        return $model->buildQuery($criteria)->count();
+    }
+
+    /**
+     * sql原生查询
+     * @param $sql
+     * @return array
+     */
+    public function query($sql)
+    {
+        $data = $this->model->getConnection()->select($sql);
+        return json_decode(json_encode($data), true);
+    }
+
+    /**
+     * 字段唯一性性验证
+     * 修改数据验证时请组装主键notIn条件语句,返回false时为存在重复
+     * @param $field
+     * @param $value
+     * @param $criteria
+     * @return bool
+     */
+    public function checkFieldUnique($field, $value, $criteria)
+    {
+        $collection = $this->model->newInstance()->buildQuery($criteria)->selectRaw($field)->get();
+        if (empty($collection->toArray())) {
+            return true;
+        }
+        $checkArray = array_column($collection->toArray(), $field);
+        if (in_array($value, $checkArray, true)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Increment a column's value by a given amount.
+     * @param $criteria
+     * @param $column
+     * @param int $amount
+     * @param array $extra
+     * @return int
+     */
+    public function incrementBy($criteria, $column, $amount = 1, array $extra = [])
+    {
+        return $this->model->newInstance()->buildQuery($criteria)->increment($column, $amount, $extra);
+    }
+
+    /**
+     * Decrement a column's value by a given amount.
+     * @param $criteria
+     * @param $column
+     * @param int $amount
+     * @param array $extra
+     * @return int
+     */
+    public function decrementBy($criteria, $column, $amount = 1, array $extra = [])
+    {
+        return $this->model->newInstance()->buildQuery($criteria)->decrement($column, $amount, $extra);
+    }
+
+    /**
+     * 获取某一字段值
+     * @param $field
+     * @param $criteria
+     * @return string|int|array
+     */
+    public function getFieldBy($field, $criteria)
+    {
+        $findOne = $this->findOneBy($criteria, $field);
+        $findOne = $findOne ? $findOne->toArray() : [];
+        return $findOne[$field]??'';
+    }
+
+    /**
+     * 根据id获取某一个字段值
+     * @param $field
+     * @param $id
+     * @return array|int|string
+     */
+    public function getFieldById($field, $id)
+    {
+        return $this->getFieldBy($field, [
+            $this->model->getKeyName() => $id
+        ]);
+    }
+
+    /**
+     * 获取模型的table
+     * @return string
+     */
+    public function getTable()
+    {
+        return $this->model->getTable();
+    }
+
+    /**
+     * 分页
+     * @param $items 数据结果集
+     * @param $total 总数量
+     * @param $perPage 每页数量
+     * @return LengthAwarePaginator
+     */
+    public function paginator($items, $total, $perPage = null)
+    {
+        $request = app()->make(Request::class);
+        $params = $request->all();
+        if (isset($params['page'])) unset($params['page']);
+        $path = $request->url();
+        if ($perPage === null) {
+            $perPage = $this->getPageSize($params);
+        }
+        $options = [
+            'path' => $path,
+            'query' => $params,
+        ];
+        $page = new LengthAwarePaginator($items, $total, $perPage, $currentPage = null, $options);
+        return $page;
+    }
+
+    /**
+     * 过滤数据库字段
+     * @param $data
+     * @return array
+     */
+    public function create($data)
+    {
+        return $this->model->filter($data);
+    }
+
+
+
+
+}

+ 211 - 0
app/Base/Services/ApiAuthUser.php

@@ -0,0 +1,211 @@
+<?php
+
+
+namespace App\Base\Services;
+
+
+use App\Api\Facades\ApiFacade;
+use App\User\Facades\UserFacade;
+use Illuminate\Support\Facades\Auth;
+
+
+
+trait ApiAuthUser
+{
+    /** @var null 用户系统后台任务动态授权 */
+    private $sysAuthUser = null;
+
+    public function setSysAuthUser($sysAuthUser)
+    {
+        $this->sysAuthUser = $sysAuthUser;
+    }
+
+    /**
+     * 获取登录用户ID
+     * @return int
+     */
+    public function getAuthUserId()
+    {
+        $user = $this->getAuthUser();
+        return $user['id'] ?? 0;
+    }
+
+    /**
+     * 获取登录用户当前主页身份
+     * @return int
+     */
+    public function getAuthCurrEnterpriseId()
+    {
+        $user = $this->getAuthUser();
+        return $user['curr_enterprise_id'] ?? 0;
+    }
+
+    /**
+     * 获取登录用户个人主页
+     * @return int
+     */
+    public function getAuthPersonalEnterpriseId()
+    {
+        $user = $this->getAuthUser();
+        return $user['enterprise_id'] ?? 0;
+    }
+
+    /**
+     * 获取token
+     * @return int
+     */
+    public function getAuthApiToken()
+    {
+        $user = $this->getAuthUser();
+        return $user['api_token']??'';
+    }
+
+    /**
+     * 获取token
+     * @return int
+     */
+    public function getAuthSysApiToken()
+    {
+        $user = $this->getAuthUser();
+        return $user['sys_api_token']??'';
+    }
+
+    /**
+     * 获取手机
+     * @return string
+     */
+    public function getAuthPhone()
+    {
+        $user = $this->getAuthUser();
+        return $user['phone']??'';
+    }
+    /**
+     * 获取国家编码
+     * @return string
+     */
+    public function getAuthCountryCode()
+    {
+        $user = $this->getAuthUser();
+        return $user['country_code']??'86';
+    }
+
+    /**
+     * 获取邮箱
+     * @return string
+     */
+    public function getAuthEmail()
+    {
+        $user = $this->getAuthUser();
+        return $user['email']??'';
+    }
+
+    /**
+     * 获取登录用户真实姓名
+     * @return int
+     */
+    public function getAuthRealName()
+    {
+        $user = $this->getAuthUser();
+        return $user['real_name'] ?? '';
+    }
+
+    /**
+     * 获取登录用实名认证id
+     * @return int
+     */
+    public function getAuthPersonalCertifiedId()
+    {
+        $user = $this->getAuthUser();
+        return $user['personal_certified_id'] ?? 0;
+    }
+
+    /**
+     * 获取登录用户企业认证id
+     * @return int
+     */
+    public function getAuthEnterpriseCertifiedId()
+    {
+        $user = $this->getAuthUser();
+        return $user['enterprise_certified_id'] ?? 0;
+    }
+
+    /**
+     * 获取子账号id列表
+     * @return string
+     */
+    public function getAuthEnterpriseIds()
+    {
+        $user = $this->getAuthUser();
+        return $user['enterprise_ids']??[];
+    }
+
+
+    /**
+     * 获取登录用户
+     * @return \Illuminate\Contracts\Auth\Authenticatable|null
+     */
+    public function getAuthUser()
+    {
+        // todo 用redis缓存来获取
+        $user = Auth::user();
+        if ($user == null) {
+            $user = $this->sysAuthUser ?: null;
+        }
+        return $user;
+    }
+
+    /**
+     * 获取登录设备语言
+     * */
+    public function getAuthDeviceLanguage(){
+        $user = $this->getAuthUser();
+        return empty($user['device_language'])?'zh-cn':$user['device_language'];
+    }
+
+
+    /**
+     * 获取是否长登录
+     * */
+    public function getAuthlongLogin(){
+        $user = $this->getAuthUser();
+        return empty($user['long_login'])?0:$user['long_login'];
+    }
+
+
+    /**
+     * 获取房间id
+     * */
+    public function getAuthRoomId(){
+        $user = $this->getAuthUser();
+        return empty($user['room_id'])?'':$user['room_id'];
+    }
+
+    /**
+     * 更新用户的
+     * */
+    public function updateAuthUserData($isDetail=0){
+        $user=Auth::user();
+        if(!empty($user)){
+            if($isDetail){
+                $newUser = UserFacade::getDetailUserInfoById($user['id']);
+            }else{
+                $newUser = UserFacade::getBaseUserInfoById($user['id']);
+            }
+            $newUser['device_language']=empty($user['device_language'])?'zh-cn':$user['device_language'];
+            $newUser['long_login']=empty($user['long_login'])?'0':$user['long_login'];
+            $newUser['sys_api_token']=empty($user['sys_api_token'])?'':$user['sys_api_token'];
+            $user =   ApiFacade::updateUserInfoCache($user['api_token'], $newUser);
+        }
+        return $user;
+    }
+
+    /**
+     * 获取token
+     * @return int
+     */
+    public function getAuthIsUpdateUser()
+    {
+        $user = $this->getAuthUser();
+        return empty($user['is_update_user'])?false:$user['is_update_user'];
+    }
+}

+ 17 - 0
app/Base/Services/ApiBaseService.php

@@ -0,0 +1,17 @@
+<?php
+
+
+namespace App\Base\Services;
+
+
+use App\Base\Models\BaseModel;
+use Illuminate\Http\Request;
+use Illuminate\Pagination\LengthAwarePaginator;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+
+class ApiBaseService extends AbstractBaseService
+{
+    use ApiAuthUser;
+}

+ 65 - 0
app/Base/Services/AuthUser.php

@@ -0,0 +1,65 @@
+<?php
+
+
+namespace App\Base\Services;
+
+
+use Illuminate\Support\Facades\Auth;
+use App\User\Facades\CompanyDepartmentRelationFacade;
+use App\Base\Facades\ApiFacade;
+
+
+trait AuthUser
+{
+
+    /**
+     * 获取登录用户ID
+     * @return int
+     */
+    public function getAuthAdminId()
+    {
+        $user = $this->getAuthUser();
+        return $user['id'] ?? 0;
+    }
+
+
+    /**
+     * 获取登录用户
+     * @return \Illuminate\Contracts\Auth\Authenticatable|null
+     */
+    public function getAuthUser()
+    {
+        return Auth::user();
+    }
+
+    /**
+     * 获取头部token
+     * @return mixed
+     */
+    public function getAuthToken()
+    {
+        $user = $this->getAuthUser();
+        return $user['token'];
+    }
+
+
+    /**
+     * 获取角色ID
+     * @return int
+     */
+    public function getAuthRoleId()
+    {
+        $user = $this->getAuthUser();
+        return $user['role_id'];
+    }
+
+    /**
+     * 是否为管理员
+     * @param $typeId
+     * @return bool
+     */
+    protected function isAdmin($roleId = 1)
+    {
+        return $roleId == $this->getAuthRoleId();
+    }
+}

+ 50 - 0
app/Base/Services/BaseService.php

@@ -0,0 +1,50 @@
+<?php
+
+
+namespace App\Base\Services;
+
+
+use App\Base\Exceptions\ApiException;
+use Illuminate\Support\Facades\Redis;
+
+class BaseService extends AbstractBaseService
+{
+    use AuthUser;
+
+    /**
+     * 获取并发锁
+     * @param $lockKey
+     * @param $expire
+     * @return bool
+     */
+    function getLock($lockKey, $expire = 3)
+    {
+        $redis = Redis::connection()->client();
+        if (!$redis->setnx($lockKey, 1)) {
+            return false;
+        }
+        $redis->expire($lockKey, $expire);
+        return true;
+    }
+
+    /**
+     * 释放锁
+     * @param $lockKey
+     * @return mixed
+     */
+    function releaseLock($lockKey)
+    {
+        $redis = Redis::connection()->client();
+        return $redis->del($lockKey);
+    }
+
+    /**
+     * 没有操作权限
+     * @throws ApiException
+     */
+    function throwNoPermission()
+    {
+        throw new ApiException('common.no_operator_permission', '您没有权限操作此功能');
+    }
+
+}

+ 53 - 0
app/Base/Services/CacheTrait.php

@@ -0,0 +1,53 @@
+<?php
+
+
+namespace App\Base\Services;
+
+use Predis\Client;
+
+trait CacheTrait
+{
+    protected function getCacheClient()
+    {
+        return new Client(config('database.redis.default'));
+    }
+
+    /**
+     * 缓存前缀
+     * @return string
+     */
+    protected function getPrefix()
+    {
+        return config('cache.prefix') ? (config('cache.prefix') . ':') : '';
+    }
+
+
+
+    /**
+     * 获取所有前缀为$key的所有key
+     * @param $key
+     * @return array
+     */
+    public function getCacheKeys($key)
+    {
+        return $this->getCacheClient()->keys($this->getPrefix() . $key . '*');
+    }
+
+    public function getCacheKeysAll($key)
+    {
+        return $this->getCacheClient()->keys('*' . $key . '*');
+    }
+
+    /**
+     * 模糊清除所有前缀的缓存
+     * @param $key
+     */
+    public function removeByKey($key)
+    {
+
+        $list = $this->getCacheKeysAll($key);
+        foreach ($list as $item) {
+            $this->getCacheClient()->del($item);
+        }
+    }
+}

File diff suppressed because it is too large
+ 1633 - 0
app/Base/helpers.php


+ 13 - 0
app/Basic/Facades/UrlsFacade.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Basic\Facades;
+
+use Illuminate\Support\Facades\Facade;
+
+class UrlsFacade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+}

+ 13 - 0
app/Basic/Models/UrlsModel.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Basic\Models;
+
+use App\Base\Models\BaseModel;
+use App\Base\Models\ApiSoftDeletes;
+
+class UrlsModel extends BaseModel
+{
+    use ApiSoftDeletes;
+    protected $table = 'urls';
+
+}

+ 79 - 0
app/Basic/Providers/BasicServiceProvider.php

@@ -0,0 +1,79 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: fangx
+ * Date: 2021/7/12
+ * Time: 11:52
+ */
+
+namespace App\Basic\Providers;
+
+
+use App\Base\Providers\AppServiceProvider;
+use App\Basic\Facades\DictActivityTypeFacade;
+use App\Basic\Facades\DictColumnFacade;
+use App\Basic\Facades\DictCompanyCategoryFacade;
+use App\Basic\Facades\DictCompanyFacade;
+use App\Basic\Facades\DictDepartmentFacade;
+use App\Basic\Facades\DictExhibitionFacade;
+use App\Basic\Facades\DictIconFacade;
+use App\Basic\Facades\DictIndustryExhibitionFacade;
+use App\Basic\Facades\DictIndustryFacade;
+use App\Basic\Facades\DictJobTitleFacade;
+use App\Basic\Facades\DictLanguageFacade;
+use App\Basic\Facades\DictLocationFacade;
+use App\Basic\Facades\DictPlurilingualFacade;
+use App\Basic\Facades\DictProductFacade;
+use App\Basic\Facades\DictRegionFacade;
+use App\Basic\Facades\DictRegionLocationReFacade;
+use App\Basic\Facades\DictVenueFacade;
+use App\Basic\Facades\SyncTagFacade;
+use App\Basic\Facades\UrlsFacade;
+use App\Basic\Facades\VersionInfoFacade;
+use App\Basic\Services\DictActivityTypeService;
+use App\Basic\Services\DictColumnService;
+use App\Basic\Services\DictCompanyCategoryService;
+use App\Basic\Services\DictCompanyService;
+use App\Basic\Services\DictDepartmentService;
+use App\Basic\Services\DictExhibitionService;
+use App\Basic\Services\DictIconService;
+use App\Basic\Services\DictIndustryExhibitionService;
+use App\Basic\Services\DictIndustryService;
+use App\Basic\Services\DictJobTitleService;
+use App\Basic\Services\DictLanguageService;
+use App\Basic\Services\DictLocationService;
+use App\Basic\Services\DictPlurilingualService;
+use App\Basic\Services\DictProductService;
+use App\Basic\Services\DictRegionLocationReService;
+use App\Basic\Services\DictRegionService;
+use App\Basic\Services\DictVenueService;
+use App\Basic\Services\SyncTagService;
+use App\Basic\Services\UrlsService;
+use App\Basic\Services\VersionInfoService;
+
+
+class BasicServiceProvider extends AppServiceProvider
+{
+    public function boot()
+    {
+        //sql打印 不提交
+        /*\DB::listen(function ($query) {
+            $sql = array_reduce($query->bindings, function($sql, $binding) {
+                return preg_replace('/\?/', is_numeric($binding) ? $binding : sprintf("'%s'", $binding), $sql, 1);
+            }, $query->sql);
+
+            \Log::info($sql);
+        });*/
+        parent::boot();
+    }
+
+    /**
+     * 注册绑定门面
+     */
+    public function register()
+    {
+        $this->app->bind(UrlsFacade::class, function () {
+            return app()->make(UrlsService::class);
+        });
+    }
+}

+ 15 - 0
app/Basic/Services/UrlsService.php

@@ -0,0 +1,15 @@
+<?php
+
+
+namespace App\Basic\Services;
+
+use App\Base\Services\BaseService;
+use App\Basic\Models\UrlsModel;
+
+class UrlsService extends BaseService
+{
+    public function __construct(UrlsModel $model)
+    {
+        $this->model = $model;
+    }
+}

+ 0 - 0
app/Console/Commands/.gitkeep


+ 59 - 0
app/Console/Commands/CacheCommand.php

@@ -0,0 +1,59 @@
+<?php
+
+
+namespace App\Console\Commands;
+
+use App\Base\Services\CacheTrait;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Cache;
+
+class CacheCommand extends Command
+{
+    use CacheTrait;
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'cache {name}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '清除缓存';
+
+
+
+    /**
+     * Create a new command instance.
+     *
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $name = $this->argument('name');
+        if ($name=='flush') {
+            Cache::flush();
+            echo 'cache flush success.';
+        } elseif (!empty($name)) {
+            if ($name == 'ws:fd') {
+                $withPrefix = false;
+            } else {
+                $withPrefix = true;
+            }
+            $this->removeByKey($name, $withPrefix);
+            echo 'cache keys:'.$name.' remove successful';
+        }
+    }
+}

+ 29 - 0
app/Console/Kernel.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Console;
+
+use App\Console\Commands\CacheCommand;
+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 = [
+        CacheCommand::class,
+    ];
+
+    /**
+     * Define the application's command schedule.
+     *
+     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
+     * @return void
+     */
+    protected function schedule(Schedule $schedule)
+    {
+    }
+}

+ 26 - 0
app/Console/README.MD

@@ -0,0 +1,26 @@
+#命令使用说明
+##创建模块
+    php artisan module moduleName
+moduleName:模块名
+
+##创建模型文件
+    php artisan make:tpl moduleName ActionName
+moduleName:模块名
+
+actionName:文件名称
+
+例:php artisan make:tpl User CompanyUser
+
+以上命令会再User目录下的Facades、Services、Models新建CompanyUser对应的类,已经存在的文件不会再创建,但provider需要手动处理
+
+##生成API文档
+    php artisan make:doc
+##清除缓存
+    php artisan cache key
+key:key=flush 时清除所有缓存 其他的是清除带有key字符的缓存
+
+## 成客户端 模块
+    php artisan make:api {ActionName}
+    
+## 生成文章的pageinfo记录
+    php artisan make:setpageinfo {module}

+ 21 - 0
app/Console/Template/ApiController.tpl

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\{module}\Controllers;
+
+use App\{module}\Services\Api{action}Service;
+use App\Base\Controllers\ApiBaseController;
+use Illuminate\Http\Request;
+
+class Api{action}Controller extends ApiBaseController
+{
+    private $service;
+
+    /**
+     * Api{action}Controller constructor.
+     * @param Api{action}Service $service
+     */
+    public function __construct(Api{action}Service $service)
+    {
+        $this->service = $service;
+    }
+}

+ 13 - 0
app/Console/Template/ApiFacade.tpl

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\{module}\Facades;
+
+use Illuminate\Support\Facades\Facade;
+
+class Api{action}Facade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+}

+ 21 - 0
app/Console/Template/ApiService.tpl

@@ -0,0 +1,21 @@
+<?php
+
+
+namespace App\{module}\Services;
+use App\Base\Services\ApiBaseService;
+use App\Base\Models\BaseModel;
+
+class Api{action}Service extends ApiBaseService
+{
+
+    /**
+     * Api{action}Service constructor.
+     * @param BaseModel $model
+     */
+    public function __construct(BaseModel $model)
+    {
+        $this->model = $model;
+    }
+
+
+}

+ 89 - 0
app/Console/Template/Controller.tpl

@@ -0,0 +1,89 @@
+<?php
+
+namespace App\{module}\Controllers;
+
+use App\{module}\Services\{action}Service;
+use App\Base\Controllers\Controller;
+use Illuminate\Http\Request;
+
+class {action}Controller extends Controller
+{
+    private $service;
+
+    /**
+     * {action}Controller constructor.
+     * @param {action}Service $service
+     */
+    public function __construct({action}Service $service)
+    {
+        $this->service = $service;
+    }
+
+    /**
+     * @param string name 主页名称
+     * @param int page_size 每页显示数量
+     * @param int page 当前页
+     * @return array
+     * @throws \App\Base\Exceptions\ApiException
+     * @throws
+     * @api get (接口路径) (接口名称)
+     * @group (接口分组)
+     * @successExample
+     */
+    public function lists(Request $request)
+    {
+        return $this->service->lists($request);
+    }
+
+
+    /**
+     * @param (参数类型) (参数字段) (描述)
+     * @return mixed
+     * @throws \App\Base\Exceptions\ApiException
+     * @api post (接口路径) (接口名称)
+     * @group (接口分组)
+     * @successExample
+     * {"ret":0,"msg":"success","data":1}
+     */
+    public function saveInfo(Request $request){
+        return $this->service->saveInfo($request);
+    }
+
+    /**
+     * @param (参数类型) (参数字段) (描述)
+     * @return bool
+     * @throws \App\Base\Exceptions\ApiException
+     * @api post (接口路径) (接口名称)
+     * @group (接口分组)
+     * @successExample
+     * {"ret":0,"msg":"success","data":1}
+     */
+    public function changeStatus(Request $request){
+        return $this->service->changeStatus($request);
+    }
+
+    /**
+     * @param int id required
+     * @return \App\Base\Models\BaseModel|array|void
+     * @api post (接口路径) (接口名称)
+     * @group (接口分组)
+     * @successExample
+     */
+    public function detail(Request $request){
+        parent::detail($request);
+        return $this->service->detail($request);
+    }
+
+    /**
+     * @param array ids ids required
+     * @return int
+     * @api delete (接口路径) (接口名称)
+     * @group (接口分组)
+     * @successExample
+     * {"ret":0,"msg":"success.","data":1}
+     */
+    public function deleteInfo(Request $request){
+        parent::deleteInfo($request);
+        return $this->service->deleteInfo($request);
+    }
+}

+ 13 - 0
app/Console/Template/Facade.tpl

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\{module}\Facades;
+
+use Illuminate\Support\Facades\Facade;
+
+class {action}Facade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+}

+ 12 - 0
app/Console/Template/Model.tpl

@@ -0,0 +1,12 @@
+<?php
+
+namespace App\{module}\Models;
+
+use App\Base\Models\BaseModel;
+use App\Base\Models\ApiSoftDeletes;
+
+class {action}Model extends BaseModel
+{
+    use ApiSoftDeletes;
+    protected $table = '{table}';
+}

+ 123 - 0
app/Console/Template/Service.tpl

@@ -0,0 +1,123 @@
+<?php
+
+
+namespace App\{module}\Services;
+
+use App\Base\Exceptions\ApiException;
+use App\Base\Services\BaseService;
+use App\{module}\Models\{action}Model;
+use App\Log\Facades\LogAdminOperationFacade;
+
+class {action}Service extends BaseService
+{
+
+    /**
+     * {action}Service constructor.
+     * @param {action}Model $model
+     */
+    public function __construct({action}Model $model)
+    {
+        $this->model = $model;
+    }
+
+    public function lists($request){
+        $params = $request->all();
+        $limit = $this->getPageSize($params);
+        $page = $params['page'] ?? 1;
+
+        $map = [];
+        $OrWhere = [];
+        $status = $params['status'] ?? '';
+        if($status == '') {
+            $map['a.status'] = [['in', [0, 1]]];
+        }else{
+            $map['a.status'] = $status;
+        }
+
+        $model = $this->model->newInstance()->alias('a')
+            ->where(function($q)use($OrWhere){
+                foreach ($OrWhere as $or){
+                    $q->OrWhere($or[0],$or[1],$or[2]);
+                }
+            })
+            ->buildQuery($map);
+        $counts = $model->count();
+        $list = $model->forPage($page, $limit)
+        ->orderBy('id', 'desc')
+        ->get()->toArray();
+
+        $data = $this->paginator($list, $counts);
+        return $data;
+    }
+
+    public function saveInfo($request){
+        $params = $request->all();
+        $id = $params['id'];
+        //无ID则为新增
+        $params['update_id'] = $this->getAuthAdminId();
+
+        $log = [];
+        $log['admin_id'] = $params['update_id'];
+        $log['ip'] = getClientIp();
+        $log['url'] = $request->getRequestUri();
+        if($id){
+            $this->updateBy(['id' => $id],$params);
+            $log['operation'] = '更新数据:' . json_encode($params, JSON_UNESCAPED_UNICODE);
+        }else{
+            $params['create_id'] = $params['update_id'];
+            $id = $this->save($params)->id;
+            $log['operation'] = '新增数据:id => '.$id.' =>'.json_encode($params, JSON_UNESCAPED_UNICODE);
+        }
+        LogAdminOperationFacade::addOperationLog($log);
+        return $id;
+    }
+
+    public function changeStatus($request){
+        $params = $request->all();
+        $model = $this->findOneById($params['id']);
+        if(!$model){
+            throw new ApiException('common.no_records', '没有找到相关的记录');
+        }
+        $params['update_id'] = $this->getAuthAdminId();
+        $ret = $this->updateBy(['id' => $params['id']],['status' => $params['status'],'update_id' => $params['update_id']]);
+        $log = [];
+        $log['admin_id'] = $params['update_id'];
+        $log['ip'] = getClientIp();
+        $log['url'] = $request->getRequestUri();
+        $log['operation'] = '更新状态:' . json_encode($params, JSON_UNESCAPED_UNICODE);
+        LogAdminOperationFacade::addOperationLog($log);
+        return $ret;
+    }
+
+    public function deleteInfo($request){
+        $ids = $request->input('ids', 0);
+        if (!is_array($ids)) {
+            $ids = explode(',', $ids);
+        }
+        $map = ['id' => [['in', $ids]]];
+        $this->deleteBy($map, ['update_id' => $this->getAuthAdminId()]);
+        $log = [];
+        $log['admin_id'] = $this->getAuthAdminId();
+        $log['ip'] = getClientIp();
+        $log['url'] = $request->getRequestUri();
+        $log['operation'] = '删除数据:ids => ' . implode(',',$ids);
+        LogAdminOperationFacade::addOperationLog($log);
+        return 1;
+    }
+
+    public function detail($request)
+    {
+        $id = $request->input('id', 0);
+        $info = $this->findOneById($id);
+        if ($info) {
+            $info = $info->toArray();
+        }
+        $log = [];
+        $log['admin_id'] = $this->getAuthAdminId();
+        $log['ip'] = getClientIp();
+        $log['url'] = $request->getRequestUri();
+        $log['operation'] = '查看详情:id => ' . $id;
+        LogAdminOperationFacade::addOperationLog($log);
+        return $info;
+    }
+}

+ 23 - 0
app/Console/Template/ServiceProvider.tpl

@@ -0,0 +1,23 @@
+<?php
+
+
+namespace App\{module}\Providers;
+
+
+use App\Base\Providers\AppServiceProvider;
+use App\{module}\Services\{action}Service;
+use App\{module}\Models\{action}Model;
+use App\{module}\Facades\{action}Facade;
+
+class {action}ServiceProvider extends AppServiceProvider
+{
+    public function register()
+    {
+        $this->app->bind({action}Service::class,function(){
+            return new {action}Service(new {action}Model);
+        });
+        $this->app->bind({action}Facade::class,function(){
+            return app()->make({action}Service::class);
+        });
+    }
+}

+ 31 - 0
app/Crontab/Controllers/TaskInfoController.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Crontab\Controllers;
+
+use App\Crontab\Services\TaskInfoService;
+use App\Base\Controllers\Controller;
+use Illuminate\Http\Request;
+
+class TaskInfoController extends Controller
+{
+    private $service;
+
+    /**
+     * TaskInfoController constructor.
+     * @param TaskInfoService $service
+     */
+    public function __construct(TaskInfoService $service)
+    {
+        $this->service = $service;
+    }
+
+    /**
+     * @param Request $request
+     * @return int
+     */
+    public function task(Request $request)
+    {
+        return $this->service->execute($request->all());
+    }
+
+}

+ 22 - 0
app/Crontab/Providers/TaskServiceProvider.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Crontab\Providers;
+
+use App\Base\Providers\AppServiceProvider;
+class TaskServiceProvider extends AppServiceProvider
+{
+    public function boot()
+    {
+        parent::boot();
+    }
+
+    /**
+     * 注册绑定门面
+     */
+    public function register()
+    {
+
+    }
+
+
+}

+ 0 - 0
app/Crontab/README.md


+ 50 - 0
app/Crontab/Services/TaskInfoService.php

@@ -0,0 +1,50 @@
+<?php
+
+
+namespace App\Crontab\Services;
+
+class TaskInfoService
+{
+
+    /**
+     * TaskInfoService constructor.
+     */
+    public function __construct()
+    {
+
+    }
+    /**
+     * 执行任务
+     * todo 请维护README.md文件!!!
+     * @param $params //module:执行代码所在的模块,service:执行代码所在的业务文件,method:执行代码所在的业务方法,params:执行代码的业务方法所携带的参数
+     * @return int|void
+     */
+    public function execute($params)
+    {
+        try {
+            $module = $params['module'] ?? '';
+            $service = $params['service'] ?? '';
+            $method = $params['method'] ?? '';
+            $params = $params['params'] ?? [];
+
+            if($module && $method){
+                $module = ucfirst(convertUnderline($module));
+                $method = convertUnderline($method);
+                $service = ucfirst(convertUnderline($service));
+                $reflection = new \ReflectionClass('App\\'.$module.'\\Services\\'.$service.'Service');
+                $service = $reflection->newInstanceWithoutConstructor();
+                if($reflection->hasMethod($method)){
+                    if(!empty($params)){
+                        return $service->$method(...$params);
+                    } else {
+                        return $service->$method();
+                    }
+                }
+                return 0;
+            }
+        } catch (\Exception $e){
+            //print_r($e->getMessage());
+        }
+
+    }
+}

+ 11 - 0
app/Crontab/routes.php

@@ -0,0 +1,11 @@
+<?php
+$app = app()->router;
+$app->group([
+    'namespace' => 'App\Crontab\Controllers',
+    'prefix' => 'crontab',
+    'middleware' => [],
+], function () use ($app) {
+       //   定时任务请求地址
+       //  /crontab/task/run?module=module&service=service&method=method&params=params
+    $app->get('/task/run', 'TaskInfoController@task');
+});

+ 134 - 0
app/Doc/Controllers/CountController.php

@@ -0,0 +1,134 @@
+<?php
+/**
+ * User: bluey
+ * Date: 2019/1/10
+ * Time: 17:47
+ */
+
+namespace App\Doc\Controllers;
+
+use App\Base\Controllers\Controller;
+use function GuzzleHttp\Psr7\str;
+
+class CountController extends Controller
+{
+    /**
+     * 获取文件列表
+     * @param $dir
+     * @return array
+     */
+    protected function file_list($dir)
+    {
+        $arr = [];
+        $dir_handle = opendir($dir);
+        if ($dir_handle) {
+            while (($file = readdir($dir_handle)) !== false) {
+                if ($file === '.' || $file === '..') {
+                    continue;
+                }
+                $tmp = realpath($dir . '/' . $file);
+                if (is_dir($tmp)) {
+                    if (in_array($file, ['Doc', 'Facades', 'Traits', 'Api', 'Providers', 'Base', 'Console', 'Jobs'])) {
+                        continue;
+                    }
+
+                    $retArr = $this->file_list($tmp);
+                    if (!empty($retArr)) {
+                        $arr = array_merge($arr, $retArr);
+                    }
+                } else {
+                    if (in_array($file, ['routes.php', 'README.MD', 'WebSocketHandler.php'])) {
+                        continue;
+                    }
+
+                    $arr[] = $tmp;
+                }
+            }
+            closedir($dir_handle);
+        }
+        return $arr;
+    }
+
+    /**
+     * 解析PHP文件
+     * @param $file - 文件绝对路径
+     * @return string
+     */
+    protected function parseDoc($file)
+    {
+        $doc = '';
+        $content = file_get_contents($file);
+        $tokens = token_get_all($content);
+        $total = count($tokens);
+        for ($i = 0; $i < $total; $i++) {
+            if (is_string($tokens[$i])) {
+                $doc .= $tokens[$i];
+            } else {
+                if ($tokens[$i][0] != T_COMMENT && $tokens[$i][0] != T_DOC_COMMENT) {
+                    $doc .= $tokens[$i][1];
+                }
+            }
+        }
+
+        return str_replace("\r\n\r\n", "\r\n", $doc);
+    }
+
+    /**
+     * 统计文件列表
+     */
+    public function doc()
+    {
+        set_time_limit(0);
+        $zhList = [];
+        $dir = str_replace(lcfirst(__CLASS__) . ".php", "app\\", __FILE__);
+        $fileList = $this->file_list($dir);
+        $curFile = '';
+        foreach ($fileList as $file) {
+            $path = str_replace($dir, "", $file);
+            if ($curFile != $file) {
+                $zhList[] = '## ' . $path;
+            }
+
+            $content = $this->parseDoc($file);
+            // 逐行解析数据
+            $lines = explode("\r\n", $content);
+            if (1 == count($lines)) {
+                $lines = explode("\n", $content);
+            }
+
+            $zhList[] = '~~~';
+            $bFind = false;
+            foreach ($lines as $line) {
+                $newline = trim($line);
+                if ($newline == "") {
+                    continue;
+                }
+
+                if (strlen($newline) != mb_strlen($newline)) {
+                    if (false !== strpos($newline, 'ApiException') ||
+                        false !== strpos($newline, 'transL')) {
+                        continue;
+                    }
+
+                    $bFind = true;
+                    $zhList[] = $newline . "\n";
+                }
+            }
+
+            if ($bFind) {
+                $zhList[] = '~~~';
+            } else {
+                array_pop($zhList);
+                array_pop($zhList);
+            }
+        }
+
+        header("Content-type:application/octet-stream");
+        header("Accept-Ranges:bytes");
+        header("Content-Disposition:attachment;filename=多语言关联文件.md");
+        header("Expires: 0");
+        header("Cache-Control:must-revalidate,post-check=0,pre-check=0");
+        header("Pragma:public");
+        echo implode("\n", $zhList);
+    }
+}

+ 45 - 0
app/Doc/Controllers/DocController.php

@@ -0,0 +1,45 @@
+<?php
+
+
+namespace App\Doc\Controllers;
+
+use App\Base\Controllers\Controller;
+use App\Doc\Services\DocService;
+use Illuminate\Http\Request;
+
+class DocController extends Controller
+{
+    /**
+     * @var DocService
+     */
+    private $service;
+
+    /**
+     * DocController constructor.
+     * @param DocService $service
+     */
+    public function __construct(DocService $service)
+    {
+        $this->service = $service;
+    }
+
+    /**
+     * 获取文档分组
+     * @param Request $request
+     * @return string
+     */
+    public function group(Request $request)
+    {
+        return $this->service->group();
+    }
+
+    /**
+     * 文档详情
+     * @param Request $request
+     * @return mixed
+     */
+    public function detail(Request $request)
+    {
+        return $this->service->detail($request->all());
+    }
+}

+ 17 - 0
app/Doc/Providers/DocServiceProvider.php

@@ -0,0 +1,17 @@
+<?php
+
+
+namespace App\Doc\Providers;
+
+use App\Base\Providers\AppServiceProvider;
+use App\Doc\Services\DocService;
+
+class DocServiceProvider extends AppServiceProvider
+{
+    public function register()
+    {
+        $this->app->bind(DocService::class, function () {
+            return new DocService();
+        });
+    }
+}

+ 185 - 0
app/Doc/Services/AnnotationService.php

@@ -0,0 +1,185 @@
+<?php
+
+
+namespace App\Doc\Services;
+
+
+use Illuminate\Support\Facades\Log;
+
+class AnnotationService
+{
+    private $objRe;
+    private $class;
+    private $data;
+
+    public function __construct($class)
+    {
+        $className = '';
+        if (is_object($class)) {
+            $className = get_class($class);
+        } else if (is_string($class)) {
+            $className = $class;
+        } else {
+            exit("class param error!");
+        }
+        $this->objRe = new \ReflectionClass($className);//此类的方法被原封不动的继承 可以直接调用
+        $this->class = $className;
+    }
+
+    /**
+     * 获取所有的注释文档
+     * @return array
+     */
+    public function getAllDocComment()
+    {
+        $methods = $this->objRe->getMethods();
+        $this->data = [];
+        foreach ($methods as $item) {
+            if ($item->isConstructor() || !$item->isPublic() || $item->class != $this->class) {
+                continue;
+            }
+            //获取方法注释
+            $doc = $item->getDocComment();
+            $params = $this->getDocuments($doc);
+            if (isset($params['api'])) {
+                if (!empty($params) && !isset($params['group'])) {
+                    continue;
+                }
+                $this->formatDocument($params);
+                $group = $params['group']['group'] . ($params['group']['child'] ? '-' . $params['group']['child'] : '');
+                $data[$group][] = $params;
+            }
+        }
+        return $this->data;
+    }
+
+    /**
+     * 解析注释
+     * @param $doc
+     * @return array
+     */
+    private function getDocuments($doc)
+    {
+        $result = [];
+        $list = explode('* @', $doc);
+        foreach ($list as $item) {
+            $item = str_replace('*/', '', $item);
+            $item = str_replace('* ', '', $item);
+            $item = trim($item);
+            $line = explode(' ', $item);
+            $defined = $line[0];
+            $defined = str_replace(array("\r\n", "\r", "\n"), "", $defined);
+
+            switch ($defined) {
+                case 'api':
+                    $result[$defined] = [
+                        'key' => $line[0] ?? '',
+                        'method' => isset($line[1]) && in_array($line[1], ['get', 'post', 'put', 'delete', 'options', 'patch']) ? $line[1] : 'get',
+                        'route' => $line[2] ?? '',
+                        'comment' => $line[3] ?? ''
+                    ];
+                    break;
+                case 'group':
+                    $result[$defined] = [
+                        'key' => $line[0] ?? '',
+                        'group' => isset($line[1]) ? str_replace(array("\r\n", "\r", "\n"), "", $line[1]) : '',
+                        'child' => isset($line[2]) ? str_replace(array("\r\n", "\r", "\n"), "", $line[2]) : '',
+                    ];
+                    break;
+                case 'param':
+                    $result[$defined][] = [
+                        'desc' => $line[3] ?? '',
+                        'example' => $line[5] ?? '',
+                        'type' => (isset($line[1]) && $line[1] == 'file') ? 'file' : 'text',
+                        'name' => $line[2] ?? '',
+                        'required' => (isset($line[4]) && $line[4] == 'required') ? "1" : "0",
+                    ];
+                    break;
+                case 'header':
+                    $result[$defined][] = [
+                        'desc' => $line[3] ?? '',
+                        'example' => $line[5] ?? '',
+                        'name' => $line[3] ?? '',
+                        'required' => (isset($line[4]) && $line[4] == 'required') ? "1" : "0",
+                    ];
+                    break;
+                case 'success':
+                    $result[$defined][] = [
+                        'key' => $line[0] ?? '',
+                        'type' => $line[1] ?? '',
+                        'field' => $line[2] ?? '',
+                        'comment' => $line[3] ?? '',
+                        'required' => (isset($line[4]) && $line[4] == 'required') ? true : false,
+                    ];
+                    break;
+                case 'error':
+                    $result[$defined][] = [
+                        'key' => $line[0] ?? '',
+                        'code' => $line[1] ?? '',
+                        'msg' => $line[2] ?? ''
+                    ];
+                    break;
+                case 'paramExample':
+                case 'successExample':
+                    unset($line[0]);
+                    $result[$defined] = implode(' ', $line);
+                    break;
+                case 'desc':
+                    unset($line[0]);
+                    $result[$defined] = implode(' ', $line);
+                    break;
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * 更改文档格式
+     * @param $doc
+     */
+    private function formatDocument($doc)
+    {
+        if (!isset($this->data[$doc['group']['group'] . '-' . $doc['group']['child']])) {
+            $this->data[$doc['group']['group'] . '-' . $doc['group']['child']] = [
+                'name' => $doc['group']['group'] . '-' . $doc['group']['child'],
+                'desc' => $doc['group']['group'] . '-' . $doc['group']['child'],
+                'add_time' => time(),
+                'up_time' => time(),
+                'list' => []
+            ];
+        }
+        $this->data[$doc['group']['group'] . '-' . $doc['group']['child']]['list'][$doc['api']['comment']] = [
+            'method' => $doc['api']['method'],
+            'title' => $doc['api']['comment'] ? $doc['api']['comment'] : '-',
+            'path' => $doc['api']['route'] ? ($doc['api']['route'][0] == '/' ? $doc['api']['route'] : '/' . $doc['api']['route']) : '/undefine',
+            'res_body_type' => 'json',
+            'res_body' => $doc['successExample'] ?? '',
+            'req_body_is_json_schema' => false,
+            'req_params' => [],
+            'req_headers' => isset($doc['header']) && $doc['header'] ? $doc['header'] : [],
+            'type' => 'static',
+            'status' => 'done',
+            'desc' => $doc['desc'] ?? '',
+        ];
+        if ($doc['api']['method'] == 'post') {
+            $this->data[$doc['group']['group'] . '-' . $doc['group']['child']]['list'][$doc['api']['comment']]['req_body_type'] = 'form';
+            $this->data[$doc['group']['group'] . '-' . $doc['group']['child']]['list'][$doc['api']['comment']]['req_body_form'] = $doc['param'] ?? [];
+        } else {
+            $this->data[$doc['group']['group'] . '-' . $doc['group']['child']]['list'][$doc['api']['comment']]['req_query'] = $doc['param'] ?? [];
+        }
+        $this->data[$doc['group']['group'] . '-' . $doc['group']['child']]['list'][$doc['api']['comment']]['query_path'] = [
+            'path' => $doc['api']['route'],
+            'params' => []
+        ];
+//        $this->data[$doc['group']['group']][$doc['group']['child']][$doc['api']['comment']] = [
+//            'method'    =>  $doc['api']['method'],
+//            'url'       =>  $doc['api']['route'],
+//            'comment'   =>  $doc['api']['comment'],
+//            'param'     =>  $doc['param']??[],
+//            'success'   =>  $doc['success']??[],
+//            'header'    =>  $doc['header']??[],
+//            'successExample' => $doc['successExample']??''
+//        ];
+
+    }
+}

+ 189 - 0
app/Doc/Services/DocService.php

@@ -0,0 +1,189 @@
+<?php
+
+
+namespace App\Doc\Services;
+
+use Illuminate\Support\Facades\Log;
+
+class DocService
+{
+    private $data = [];
+    /**
+     * 获取文档分组菜单
+     * @return array
+     */
+    public function group()
+    {
+        return include base_path('config/doc.php');
+    }
+
+    /**
+     * 获取模块接口详情
+     * @param $data
+     * @return array
+     */
+    public function detail($data)
+    {
+        if (!isset($data['module'])) {
+            return [];
+        }
+        $module = $data['module'];
+        $content = file_get_contents(storage_path('doc/' . $module . '.json'));
+        $content = json_decode($content, true);
+        return $content;
+    }
+
+    /**
+     * 根据路径生成文档
+     * @param $path
+     */
+    public function make($path)
+    {
+        $this->delete();
+        $files = $this->getAllFiles($path);
+        foreach ($files as $file) {
+            if (strpos($file, '.php') == false) {
+                continue;
+            }
+            if (strpos($file, 'routes.php') != false) {
+                continue;
+            }
+            if (strpos($file, 'helpers.php') != false) {
+                continue;
+            }
+            if (strpos($file, 'Controller.php') == false) {
+                continue;
+            }
+            $class = $this->getClassByPath($file);
+            $annotation = new AnnotationService($class);
+            $document = $annotation->getAllDocComment();
+            if (empty($document)) {
+                continue;
+            }
+            if ($document) {
+                $this->addData($document);
+            }
+        }
+        $this->writeToFile(base_path('public').'/doc.json', $this->data);
+        echo 'success'.PHP_EOL;
+    }
+
+    /**
+     * 根据路径获取类名
+     * @param $path
+     * @return string
+     */
+    private function getClassByPath($path)
+    {
+        $basePath = base_path('app');
+        $path = str_replace($basePath, 'App', $path);
+        $path = str_replace("/", "\\", $path);
+        $path = str_replace('.php', '', $path);
+        return $path;
+    }
+
+    /**
+     * 删除存储目录下所有生成的文件
+     */
+    private function delete()
+    {
+        $files = $this->getAllFiles(storage_path('doc'));
+        foreach ($files as $file) {
+            @unlink($file);
+        }
+    }
+
+    /**
+     * 遍历获取目录下的所有文件
+     * @param $path
+     * @param $files
+     */
+    private function getFiles($path, &$files)
+    {
+        if (is_dir($path)) {
+            $dp = dir($path);
+            while ($file = $dp->read()) {
+                if ($file != "." && $file != "..") {
+                    // AudiencePool/Scene/Websocket/Workflow
+                    $this->getFiles($path . "/" . $file, $files);
+                }
+            }
+            $dp->close();
+        }
+
+        if (is_file($path)) {
+            $files[] = $path;
+        }
+    }
+
+    /**
+     * 递归获取目录下所有的文件 包含子目录
+     * @param $dir
+     * @return array
+     */
+    public function getAllFiles($dir)
+    {
+        $files = array();
+        $this->getFiles($dir, $files);
+        return $files;
+    }
+
+    /**
+     * 写入文档
+     * @param $document
+     */
+    private function write($document)
+    {
+        foreach ($document as $key=>$item) {
+            $file = storage_path('doc/'.$key.'.json');
+            if (!file_exists($file)) {
+                $this->writeToFile($file);
+            }
+            $content = json_decode(file_get_contents($file), true);
+            foreach ($item as $value) {
+                $content[] = $value;
+            }
+            $this->writeToFile($file, $content);
+        }
+    }
+
+    /**
+     * 写入内容到文件
+     * @param $file
+     * @param array $content
+     */
+    private function writeToFile($file, $content=[])
+    {
+        $content = array_values($content);
+        foreach ($content as &$item) {
+            if (isset($item['list'])) {
+                $item['list'] = array_values($item['list']);
+            }
+        }
+        if (strpos($file, '.json')) {
+            $content = json_encode($content, JSON_UNESCAPED_UNICODE);
+        } else {
+            $content = "export default ".json_encode($content, JSON_UNESCAPED_UNICODE);
+        }
+        $file = fopen($file, 'w');
+        fwrite($file, $content);
+        fclose($file);
+    }
+
+    /**
+     * 加入数据
+     * @param $document
+     */
+    private function addData($document)
+    {
+        foreach ($document as $index=>$item) {
+            if (isset($this->data[$index])) {
+                foreach ($item['list'] as $list) {
+                    $this->data[$index]['list'][] = $list;
+                }
+            } else {
+                $this->data[$index] = $document[$index];
+            }
+        }
+    }
+}

+ 12 - 0
app/Doc/routes.php

@@ -0,0 +1,12 @@
+<?php
+$app = app()->router;
+$app->group([
+    'namespace' => 'App\Doc\Controllers',
+], function ($router) {
+    //文档分组菜单
+    $router->get('/doc/group', 'DocController@group');
+    //文档接口详情
+    $router->get('/doc/detail', 'DocController@detail');
+    //文档多语言统计
+    $router->get('/count/file', 'CountController@doc');
+});

+ 26 - 0
app/Jobs/ExampleJob.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Jobs;
+
+class ExampleJob extends Job
+{
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return void
+     */
+    public function handle()
+    {
+        //
+    }
+}

+ 52 - 0
app/Jobs/IntegrateJob.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Jobs;
+
+class IntegrateJob extends Job
+{
+    protected $module = null;
+    protected $service = null;
+    protected $method = null;
+    protected $params = null;
+    /**
+     * Create a new job instance.
+     *
+     * @return void
+     */
+    public function __construct($params)
+    {
+        $this->module = $params['module'] ?? '';
+        $this->service = $params['service'] ?? '';
+        $this->method = $params['method'] ?? '';
+        $this->params = $params['params'] ?? [];
+
+    }
+
+    /**
+     * Execute the job.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        try {
+            if($this->module && $this->method){
+                $this->module = ucfirst(convertUnderline($this->module));
+                $this->method = convertUnderline($this->method);
+                $this->service = ucfirst(convertUnderline($this->service));
+                $reflection = new \ReflectionClass('App\\'.$this->module.'\\Services\\'.$this->service);
+                $service = $reflection->newInstanceWithoutConstructor();
+                if($reflection->hasMethod($this->method)){
+                    $method = $this->method;
+                    if(!empty($this->params)){
+                        return $service->$method(...$this->params);
+                    } else {
+                        return $service->$method();
+                    }
+                }
+            }
+        } catch (\Exception $e){
+            return $e->getMessage();
+        }
+    }
+}

+ 24 - 0
app/Jobs/Job.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Jobs;
+
+use Illuminate\Bus\Queueable;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Contracts\Queue\ShouldQueue;
+
+abstract class Job implements ShouldQueue
+{
+    /*
+    |--------------------------------------------------------------------------
+    | Queueable Jobs
+    |--------------------------------------------------------------------------
+    |
+    | This job base class provides a central location to place any logic that
+    | is shared across all of your jobs. The trait included with the class
+    | provides access to the "queueOn" and "delay" queue helper methods.
+    |
+    */
+
+    use InteractsWithQueue, Queueable, SerializesModels;
+}

+ 35 - 0
artisan

@@ -0,0 +1,35 @@
+#!/usr/bin/env php
+<?php
+
+use Symfony\Component\Console\Input\ArgvInput;
+use Symfony\Component\Console\Output\ConsoleOutput;
+
+/*
+|--------------------------------------------------------------------------
+| Create The Application
+|--------------------------------------------------------------------------
+|
+| First we need to get an application instance. This creates an instance
+| of the application / container and bootstraps the application so it
+| is ready to receive HTTP / Console requests from the environment.
+|
+*/
+
+$app = require __DIR__.'/bootstrap/app.php';
+
+/*
+|--------------------------------------------------------------------------
+| Run The Artisan Application
+|--------------------------------------------------------------------------
+|
+| When we run the console application, the current CLI command will be
+| executed in this console and the response sent back to a terminal
+| or another output device for the developers. Here goes nothing!
+|
+*/
+
+$kernel = $app->make(
+    'Illuminate\Contracts\Console\Kernel'
+);
+
+exit($kernel->handle(new ArgvInput, new ConsoleOutput));

+ 124 - 0
bootstrap/app.php

@@ -0,0 +1,124 @@
+<?php
+
+ini_set('date.timezone', 'Asia/Shanghai');
+require_once __DIR__ . '/../vendor/autoload.php';
+
+$envFile = '.env';
+(new Laravel\Lumen\Bootstrap\LoadEnvironmentVariables(
+    dirname(__DIR__),
+    $envFile
+))->bootstrap();
+//try {
+//    (new Dotenv\Dotenv(__DIR__ . '/../'))->load();
+//} catch (Dotenv\Exception\InvalidPathException $e) {
+//    //
+//}
+
+/*
+  |--------------------------------------------------------------------------
+  | 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();
+//注册mongodb  必须在withEloquent之前
+//$app->register(Jenssegers\Mongodb\MongodbServiceProvider::class);
+$app->withEloquent();
+
+/**
+ * 缓存配置
+ */
+$app->configure('app');
+$app->configure('auth');
+$app->configure('cache');
+
+/*
+  |--------------------------------------------------------------------------
+  | 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\Base\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->middleware([
+    //多语言
+    App\Base\Middleware\Localization::class,
+    //全局添加返回值格式
+    App\Base\Middleware\Response::class
+]);
+
+$app->routeMiddleware([
+    'auth' => App\Base\Middleware\Authenticate::class,
+    'permission' => App\Base\Middleware\Permission::class,
+    'api_permission' => App\Base\Middleware\ApiPermission::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.
+  |
+ */
+$getMillisecond = function () {
+    list($t1, $t2) = explode(' ', microtime());
+    return (float) sprintf('%.0f', (floatval($t1) + floatval($t2)) * 1000);
+};
+$app->register(App\Base\Providers\AppServiceProvider::class);
+$app->register(App\Base\Providers\EventServiceProvider::class);
+
+/*
+ * 配置日志文件为每日
+ */
+//
+//$app->configureMonologUsing(function(Monolog\Logger $monoLog) use ($app) {
+//    return $monoLog->pushHandler(
+//        new \Monolog\Handler\RotatingFileHandler($app->storagePath() . '/logs/lumen' . php_sapi_name() . '.log', 5)
+//    );
+//});
+
+/*
+  |--------------------------------------------------------------------------
+  | 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.
+  |
+ */
+return $app;

+ 53 - 0
composer.json

@@ -0,0 +1,53 @@
+{
+    "name": "laravel/lumen",
+    "description": "The Laravel Lumen Framework.",
+    "keywords": ["framework", "laravel", "lumen"],
+    "license": "MIT",
+    "type": "project",
+    "require": {
+        "php": "^7.3|^8.0",
+        "laravel/lumen-framework": "^8.1",
+        "vlucas/phpdotenv": "~5.2",
+        "predis/predis": "^1.1",
+        "guzzlehttp/guzzle": "^6.3",
+        "illuminate/redis": "^8.32",
+        "illuminate/cookie": "^8.40",
+        "zoujingli/ip2region": "^1.0"
+    },
+    "require-dev": {
+        "fakerphp/faker": "^1.9.1",
+        "mockery/mockery": "^1.3.1",
+        "phpunit/phpunit": "^9.3"
+    },
+    "autoload": {
+        "psr-4": {
+            "App\\": "app/"
+        },
+        "files":[
+            "app/Base/helpers.php"
+        ]
+    },
+    "autoload-dev": {
+        "classmap": [
+            "tests/",
+            "database/"
+        ]
+    },
+    "scripts": {
+        "post-root-package-install": [
+            "php -r \"copy('.env.example', '.env');\""
+        ]
+    },
+	"packagist": {
+        "type": "composer",
+        "url": "https://mirrors.aliyun.com/composer/"
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true,
+    "repositories": {
+        "packagist": {
+            "type": "composer",
+            "url": "https://mirrors.aliyun.com/composer/"
+        }
+    }
+}

+ 64 - 0
config/app.php

@@ -0,0 +1,64 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Encryption Key
+    |--------------------------------------------------------------------------
+    |
+    | This key is used by the Illuminate encrypter service and should be set
+    | to a random, 32 character string, otherwise these encrypted strings
+    | will not be safe. Please do this before deploying an application!
+    |
+    */
+
+    'key' => env('APP_KEY', 'SomeRandomString!!!'),
+
+    'cipher' => 'AES-256-CBC',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Application Locale Configuration
+    |--------------------------------------------------------------------------
+    |
+    | The application locale determines the default locale that will be used
+    | by the translation service provider. You are free to set this value
+    | to any of the locales which will be supported by the application.
+    |
+    */
+    'locale' => env('APP_LOCALE', 'zh-cn'),
+
+    'log' => 'daily',
+    /*
+    |--------------------------------------------------------------------------
+    | Application Fallback Locale
+    |--------------------------------------------------------------------------
+    |
+    | The fallback locale determines the locale to use when the current one
+    | is not available. You may change the value to correspond to any of
+    | the language folders that are provided through your application.
+    |
+    */
+    'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
+    /**
+     * 默认分页行数
+     */
+    'app_rows' => env('APP_ROWS', 10),
+    /**
+     * token缓存时间
+     */
+    'token_time' => env('APP_TOKEN_TIME', 20 * 60),
+
+    // 运行模式, 0-开发模式,1-正式模式
+    'app_mode' => env('APP_MODE', 1),
+
+    'attachment_host' => env('ATTACHMENT_HOST', 'https://api.matchexpo.com'),
+
+    //开启单点登录
+    'login_singleton' => env('SINGLE_LOGIN', false),
+
+    'js_version' => env('JS_VERSION', '0.01'),
+    // 短地址地址
+    'domain' => env('DOMAIN', 'http://s.juye.cn'),
+];

+ 91 - 0
config/auth.php

@@ -0,0 +1,91 @@
+<?php
+return [
+    'providers' => [
+        'users' => [
+            'driver' => 'eloquent',
+            #'model' => \App\User\Models\UserModel::class,
+            'table' => 'sys_users',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Defaults
+    |--------------------------------------------------------------------------
+    |
+    | This option controls the default authentication "guard" and password
+    | reset options for your application. You may change these defaults
+    | as required, but they're a perfect start for most applications.
+    |
+    */
+
+    'defaults' => [
+        'guard' => env('AUTH_GUARD', 'api'),
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Guards
+    |--------------------------------------------------------------------------
+    |
+    | Next, you may define every authentication guard for your application.
+    | Of course, a great default configuration has been defined for you
+    | here which uses session storage and the Eloquent user provider.
+    |
+    | All authentication drivers have a user provider. This defines how the
+    | users are actually retrieved out of your database or other storage
+    | mechanisms used by this application to persist your user's data.
+    |
+    | Supported: "token"
+    |
+    */
+
+    'guards' => [
+        'api' => ['driver' => 'api'],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | User Providers
+    |--------------------------------------------------------------------------
+    |
+    | All authentication drivers have a user provider. This defines how the
+    | users are actually retrieved out of your database or other storage
+    | mechanisms used by this application to persist your user's data.
+    |
+    | If you have multiple user tables or models you may configure multiple
+    | sources which represent each model / table. These sources may then
+    | be assigned to any extra authentication guards you have defined.
+    |
+    | Supported: "database", "eloquent"
+    |
+    */
+
+    'providers' => [
+        //
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Resetting Passwords
+    |--------------------------------------------------------------------------
+    |
+    | Here you may set the options for resetting passwords including the view
+    | that is your password reset e-mail. You may also set the name of the
+    | table that maintains all of the reset tokens for your application.
+    |
+    | You may specify multiple password reset configurations if you have more
+    | than one user table or model in the application and you want to have
+    | separate password reset settings based on the specific user types.
+    |
+    | The expire time is the number of minutes that the reset token should be
+    | considered valid. This security feature keeps tokens short-lived so
+    | they have less time to be guessed. You may change this as needed.
+    |
+    */
+
+    'passwords' => [
+        //
+    ],
+
+];

+ 105 - 0
config/cache.php

@@ -0,0 +1,105 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Cache Store
+    |--------------------------------------------------------------------------
+    |
+    | This option controls the default cache connection that gets used while
+    | using this caching library. This connection is used when another is
+    | not explicitly specified when executing a given caching function.
+    |
+    */
+
+    'default' => env('CACHE_DRIVER', 'redis'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cache Stores
+    |--------------------------------------------------------------------------
+    |
+    | Here you may define all of the cache "stores" for your application as
+    | well as their drivers. You may even define multiple stores for the
+    | same cache driver to group types of items stored in your caches.
+    |
+    */
+
+    'stores' => [
+
+        'apc' => [
+            'driver' => 'apc',
+        ],
+
+        'array' => [
+            'driver' => 'array',
+        ],
+
+        'database' => [
+            'driver' => 'database',
+            'table'  => env('CACHE_DATABASE_TABLE', 'cache'),
+            'connection' => env('CACHE_DATABASE_CONNECTION', null),
+        ],
+
+        'file' => [
+            'driver' => 'file',
+            'path'   => storage_path('framework/cache'),
+        ],
+
+        'memcached' => [
+            'driver'  => 'memcached',
+            'servers' => [
+                [
+                    'host' => env('MEMCACHED_HOST', '127.0.0.1'), 'port' => env('MEMCACHED_PORT', 11211), 'weight' => 100,
+                ],
+            ],
+        ],
+
+        'redis' => [
+            'driver' => 'redis',
+            'connection' => env('CACHE_REDIS_CONNECTION', 'default'),
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Cache Key Prefix
+    |--------------------------------------------------------------------------
+    |
+    | When utilizing a RAM based store such as APC or Memcached, there might
+    | be other applications utilizing the same cache. So, we'll specify a
+    | value to get prefixed to all our keys so we can avoid collisions.
+    |
+    */
+
+    'prefix' => env('CACHE_PREFIX', 'matchexpo:'),
+    /**
+     * 字段缓存时间
+     */
+    'columns' => env('CACHE_COLUMNS',3600),
+    /**
+     * 数据缓存时间
+     */
+    'time'  => env('CACHE_TIME',3600),
+    /**
+     * 较短的缓存时间
+     */
+    'short_time' => env('SHORT_CACHE_TIME', 300),
+    /**
+     * token缓存时间, 单位秒
+     */
+    'token' => env('TOKEN_TIME',3600),
+    'api_token' => env('TOKEN_TIME',3600),
+    'sms_time'=>env('SMS_TIME',1800), //30分钟
+    'email_time'=>env('EMAIL_TIME',3600), //一小时
+    'def_time'=>env('DEF_TIME',60*60*24), //一天
+    'api_token_expire_time'=>env('TOKEN_EXPIRE_TIME',60*60*2), //2*60*60小时(秒)token缓存时间
+    'api_token_long_expire_time'=>env('TOKEN_EXPIRE_TIME',60*60*24*30), //30*24小时(秒)token缓存时间
+    //表单缓存时间
+    'form_cache_time' => env('FORM_CACHE_TIME', 7*24*60),
+    //订单缓存时间
+    'order_expire_time' => env('ORDER_EXPIRE_TIME', 30),
+    'heart_beat_time' => env('TOKEN_EXPIRE_TIME', 60 * 30), //7*24小时(秒)token缓存时间
+];

+ 136 - 0
config/database.php

@@ -0,0 +1,136 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | PDO Fetch Style
+    |--------------------------------------------------------------------------
+    |
+    | By default, database results will be returned as instances of the PHP
+    | stdClass object; however, you may desire to retrieve records in an
+    | array format for simplicity. Here you can tweak the fetch style.
+    |
+    */
+
+    'fetch' => PDO::FETCH_CLASS,
+
+    /*
+    |--------------------------------------------------------------------------
+    | 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'),
+
+    /*
+    |--------------------------------------------------------------------------
+    | 是否开启事务
+    |--------------------------------------------------------------------------
+    | 是否主动开启,配置为是,在put、delete、post请求时会主动开启事务,开发人员不必在代码里
+    | 添加开启事务代码,开始事务、提交事务、回滚事务会再中间件自动处理,配置为否时,所有的请求
+    | 都不开启事务
+    |
+    */
+    'transaction' => env('DB_TRANSACTION', false),
+    /*
+    |--------------------------------------------------------------------------
+    | 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' => [
+
+        'testing' => [
+            'driver' => 'sqlite',
+            'database' => ':memory:',
+        ],
+
+        'mysql' => [
+            'driver' => 'mysql',
+            'host' => env('DB_HOST', '147.114.90.54'),
+            'port' => env('DB_PORT', 3306),
+            'database' => env('DB_DATABASE', 'matchexpo_new'),
+            'username' => env('DB_USERNAME', 'matchexpo'),
+            'password' => env('DB_PASSWORD', 'matchexpo0823!@'),
+            'charset' => env('DB_CHARSET', 'utf8mb4'),
+            'collation' => env('DB_COLLATION', 'utf8mb4_general_ci'),
+            'prefix' => env('DB_PREFIX', ''),
+            'timezone' => env('DB_TIMEZONE', '+08:00'),
+            'strict' => env('DB_STRICT_MODE', false),
+//            'options'   => [
+//                MYSQLI_OPT_INT_AND_FLOAT_NATIVE => true,
+//                PDO::ATTR_PERSISTENT => true,
+//            ]
+//            MYSQLI_OPT_INT_AND_FLOAT_NATIVE=>true
+        ],
+
+        'mongodb' => [
+            'driver' => 'mongodb',
+            'host' => env('MONGO_HOST', 'localhost'),
+            'port' => env('MONGO_PORT', 27017),
+            'database' => env('MONGO_DATABASE'),
+            'username' => env('MONGO_USERNAME'),
+            'password' => env('MONGO_PASSWORD'),
+            'options' => [
+                'database' => 'admin' // sets the authentication database required by mongo 3
+            ]
+        ],
+
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | 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' => [
+
+        'cluster' => env('REDIS_CLUSTER', false),
+
+        'default' => [
+            'host' => env('REDIS_HOST', '127.0.0.1'),
+            'port' => env('REDIS_PORT', 6379),
+            'database' => env('REDIS_DATABASE', 0),
+            'password' => env('REDIS_PASSWORD', null),
+            'read_write_timeout' => -1,
+        ],
+
+    ],
+    'max_idle_time' => env('DB_MAX_IDLE_TIME', 30),
+
+];

+ 25 - 0
public/.htaccess

@@ -0,0 +1,25 @@
+<IfModule mod_rewrite.c>
+    <IfModule mod_negotiation.c>
+        Options -MultiViews
+    </IfModule>
+
+    RewriteEngine On
+
+    RewriteRule ^static/(.*)$  /dist/static/$1  [NC,L]
+
+    RewriteRule ^/(cY95CwKkfB\.txt)$ $1 [QSA,PT,L]
+
+    # Redirect Trailing Slashes If Not A Folder...
+    RewriteCond %{REQUEST_FILENAME} !-d
+    RewriteRule ^(.*)/$ /$1 [L,R=301]
+
+    # Handle Front Controller...
+    RewriteCond %{REQUEST_FILENAME} !-d
+    RewriteCond %{REQUEST_FILENAME} !-f
+    RewriteRule ^ index.php [L]
+
+    # Handle Authorization Header
+    RewriteCond %{HTTP:Authorization} .
+    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+</IfModule>
+

BIN
public/assets/image/img_def_404.png


BIN
public/favicon.ico


+ 24 - 0
public/index.php

@@ -0,0 +1,24 @@
+<?php
+/*
+|--------------------------------------------------------------------------
+| Create The Application
+|--------------------------------------------------------------------------
+|
+| First we need to get an application instance. This creates an instance
+| of the application / container and bootstraps the application so it
+| is ready to receive HTTP / Console requests from the environment.
+|
+*/
+$app = require __DIR__.'/../bootstrap/app.php';
+/*
+|--------------------------------------------------------------------------
+| Run The Application
+|--------------------------------------------------------------------------
+|
+| Once we have the application, we can handle the incoming request
+| through the kernel, and send the associated response back to
+| the client's browser allowing them to enjoy the creative
+| and wonderful application we have prepared for them.
+|
+*/
+$app->run();

+ 2 - 0
public/robots.txt

@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /dist/

+ 101 - 0
resources/README.md

@@ -0,0 +1,101 @@
+## 目录结构
+
+```
+public.assets.css    	  html样式文件目录
+  ├─layouts               公共样式文件目录
+  ├─ ...                  更多目录
+public.assets.js     	  html脚本文件目录
+  ├─layouts               公共脚本文件目录
+  ├─ ...                  更多目录
+resources.lang            语言包文件目录
+resources.views           html视图文件目录
+  ├─components            组件视图目录
+  │  ├─index              首页组件(可更改)
+  │  │  ├─banner.blade.php广告位组件(可更改)
+  │  │  └─ ...            更多组件文件
+  │  ├─ ...               更多目录
+  ├─layouts               公共布局视图目录
+  │  ├─app.blade.php      共有视图
+  │  ├─page.blade.php     分页视图
+  |  ├─ ...               更多视图
+  ├─index                 首页视图目录(可定义)
+  │  ├─index.blade.php    首页视图
+  │  ├─ ...               更多视图
+  ├─news                  资讯视图目录(可定义)
+  │  ├─index.blade.php    资讯列表视图
+  │  ├─ ...               更多视图
+  ├─live                  直播视图目录(可定义)
+  │  ├─index.blade.php    直播列表视图
+  │  ├─ ...               更多视图
+  ├─expo                  展会视图目录(可定义)
+  │  ├─index.blade.php    展会列表视图
+  │  ├─ ...               更多视图
+  ├─homepage              主页视图目录(可定义)
+  │  ├─detail.blade.php   主页视图
+  ├─expert                专家视图目录(可定义)
+  │  ├─detail.blade.php   专家视图
+  ├─ ...                  更多视图目录(可定义)
+```
+
+## 开发规范
+
+#### 视图规范
+
+1. 对应模块的视图需放在对应模块目录下
+
+   ```
+   路由地址:/{module}-{mainpage}/{plink}.html
+   	  module(模块) ,如: index,news,live 等
+   	  mainpage(主页id)
+   	  plink(详情对应的生成的key,同主页同模块不可重复)
+   	  
+   如果请求地址为/index.html ,则视图文件路径:/resources/views/index/index.blade.php
+   如果请求地址为/news.html ,则视图文件路径:/resources/views/news/index.blade.php
+   如果请求地址为/news-2/pg8282.html, 则视图文件路径:/resources/views/news/detail.blade.php
+   ```
+
+2. 所有视图需继承/view/layouts/app.blade.php文件
+
+   ```
+   参考: /resources/view/index/index.blade.php
+   
+   @section('content')
+   	 当前视图主体内容
+   @endsection
+   ```
+
+4. js、css引用: 
+
+   ```html
+   {{-- 推送js文件到公共布局中 --}}
+   @push('scripts')
+       <script src="/assets/js/example.js" type="javascript"></script>
+   @endpush
+   {{-- 推送css文件到公共布局中 --}}
+   @push('css')
+       <link href="/assets/css/example.css" rel="stylesheet" type="text/css"/>
+   @endpush
+   ```
+
+   
+
+5. 其他写法: https://learnku.com/docs/laravel/8.x/blade/9377#918cb6
+6. 视图兼容多语言使用: {{ transL('common.param_format_error', '发生错误了') }} 来输出文本
+
+#### 组件规范
+
+1. 调用方式
+
+   ```html
+   <x-blog-list view-file="components.blog.list" :page="$page ?? 1" :page-size="$pageSize" industry-id="43" is-tourist="0"  />
+   <!--
+   1. 调用组件必须<x-组件名称 />
+   2. 传递参数时使用 烤串式 类型:page-size
+      变量应该通过以 : 字符作为前缀的变量来进行传递
+   3. 必传参数 view-file (组件调用的视图文件)
+   4. 固定参数 :page, :page-size, 如果不确定参数是否存在 使用 "$page ?? 1" 设置默认的值 否则报错
+   -->
+   ```
+
+2. 对应的组件名称,参数等都会已文档形式提供
+

+ 0 - 0
resources/views/.gitkeep


+ 65 - 0
resources/views/errors/404.blade.php

@@ -0,0 +1,65 @@
+<html lang="zh">
+<head>
+    <meta charset="UTF-8" />
+    <title>404 - 会邦人</title>
+    <link rel="shortcut icon" href="/favicon.ico" />
+    <style>
+        .undefined_contain {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            height: 94vh;
+            flex-wrap: wrap;
+            flex-direction: column;
+        }
+        .image {
+            height: 40vh;
+        }
+        .error_title {
+            font-size: 18px;
+            margin-top: 30px;
+            width: 100%;
+            font-family: '思源黑体旧字形 Regular';
+            font-weight: bold;
+            color: #53565c;
+            line-height: 28px;
+            text-align: center;
+        }
+        .error_desc {
+            font-size: 14px;
+            font-family: '思源黑体旧字形 Regular';
+            font-weight: 400;
+            color: #53565c;
+            line-height: 22px;
+        }
+        .error_btn {
+            cursor: pointer;
+            margin-top: 40px;
+            font-size: 12px;
+            padding: 2px 16px;
+            background: #ffffff;
+            border-radius: 41px;
+            border: 1px solid #3888ff;
+            font-family: '思源黑体旧字形 Regular';
+            font-weight: 400;
+            color: #3888ff;
+            line-height: 20px;
+            transition: 0.2s;
+            text-decoration: none;
+        }
+        .error_btn :hover {
+            background-color: #3888ff;
+            color: #ffffff;
+        }
+
+    </style>
+</head>
+<body>
+<div class="undefined_contain">
+    <image class="image" src="/assets/image/img_def_404.png" />
+    <div class="error_title">页面异常</div>
+    <div class="error_desc">抱歉!找不到相关的内容</div>
+    <a class="error_btn" href="/index">返回首页</a>
+</div>
+</body>
+</html>

+ 70 - 0
tests/ExampleTest.php

@@ -0,0 +1,70 @@
+<?php
+
+use Laravel\Lumen\Testing\DatabaseMigrations;
+use Laravel\Lumen\Testing\DatabaseTransactions;
+
+class ExampleTest extends TestCase
+{
+    /**
+     * A basic test example.
+     *
+     * @return void
+     */
+    public function testExample()
+    {
+        $this->get('/');
+        \Illuminate\Support\Facades\Log::info('1212434');
+//        $this->assertEquals(
+//            $this->app->version(), $this->response->getContent()
+//        );
+    }
+
+    /**
+     * 获取上个月的开始和结束
+     * @param int $ts 时间戳
+     * @return array 第一个元素为开始日期,第二个元素为结束日期
+     */
+    protected function lastMonth($ts) {
+        $ts = intval($ts);
+
+        $oneMonthAgo = mktime(0, 0, 0, date('n', $ts) - 1, 1, date('Y', $ts));
+        $year = date('Y', $oneMonthAgo);
+        $month = date('n', $oneMonthAgo);
+        return array(
+            date('Y-m-1', strtotime($year . "-{$month}-1")),
+            date('Y-m-t', strtotime($year . "-{$month}-1"))
+        );
+    }
+    /**
+     * 获取上n周的开始和结束,每周从周一开始,周日结束日期
+     * @param int $ts 时间戳
+     * @param int $n 你懂的(前多少周)
+     * @param string $format 默认为'%Y-%m-%d',比如"2012-12-18"
+     * @return array 第一个元素为开始日期,第二个元素为结束日期
+     */
+    protected function lastNWeek($ts, $n, $format = '%Y-%m-%d') {
+        $ts = intval($ts);
+        $n  = abs(intval($n));
+
+        // 周一到周日分别为1-7
+        $dayOfWeek = date('w', $ts);
+        if (0 == $dayOfWeek)
+        {
+            $dayOfWeek = 7;
+        }
+
+        $lastNMonday = 7 * $n + $dayOfWeek - 1;
+        $lastNSunday = 7 * ($n - 1) + $dayOfWeek;
+        return array(
+            strftime($format, strtotime("-{$lastNMonday} day", $ts)),
+            strftime($format, strtotime("-{$lastNSunday} day", $ts))
+        );
+    }
+
+    public function testLast(){
+        print_r($this->lastMonth(strtotime('2017-07-06')));
+        print_r($this->lastNWeek(strtotime('2017-07-06'),1));
+        echo date('Y-m-d', strtotime('this week'));
+        echo date('Y-m-d', (time() + (7 - (date('w') == 0 ? 7 : date('w'))) * 24 * 3600));
+    }
+}

+ 75 - 0
tests/TestCase.php

@@ -0,0 +1,75 @@
+<?php
+
+abstract class TestCase extends Laravel\Lumen\Testing\TestCase
+{
+    protected $token = '5da1beb37d511577b6fc3a9b67052344';
+    protected $username = 'youwl';
+    protected $password = 'Mm123654!';//'111111';
+    /**
+     * Creates the application.
+     *
+     * @return \Laravel\Lumen\Application
+     */
+    public function createApplication()
+    {
+        return require __DIR__.'/../bootstrap/app.php';
+    }
+
+    /**
+     * 测试模拟请求
+     * @param string $method
+     * @param string $url
+     * @param array ;
+     */
+    public function request(string $method,string $url,array $params=[], $content=null,$need_auth = 1){
+        $token = '';
+        if($need_auth) {
+            $token = !empty($this->token) ? $this->token : $this->getToken();
+            var_dump($token);
+        }
+        $headers = [
+            'token' => $token,
+            'X-Requested-With'=>'XMLHttpRequest',
+            'Content-Type' => 'application/x-www-form-urlencoded',
+//            'token' => '6ac7ad9ca379fc2d94e2df02eb457594'
+        ];
+        $this->refreshApplication();
+        $this->setUp();
+        $server = $this->transformHeadersToServerVars($headers);
+        $response = $this->call($method,$url,$params,[], [], $server, $content);
+        print_r('HTTP状态码:'.$response->getStatusCode().PHP_EOL);
+        print_r('返回值:'.PHP_EOL);
+        $data = json_decode($response->getContent(),true);
+        print_r($data);
+        $this->assertEquals(0,$data['ret']??-1);
+        echo PHP_EOL.PHP_EOL.json_encode($data,JSON_UNESCAPED_UNICODE);
+    }
+
+    protected function getToken(){
+//        return 'de5676e2dd0c2666d8b3f135b819d305';
+        $headers = [
+            'X-Requested-With'=>'XMLHttpRequest',
+        ];
+        $server = $this->transformHeadersToServerVars($headers);
+        $response = $this->call('post','/admin/admin-login',['user_name'=>$this->username,'password'=>$this->password],[],[],$server);
+        if($response->getStatusCode()!=200){
+            echo '登录失败';exit;
+        }
+
+        $data = json_decode($response->getContent(),true);
+        if(isset($data['ret']) && $data['ret']!=0){
+            print_r($data);
+            exit;
+        }
+        return $data['data']['token'];
+    }
+
+    /**
+     * 覆盖setUp方法 添加对sql查询监听
+     */
+    protected function setUp(): void
+    {
+        parent::setUp();
+        sqlDump();
+    }
+}