Ver Fonte

提交初始分支

yanj há 3 meses atrás
commit
47ba85a7b0
100 ficheiros alterados com 10693 adições e 0 exclusões
  1. 15 0
      .editorconfig
  2. 11 0
      .gitignore
  3. 6 0
      .styleci.yml
  4. 50 0
      README.md
  5. 78 0
      app/Ad/Controllers/AdController.php
  6. 13 0
      app/Ad/Facades/SysAdFacade.php
  7. 13 0
      app/Ad/Facades/SysAdPositionFacade.php
  8. 52 0
      app/Ad/Models/SysAdModel.php
  9. 38 0
      app/Ad/Models/SysAdPositionModel.php
  10. 25 0
      app/Ad/Providers/AdServiceProvider.php
  11. 273 0
      app/Ad/Services/SysAdService.php
  12. 26 0
      app/Ad/routes.php
  13. 72 0
      app/Common/Controllers/CommonController.php
  14. 21 0
      app/Common/Facades/ComEmailCodeFacade.php
  15. 21 0
      app/Common/Facades/ComSmsFacade.php
  16. 21 0
      app/Common/Facades/OssFacade.php
  17. 20 0
      app/Common/Facades/PinYinFacade.php
  18. 21 0
      app/Common/Facades/UploadFileFacade.php
  19. 28 0
      app/Common/Facades/UserCenterFacade.php
  20. 76 0
      app/Common/Library/AES.php
  21. 53 0
      app/Common/Library/DES.php
  22. 84 0
      app/Common/Library/Encryptor.php
  23. 177 0
      app/Common/Library/Random.php
  24. 30 0
      app/Common/Models/ComEmailCodeModel.php
  25. 25 0
      app/Common/Models/ComOssETagModel.php
  26. 30 0
      app/Common/Models/ComSmsModel.php
  27. 93 0
      app/Common/Models/ExportExcel.php
  28. 57 0
      app/Common/Models/ImportExcel.php
  29. 114 0
      app/Common/Providers/CommonProvider.php
  30. 199 0
      app/Common/Services/ComEmailCodeService.php
  31. 299 0
      app/Common/Services/ComSmsService.php
  32. 88 0
      app/Common/Services/OssService.php
  33. 88 0
      app/Common/Services/PinYinService.php
  34. 52 0
      app/Common/Services/ShortyService.php
  35. 584 0
      app/Common/Services/UploadFileService.php
  36. 942 0
      app/Common/Services/UserCenterService.php
  37. 199 0
      app/Common/Services/WechatService.php
  38. 1716 0
      app/Common/helpers.php
  39. 18 0
      app/Common/routes.php
  40. 0 0
      app/Console/Commands/.gitkeep
  41. 126 0
      app/Console/Commands/MakeApacheConfCommand.php
  42. 39 0
      app/Console/Commands/MakeBlogUvCommand.php
  43. 95 0
      app/Console/Commands/MakeNginxConfCommand.php
  44. 53 0
      app/Console/Kernel.php
  45. 10 0
      app/Events/Event.php
  46. 16 0
      app/Events/ExampleEvent.php
  47. 32 0
      app/ExcelData/Providers/ExcelDataProvider.php
  48. 65 0
      app/ExcelData/Services/FormRecordSumService.php
  49. 145 0
      app/ExcelData/Services/FormService.php
  50. 66 0
      app/ExcelData/Services/FormSheetsService.php
  51. 66 0
      app/ExcelData/Services/ImportHeadingExcel.php
  52. 131 0
      app/Exceptions/ApiException.php
  53. 129 0
      app/Exceptions/Handler.php
  54. 181 0
      app/Form/Controllers/FormController.php
  55. 133 0
      app/Form/Controllers/FormRecordController.php
  56. 13 0
      app/Form/Facades/FormContentFacade.php
  57. 13 0
      app/Form/Facades/FormInfoFacade.php
  58. 13 0
      app/Form/Facades/FormItemDetailFacade.php
  59. 13 0
      app/Form/Facades/FormItemFacade.php
  60. 13 0
      app/Form/Facades/FormProductDetailFacade.php
  61. 13 0
      app/Form/Facades/FormProductFacade.php
  62. 13 0
      app/Form/Facades/FormRecordFacade.php
  63. 15 0
      app/Form/Models/FormContentModel.php
  64. 26 0
      app/Form/Models/FormInfoModel.php
  65. 10 0
      app/Form/Models/FormItemDetailModel.php
  66. 10 0
      app/Form/Models/FormItemModel.php
  67. 13 0
      app/Form/Models/FormProductDetailModel.php
  68. 13 0
      app/Form/Models/FormProductModel.php
  69. 12 0
      app/Form/Models/FormRecordModel.php
  70. 87 0
      app/Form/Providers/FormServiceProvider.php
  71. 39 0
      app/Form/Services/FormContentService.php
  72. 340 0
      app/Form/Services/FormInfoService.php
  73. 144 0
      app/Form/Services/FormItemDetailService.php
  74. 188 0
      app/Form/Services/FormItemService.php
  75. 57 0
      app/Form/Services/FormProductDetailService.php
  76. 40 0
      app/Form/Services/FormProductService.php
  77. 813 0
      app/Form/Services/FormRecordService.php
  78. 58 0
      app/Form/routes.php
  79. 28 0
      app/Http/Controllers/BaseController.php
  80. 10 0
      app/Http/Controllers/Controller.php
  81. 18 0
      app/Http/Controllers/ExampleController.php
  82. 53 0
      app/Http/Middleware/AdminUserAuthenticate.php
  83. 39 0
      app/Http/Middleware/AuthCheck.php
  84. 48 0
      app/Http/Middleware/Authenticate.php
  85. 20 0
      app/Http/Middleware/ExampleMiddleware.php
  86. 44 0
      app/Http/Middleware/NormalUserAuthenticate.php
  87. 103 0
      app/Http/Middleware/Response.php
  88. 26 0
      app/Jobs/ExampleJob.php
  89. 24 0
      app/Jobs/Job.php
  90. 31 0
      app/Listeners/ExampleListener.php
  91. 97 0
      app/Models/ApiSoftDeletes.php
  92. 153 0
      app/Models/ApiSoftDeletingScope.php
  93. 275 0
      app/Models/BaseModel.php
  94. 292 0
      app/Models/BaseMongoModel.php
  95. 216 0
      app/Models/Criteria.php
  96. 50 0
      app/Models/User.php
  97. 18 0
      app/Providers/AppServiceProvider.php
  98. 113 0
      app/Providers/AppUserProvider.php
  99. 64 0
      app/Providers/AuthServiceProvider.php
  100. 0 0
      app/Providers/EventServiceProvider.php

+ 15 - 0
.editorconfig

@@ -0,0 +1,15 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.{yml,yaml}]
+indent_size = 2

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+/vendor
+/.idea
+Homestead.json
+Homestead.yaml
+.env
+.phpunit.result.cache
+.sass-cache
+.scssc
+/storage
+/storage/framework/cache
+/storage/framework/views

+ 6 - 0
.styleci.yml

@@ -0,0 +1,6 @@
+php:
+  preset: laravel
+  disabled:
+    - unused_use
+js: true
+css: true

+ 50 - 0
README.md

@@ -0,0 +1,50 @@
+# Lumen PHP Framework
+
+[![Build Status](https://travis-ci.org/laravel/lumen-framework.svg)](https://travis-ci.org/laravel/lumen-framework)
+[![Total Downloads](https://poser.pugx.org/laravel/lumen-framework/d/total.svg)](https://packagist.org/packages/laravel/lumen-framework)
+[![Latest Stable Version](https://poser.pugx.org/laravel/lumen-framework/v/stable.svg)](https://packagist.org/packages/laravel/lumen-framework)
+[![License](https://poser.pugx.org/laravel/lumen-framework/license.svg)](https://packagist.org/packages/laravel/lumen-framework)
+
+Laravel Lumen is a stunningly fast PHP micro-framework for building web applications with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Lumen attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as routing, database abstraction, queueing, and caching.
+
+## Official Documentation
+
+Documentation for the framework can be found on the [Lumen website](https://lumen.laravel.com/docs).
+
+## Contributing
+
+Thank you for considering contributing to Lumen! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
+
+## Security Vulnerabilities
+
+If you discover a security vulnerability within Lumen, please send an e-mail to Taylor Otwell at taylor@laravel.com. All security vulnerabilities will be promptly addressed.
+
+## License
+
+The Lumen framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
+### 增加虚拟阅读 每小时执行一次 
+#### 0 */1 * * * /usr/bin/php /var/www/starlight-admin/artisan make:blog_uv >> /dev/null 2>&1
+用户可以
+手机短信验证登录
+帐号密码登录
+
+更换手机号码流程
+1.获取当前手机验证码(为了确认当前用户自己的操作)
+2.填写验证码确认之后进入新界面,填写新手机号,并获取验证码以及当前帐号的密码
+
+忘记密码修改流程
+1.获取当前手机验证码(为了确认当前用户自己的操作)
+3.重复输入新密码
+
+修改密码
+输入旧密码
+输入两次新密码
+
+微信授权登录
+小程序授权登录
+
+微信公众号登录-》登录界面-》获取用户信息及获取手机号码(或者用户自己填写)-》验证时候已有普通帐号若有则进行验证码绑定,
+若没有则生成一个新的组织,并关联。
+
+微信小程序登录-》登录界面-》获取用户信息及获取手机号码(或者用户自己填写)-》验证时候已有普通帐号若有则进行验证码绑定,
+若没有则生成一个新的组织,并关联。

+ 78 - 0
app/Ad/Controllers/AdController.php

@@ -0,0 +1,78 @@
+<?php
+
+namespace App\Ad\Controllers;
+
+
+use App\Ad\Services\SysAdService;
+use App\Http\Controllers\BaseController;
+use Illuminate\Http\Request;
+
+class AdController extends BaseController
+{
+    private $service;
+    public function __construct(SysAdService $service)
+    {
+        $this->service = $service;
+    }
+
+    /**
+     * 广告保存
+     * */
+    public function saveAd(Request $request){
+        $this->validate($request, [
+            'position_id' => 'required',
+        ], [
+            'position_id.required' => '位置不能为空',
+        ]);
+        $params=$request->only(['id','position_id','ad_title','ad_file','ad_description', 'start_time','end_time',
+            'ad_link','ad_link_type','ad_script','form_id','type','remark','hit_num','hit_click_num',
+            'push_num']);
+        $ret=$this->service->saveAd($params);
+        return $this->jsonResponse(
+            'ok',
+            $ret
+        );
+    }
+
+
+    /**
+     * 广告保存
+     * */
+    public function changeStatus(Request $request){
+        $this->validate($request, [
+            'id' => 'required',
+        ], [
+            'id.required' => 'id不能为空',
+        ]);
+        $params=$request->only(['id','status']);
+        $ret=$this->service->saveAd($params);
+        return $this->jsonResponse(
+            'ok',
+            $ret
+        );
+    }
+
+    /**
+     * 获取广告列表
+     * */
+    public function getAdList(Request $request){
+        $params=$request->only(['page_size','page','status']);
+        $ret=$this->service->getAdList($params);
+        return $this->jsonResponse(
+            'ok',
+            $ret
+        );
+    }
+
+    /**
+     * 获取广告位列表
+     * */
+    public function getAdPositionList(Request $request){
+        $params=$request->only(['page_size','page','status']);
+        $ret=$this->service->getAdPositionList($params);
+        return $this->jsonResponse(
+            'ok',
+            $ret
+        );
+    }
+}

+ 13 - 0
app/Ad/Facades/SysAdFacade.php

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

+ 13 - 0
app/Ad/Facades/SysAdPositionFacade.php

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

+ 52 - 0
app/Ad/Models/SysAdModel.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Ad\Models;
+
+use App\Models\BaseModel;
+
+class SysAdModel extends BaseModel
+{
+    protected $table = 'sys_ad';
+
+    /**
+     * 检查 名称是否唯一
+     * */
+    public function checkPosition($positionId,$id=0){
+        $where=[];
+        if(!empty($id)){
+            $where[]=['id', '<>', $id];
+        }
+        $where[]=['status', '<', 2];
+        $where[]=['position_id', '=', trim($positionId)];
+        return $this->checkFieldUnique('position_id',$where);
+    }
+
+    /**
+     * 获取广告列表
+     * */
+    public function getAdList($params){
+        list($pageSize, $page, $skip) = $this->getPaginatorParams($params);
+        $where=[];
+        if(isset($params['status'])){
+            $where[]=['a.status','=',$params['status']];
+        }else{
+            $where[]=['a.status','<',2];
+        }
+        $query= $this->newInstance()->alias('a')
+            ->leftJoin('sys_ad_position as b', 'a.position_id', '=', 'b.id')
+            ->where($where);
+        $totalCount = $query->count();
+        $list= $query->skip($skip)
+            ->limit($pageSize)
+            ->selectRaw('a.*,b.number,b.route_path_key,b.ad_key')
+            ->orderBy('id')
+            ->get();
+        if(!empty($list)){
+            $list=$list->toArray();
+        }else{
+            $list=[];
+        }
+        $result = $this->buildPaginator($list, $skip, $page, $pageSize, $totalCount);
+        return $result;
+    }
+}

+ 38 - 0
app/Ad/Models/SysAdPositionModel.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace App\Ad\Models;
+
+use App\Models\BaseModel;
+
+class SysAdPositionModel extends BaseModel
+{
+    protected $table = 'sys_ad_position';
+
+
+    /**
+     * 获取广告列表
+     * */
+    public function getAdPositionList($params){
+        list($pageSize, $page, $skip) = $this->getPaginatorParams($params);
+        $where=[];
+        if(isset($params['status'])){
+            $where[]=['a.status','=',$params['status']];
+        }else{
+            $where[]=['a.status','<',2];
+        }
+        $query= $this->newInstance()->alias('a')->where($where);
+        $totalCount = $query->count();
+        $list= $query->skip($skip)
+            ->limit($pageSize)
+            ->selectRaw('a.*')
+            ->orderBy('a.update_time','desc')
+            ->get();
+        if(!empty($list)){
+            $list=$list->toArray();
+        }else{
+            $list=[];
+        }
+        $result = $this->buildPaginator($list, $skip, $page, $pageSize, $totalCount);
+        return $result;
+    }
+}

+ 25 - 0
app/Ad/Providers/AdServiceProvider.php

@@ -0,0 +1,25 @@
+<?php
+
+
+namespace App\Ad\Providers;
+
+
+use App\Ad\Facades\SysAdFacade;
+use App\Ad\Services\SysAdService;
+
+use App\Providers\EventServiceProvider;
+
+class AdServiceProvider extends EventServiceProvider
+{
+    public function register()
+    {
+        $this->registerAd();
+
+    }
+
+    public function registerAd(){
+        $this->app->bind(SysAdFacade::class, function () {
+            return app()->make(SysAdService::class);
+        });
+    }
+}

+ 273 - 0
app/Ad/Services/SysAdService.php

@@ -0,0 +1,273 @@
+<?php
+
+
+namespace App\Ad\Services;
+
+use App\Ad\Models\SysAdModel;
+use App\Ad\Models\SysAdPositionModel;
+use App\Exceptions\ApiException;
+use App\Services\CommonUserBaseService;
+use App\Web\Facades\WebFacade;
+use Illuminate\Support\Facades\Cache;
+
+class SysAdService extends CommonUserBaseService
+{
+    protected $cache = true;
+
+    protected $cacheBucket = 'SysAd:';
+
+    protected $sysAdPositionModel;
+
+
+    /**
+     * WebService construct
+     * @param SysAdModel $model
+     * @param SysAdPositionModel $sysAdPositionModel
+     * */
+    public function __construct(SysAdModel $model, SysAdPositionModel $sysAdPositionModel)
+    {
+        $this->model = $model;
+        $this->sysAdPositionModel = $sysAdPositionModel;
+    }
+
+    /**
+     * 广告设置保存
+     * */
+    public function saveAd($params){
+        $data= $this->buildAdData($params);
+        if(!empty($data['id'])){
+            $id = $data['id'];
+            $this->model->where('id','=',$id)->update($data);
+        } else {
+            $id = $this->save($data)->id;
+        }
+        $this->clearAdCache();
+        WebFacade::clearPageCache('urla:');
+        return $id;
+    }
+
+    /**
+     * 构造广告数据
+     * */
+    private function buildAdData($params,$userId = null){
+        $userId = $userId ?? $this->getAuthUserId();
+        $add = [];
+        $nowTime = nowTime();
+        $add['update_time']=$nowTime;
+        if(!empty($params['id'])){
+            $add['id']=$params['id'];
+            if(isset($params['position_id'])){
+                $add['position_id'] = $params['position_id'];
+                if(empty($params['position_id'])){
+                    throw new ApiException(12101);
+                }
+                $positionUnique =$this->model->checkPosition($params['position_id'],$params['id']);
+                if (!$positionUnique) {
+                    throw new ApiException(12102);
+                }
+            }
+            if(isset($params['ad_title'])){
+                $add['ad_title'] = empty($params['ad_title'])?'':$params['ad_title'];
+            }
+            if(isset($params['ad_file'])){
+                $add['ad_file'] = empty($params['ad_file'])?'':$params['ad_file'];
+            }
+            if(isset($params['ad_description'])){
+                $add['ad_description'] = empty($params['ad_description'])?'':json_encode($params['ad_description']);
+            }
+            if(isset($params['start_time'])){
+                if(!empty($params['start_time'])){
+                    $add['start_time']= $params['start_time'];
+                }
+            }
+            if(isset($params['end_time'])){
+                if(!empty($params['end_time'])){
+                    $add['end_time']= $params['end_time'];
+                }
+            }
+            if(isset($params['ad_link'])){
+                $add['ad_link'] = empty($params['ad_link'])?'':$params['ad_link'];
+            }
+            if(isset($params['ad_link_type'])){
+                $add['ad_link_type'] = empty($params['ad_link_type'])?0:$params['ad_link_type'];
+            }
+            if(isset($params['ad_script'])){
+                $add['ad_script'] = empty($params['ad_script'])?'':$params['ad_script'];
+            }
+            if(isset($params['form_id'])){
+                $add['form_id'] = empty($params['form_id'])?0:$params['form_id'];
+            }
+            if(isset($params['type'])){
+                $add['type'] = empty($params['type'])?0:$params['type'];
+            }
+            if(isset($params['status'])){
+                $add['status'] = empty($params['status'])?0:$params['status'];
+            }
+            if(isset($params['remark'])){
+                $add['remark'] = empty($params['remark'])?'':$params['remark'];
+            }
+            if(isset($params['hit_num'])){
+                $add['hit_num'] = empty($params['hit_num'])?0:$params['hit_num'];
+            }
+            if(isset($params['hit_click_num'])){
+                $add['hit_click_num'] = empty($params['hit_click_num'])?0:$params['hit_click_num'];
+            }
+            if(isset($params['push_num'])){
+                $add['push_num'] = empty($params['push_num'])?0:$params['push_num'];
+            }
+        }else{
+            $add['position_id'] = empty($params['position_id'])?0:$params['position_id'];
+            if(empty($params['position_id'])){
+                throw new ApiException(12101);
+            }
+            $positionUnique =$this->model->checkPosition($params['position_id']);
+            if (!$positionUnique) {
+                throw new ApiException(12102);
+            }
+            $add['ad_title'] = empty($params['ad_title'])?'':$params['ad_title'];
+            $add['ad_file'] = empty($params['ad_file'])?'':$params['ad_file'];
+            $add['ad_description'] = empty($params['ad_description'])?'':json_encode($params['ad_description']);
+            $add['ad_link'] = empty($params['ad_link'])?'':$params['ad_link'];
+            $add['ad_link_type'] = empty($params['ad_link_type'])?0:$params['ad_link_type'];
+            $add['ad_script'] = empty($params['ad_script'])?'':$params['ad_script'];
+            $add['form_id'] = empty($params['form_id'])?0:$params['form_id'];
+            $add['type'] = empty($params['type'])?0:$params['type'];
+            $add['hit_num'] = empty($params['hit_num'])?0:$params['hit_num'];
+            $add['hit_click_num'] = empty($params['hit_click_num'])?0:$params['hit_click_num'];
+            $add['push_num'] = empty($params['push_num'])?0:$params['push_num'];
+            $add['create_time'] = $nowTime;
+            if(!empty($params['start_time'])){
+                $add['start_time']= $params['start_time'];
+            }
+            if(!empty($params['end_time'])){
+                $add['end_time']= $params['end_time'];
+            }
+            $add['user_id']=$userId;
+        }
+        return $add;
+    }
+
+
+    /**
+     * 获取广告列表
+     */
+    public function getAdList($params)
+    {
+        $list = $this->model->getAdList($params);
+        return $list;
+    }
+
+    /**
+     * 获取详情
+     */
+    public function getAdInfo($id)
+    {
+        $fields = "a.*";
+        $info = $this->findOneById($id, $fields);
+        if (!empty($info['id'])) {
+            $info['ad_title'] = json_decode($info['ad_title'], true)[0];
+            $info['ad_description'] = json_decode($info['ad_description'], true)[0];
+            $info['ad_script'] = json_decode($info['ad_script'], true);
+        }
+        return $info;
+    }
+
+    /**
+     * 广告点击
+     */
+    public function adClick($id)
+    {
+        return $this->incrementBy([
+            "id" => $id
+        ], "hit_click_num");
+    }
+
+    /**
+     * 根据广告位编号
+     */
+    public function getAdPositionList($params)
+    {
+        $list = $this->sysAdPositionModel->getAdPositionList($params);
+        $data=!empty($list['data'])?$list['data']:[];
+        foreach ($data as $adPositionItem){
+            $adPositionItem['route_path_key']=json_decode($adPositionItem['route_path_key']);
+        }
+        $list['data']=$data;
+        return $list;
+    }
+
+    /**
+     * 获取所有已发布的广告
+     * */
+    public function getPublishAdData(){
+        $data=$this->getCacheAdData();
+        if(empty($data)){
+            $params=[];
+            $params['status']=0;
+            $params['page_size']=999;
+            $list = $this->model->getAdList($params);
+            if(!empty($list['data'])){
+                $data= $list['data'];
+                $this->setAdCache($data);
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * 获取页面详情
+     * @return array
+     */
+    public function getCacheAdData()
+    {
+        $key = $this->getCacheAdKey();
+        return Cache::get($key);
+    }
+
+    /**
+     * 获取广告缓存key
+     * @param $urla
+     * @return string
+     */
+    private function getCacheAdKey()
+    {
+        $pageParamsStr = 'web-ad-list:' . md5('web-ad-list');
+        return $this->cacheBucket . $pageParamsStr;
+    }
+
+    /**
+     * 缓存广告数据
+     * @param array $adData
+     */
+    private function setAdCache($adData)
+    {
+        Cache::put($this->getCacheAdKey(), $adData, config('cache.page_time'));
+    }
+
+    /**
+     * 清除广告数据缓存
+     * */
+    public function clearAdCache()
+    {
+        $this->removeByKey('web-ad-list');
+    }
+
+    /**
+     * 获取页面广告数据
+     * */
+    public function getPageAdData($routePathKey){
+        $retData=[];
+        $adData=$this->getPublishAdData();
+        if (!empty($adData)) {
+            foreach ($adData as $value){
+                if(!empty($value['route_path_key'])){
+                    $routePathKeyArray=json_decode($value['route_path_key'],true);
+                    if(in_array($routePathKey,$routePathKeyArray)){
+                        $retData[]= $value;
+                    }
+                }
+            }
+        }
+        return $retData;
+    }
+}

+ 26 - 0
app/Ad/routes.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywlministrator
+ * Date: 2023/3/10
+ * Time: 11:05
+ */
+$app = app();
+$languagePath = '';
+if(config('app.language_path')) {
+    $languagePath = config('app.language_path').'/';
+}
+$app->router->group(['namespace' => 'App\Ad\Controllers',
+    'prefix' => $languagePath.'api'], function ($router) {
+
+    $router->group(['middleware' =>['admin.auth']], function ($router) {
+        //保存广告设置
+        $router->post('ad/save-ad', 'AdController@saveAd');
+        // 更改广告状态
+        $router->post('ad/set-ad-status', 'AdController@changeStatus');
+        //获取广告列表
+        $router->get('ad/get-list', 'AdController@getAdList');
+        //获取广告位列表
+        $router->get('ad/get-position-list', 'AdController@getAdPositionList');
+    });
+});

+ 72 - 0
app/Common/Controllers/CommonController.php

@@ -0,0 +1,72 @@
+<?php
+
+namespace App\Common\Controllers;
+
+use App\Common\Facades\ComEmailCodeFacade;
+use App\Common\Facades\ComSmsFacade;
+use App\Common\Facades\UploadFileFacade;
+use App\Common\Models\ComEmailCodeModel;
+use App\Common\Services\WechatService;
+use App\Http\Controllers\BaseController;
+use Illuminate\Http\Request;
+
+class CommonController extends BaseController
+{
+
+    /**
+     * 获取验证码 phone 手机 type 验证类型 1登录验证 2注册验证 3忘记密码
+     */
+    public function getSMSCode(Request $request){
+        $this->validate($request, [
+            'phone' => 'required'
+        ], [
+            'phone.required' => '手机不能为空'
+        ]);
+        $params = $request->only(['phone', 'is_send', 'country_code', 'app_code']);
+        $phone=$params['phone'];
+        $smsVerify = ComSmsFacade::validataPhoneMinute($phone,0);
+        if ($smsVerify['code'] == -1) {
+            return $this->jsonResponse('', '',10020);
+        }
+        $ret=ComSmsFacade::sendSMSInfo($params);
+        if($ret['code'] == 0){
+            return $this->jsonResponse('ok', $ret['data']);
+        }else{
+            return $this->jsonResponse($ret['message'], $ret['data'],$ret['code']);
+        }
+    }
+
+    /**
+     * 获取邮箱验证码
+     */
+    public function getEmailCode(Request $request){
+        $email=$request->input('email','');
+        $isSend=$request->input('is_send',1);
+        $language=$request->input('language','zh-cn');
+        $footer=$request->input('footer','');
+        $this->validate($request, [
+            'email' => 'required'
+        ], [
+            'email.required' => '手机不能为空'
+        ]);
+
+        $ret=ComEmailCodeFacade::sendValidateCode($email,$isSend,$language,$footer);
+        return $this->jsonResponse('ok', $ret);
+    }
+
+
+    public function upload(Request $request)
+    {
+        $upload = UploadFileFacade::upload($request, $request->input('field', 'file'));
+        return $this->jsonResponse('ok', $upload);
+    }
+
+
+    public function getWxaQuerySchemeFade(Request $request)
+    {
+        $scheme = $request->input('scheme');
+        $wechatService = new WechatService();
+        $data = $wechatService->getWxaQueryScheme($scheme);
+        return $this->jsonResponse('ok', $data);
+    }
+}

+ 21 - 0
app/Common/Facades/ComEmailCodeFacade.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2019/3/19
+ * Time: 15:08
+ */
+
+namespace App\Common\Facades;
+
+
+use Illuminate\Support\Facades\Facade;
+
+class ComEmailCodeFacade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+
+}

+ 21 - 0
app/Common/Facades/ComSmsFacade.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2019/3/19
+ * Time: 15:08
+ */
+
+namespace App\Common\Facades;
+
+
+use Illuminate\Support\Facades\Facade;
+
+class ComSmsFacade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+
+}

+ 21 - 0
app/Common/Facades/OssFacade.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2019/3/19
+ * Time: 15:08
+ */
+
+namespace App\Common\Facades;
+
+
+use Illuminate\Support\Facades\Facade;
+
+class OssFacade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+
+}

+ 20 - 0
app/Common/Facades/PinYinFacade.php

@@ -0,0 +1,20 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2019/3/19
+ * Time: 15:08
+ */
+
+namespace App\Common\Facades;
+
+
+use Illuminate\Support\Facades\Facade;
+class PinYinFacade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+
+}

+ 21 - 0
app/Common/Facades/UploadFileFacade.php

@@ -0,0 +1,21 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2019/3/19
+ * Time: 15:08
+ */
+
+namespace App\Common\Facades;
+
+
+use Illuminate\Support\Facades\Facade;
+
+class UploadFileFacade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+
+}

+ 28 - 0
app/Common/Facades/UserCenterFacade.php

@@ -0,0 +1,28 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2019/3/19
+ * Time: 15:08
+ */
+
+namespace App\Common\Facades;
+
+
+use Illuminate\Support\Facades\Facade;
+use phpDocumentor\Reflection\Types\Integer;
+
+
+/**
+ * @method static \App\Common\Services\UserCenterService getSmsCode(string $phone,string $countryCode='86',Integer $type = 0) 获取短信验证码
+ * Class UserCenterFacade
+ * @package App\Common\Facades
+ */
+class UserCenterFacade extends Facade
+{
+    protected static function getFacadeAccessor()
+    {
+        return self::class;
+    }
+
+}

+ 76 - 0
app/Common/Library/AES.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace App\Common\Library;
+
+/**
+ * Class AES.
+ *
+ * @author overtrue <i@overtrue.me>
+ */
+class AES
+{
+    /**
+     * @param string $text
+     * @param string $key
+     * @param string $iv
+     * @param int    $option
+     *
+     * @return string
+     */
+    public static function encrypt(string $text, string $key, string $iv, int $option = OPENSSL_RAW_DATA): string
+    {
+        self::validateKey($key);
+        self::validateIv($iv);
+
+        return openssl_encrypt($text, self::getMode($key), $key, $option, $iv);
+    }
+
+    /**
+     * @param string      $cipherText
+     * @param string      $key
+     * @param string      $iv
+     * @param int         $option
+     * @param string|null $method
+     *
+     * @return string
+     */
+    public static function decrypt(string $cipherText, string $key, string $iv, int $option = OPENSSL_RAW_DATA, $method = null): string
+    {
+        self::validateKey($key);
+        self::validateIv($iv);
+
+        return openssl_decrypt($cipherText, $method ?: self::getMode($key), $key, $option, $iv);
+    }
+
+    /**
+     * @param string $key
+     *
+     * @return string
+     */
+    public static function getMode($key)
+    {
+        return 'aes-'.(8 * strlen($key)).'-cbc';
+    }
+
+    /**
+     * @param string $key
+     */
+    public static function validateKey(string $key)
+    {
+        if (!in_array(strlen($key), [16, 24, 32], true)) {
+            throw new \InvalidArgumentException(sprintf('Key length must be 16, 24, or 32 bytes; got key len (%s).', strlen($key)));
+        }
+    }
+
+    /**
+     * @param string $iv
+     *
+     * @throws \InvalidArgumentException
+     */
+    public static function validateIv(string $iv)
+    {
+        if (!empty($iv) && 16 !== strlen($iv)) {
+            throw new \InvalidArgumentException('IV length must be 16 bytes.');
+        }
+    }
+}

+ 53 - 0
app/Common/Library/DES.php

@@ -0,0 +1,53 @@
+<?php
+
+
+namespace App\Common\Library;
+
+class DES
+{
+    private $key;
+    private $encryptor;
+
+    public function __construct($key)
+    {
+        $this->key = $this->complement($key);
+        $this->encryptor = new Encryptor($key);
+    }
+
+    public function complement($key)
+    {
+        if (strlen($key) < 8) {
+            $len = strlen($key) % 8;
+            if ($len == 0) {
+                return $key;
+            } else {
+                $j = 8 - $len;
+                for ($i = 0; $i < $j; $i++) {
+                    $key .= "\0";
+                }
+                return $key;
+            }
+        } else {
+            return $key;
+        }
+    }
+
+    /**
+     * @param $encrypt
+     * @return string
+     */
+    public function encrypt($encrypt)
+    {
+        return $this->encryptor->encrypt($encrypt);
+    }
+
+
+    /**
+     * @param $decrypt
+     * @return string
+     */
+    public function decrypt($decrypt)
+    {
+        return $this->encryptor->decrypt($decrypt);
+    }
+}

+ 84 - 0
app/Common/Library/Encryptor.php

@@ -0,0 +1,84 @@
+<?php
+namespace App\Common\Library;
+class Encryptor
+{
+
+    /**
+     * Holds the Encryptor instance
+     * @var Encryptor
+     */
+    private static $instance;
+
+    /**
+     * @var string
+     */
+    private $method;
+
+    /**
+     * @var string
+     */
+    private $key;
+
+    /**
+     * @var string
+     */
+    private $separator;
+
+    /**
+     * Encryptor constructor.
+     */
+    public function __construct($key, $method = 'aes-256-cfb')
+    {
+        $this->method = $method;
+        $this->key = $key;
+        $this->separator = ':';
+    }
+
+    private function __clone()
+    {
+    }
+
+    /**
+     * Returns an instance of the Encryptor class or creates the new instance if the instance is not created yet.
+     * @return Encryptor
+     */
+    public static function getInstance()
+    {
+        if (self::$instance === null) {
+            self::$instance = new Encryptor();
+        }
+        return self::$instance;
+    }
+
+    /**
+     * Generates the initialization vector
+     * @return string
+     */
+    private function getIv()
+    {
+        return openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->method));
+    }
+
+    /**
+     * @param string $data
+     * @return string
+     */
+    public function encrypt($data)
+    {
+        $iv = $this->getIv();
+        return base64_encode(openssl_encrypt($data, $this->method, $this->key, 0, $iv) . $this->separator . base64_encode($iv));
+    }
+
+    /**
+     * @param string $dataAndVector
+     * @return string
+     */
+    public function decrypt($dataAndVector)
+    {
+        $parts = explode($this->separator, base64_decode($dataAndVector));
+        // $parts[0] = encrypted data
+        // $parts[1] = initialization vector
+        return openssl_decrypt($parts[0], $this->method, $this->key, 0, base64_decode($parts[1]));
+    }
+
+}

+ 177 - 0
app/Common/Library/Random.php

@@ -0,0 +1,177 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: fangx
+ * Date: 2021/7/8
+ * Time: 9:48
+ */
+
+namespace App\Common\Library;
+
+/**
+ * 随机生成类
+ */
+class Random
+{
+
+    /**
+     * 生成数字和字母
+     *
+     * @param int $len 长度
+     * @return string
+     */
+    public static function alnum($len = 6)
+    {
+        return self::build('alnum', $len);
+    }
+
+    /**
+     * 仅生成字符
+     *
+     * @param int $len 长度
+     * @return string
+     */
+    public static function alpha($len = 6)
+    {
+        return self::build('alpha', $len);
+    }
+
+    /**
+     * 生成指定长度的随机数字
+     *
+     * @param int $len 长度
+     * @return string
+     */
+    public static function numeric($len = 4)
+    {
+        return self::build('numeric', $len);
+    }
+
+    /**
+     * 生成指定长度的无0随机数字
+     *
+     * @param int $len 长度
+     * @return string
+     */
+    public static function nozero($len = 4)
+    {
+        return self::build('nozero', $len);
+    }
+
+    /**
+     * 能用的随机数生成
+     * @param string $type 类型 alpha/alnum/numeric/nozero/unique/md5/encrypt/sha1
+     * @param int    $len  长度
+     * @return string
+     */
+    public static function build($type = 'alnum', $len = 8)
+    {
+        switch ($type) {
+            case 'alpha':
+            case 'alnum':
+            case 'numeric':
+            case 'nozero':
+                switch ($type) {
+                    case 'alpha':
+                        $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+                        break;
+                    case 'alnum':
+                        $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+                        break;
+                    case 'numeric':
+                        $pool = '0123456789';
+                        break;
+                    case 'nozero':
+                        $pool = '123456789';
+                        break;
+                }
+                return substr(str_shuffle(str_repeat($pool, ceil($len / strlen($pool)))), 0, $len);
+            case 'unique':
+            case 'md5':
+                return md5(uniqid(mt_rand()));
+            case 'encrypt':
+            case 'sha1':
+                return sha1(uniqid(mt_rand(), true));
+        }
+    }
+
+    /**
+     * 根据数组元素的概率获得键名
+     *
+     * @param array $ps     array('p1'=>20, 'p2'=>30, 'p3'=>50);
+     * @param int   $num    默认为1,即随机出来的数量
+     * @param bool  $unique 默认为true,即当num>1时,随机出的数量是否唯一
+     * @return mixed 当num为1时返回键名,反之返回一维数组
+     */
+    public static function lottery($ps, $num = 1, $unique = true)
+    {
+        if (!$ps) {
+            return $num == 1 ? '' : [];
+        }
+        if ($num >= count($ps) && $unique) {
+            $res = array_keys($ps);
+            return $num == 1 ? $res[0] : $res;
+        }
+        $max_exp = 0;
+        $res = [];
+        foreach ($ps as $key => $value) {
+            $value = substr($value, 0, stripos($value, ".") + 6);
+            $exp = strlen(strchr($value, '.')) - 1;
+            if ($exp > $max_exp) {
+                $max_exp = $exp;
+            }
+        }
+        $pow_exp = pow(10, $max_exp);
+        if ($pow_exp > 1) {
+            reset($ps);
+            foreach ($ps as $key => $value) {
+                $ps[$key] = $value * $pow_exp;
+            }
+        }
+        $pro_sum = array_sum($ps);
+        if ($pro_sum < 1) {
+            return $num == 1 ? '' : [];
+        }
+        for ($i = 0; $i < $num; $i++) {
+            $rand_num = mt_rand(1, $pro_sum);
+            reset($ps);
+            foreach ($ps as $key => $value) {
+                if ($rand_num <= $value) {
+                    break;
+                } else {
+                    $rand_num -= $value;
+                }
+            }
+            if ($num == 1) {
+                $res = $key;
+                break;
+            } else {
+                $res[$i] = $key;
+            }
+            if ($unique) {
+                $pro_sum -= $value;
+                unset($ps[$key]);
+            }
+        }
+        return $res;
+    }
+
+    /**
+     * 获取全球唯一标识
+     * @return string
+     */
+    public static function uuid()
+    {
+        return sprintf(
+            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+            mt_rand(0, 0xffff),
+            mt_rand(0, 0xffff),
+            mt_rand(0, 0xffff),
+            mt_rand(0, 0x0fff) | 0x4000,
+            mt_rand(0, 0x3fff) | 0x8000,
+            mt_rand(0, 0xffff),
+            mt_rand(0, 0xffff),
+            mt_rand(0, 0xffff)
+        );
+    }
+}

+ 30 - 0
app/Common/Models/ComEmailCodeModel.php

@@ -0,0 +1,30 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2018/4/12
+ * Time: 下午11:18
+ */
+
+namespace App\Common\Models;
+
+use App\Models\BaseModel;
+
+/**
+ * 短信日志表,后续用MongoDB进行存储
+ * */
+
+class ComEmailCodeModel extends BaseModel
+{
+    protected $table = 'com_email_code';
+
+    /**
+     * 插入时间字段
+     */
+    const CREATED_AT = 'create_time';
+
+    /**
+     * 更新时间字段
+     */
+    const UPDATED_AT = null;
+}

+ 25 - 0
app/Common/Models/ComOssETagModel.php

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

+ 30 - 0
app/Common/Models/ComSmsModel.php

@@ -0,0 +1,30 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2018/4/12
+ * Time: 下午11:18
+ */
+
+namespace App\Common\Models;
+
+use App\Models\BaseModel;
+
+/**
+ * 短信日志表,后续用MongoDB进行存储
+ * */
+
+class ComSmsModel extends BaseModel
+{
+    protected $table = 'com_sms';
+
+    /**
+     * 插入时间字段
+     */
+    const CREATED_AT = 'create_time';
+
+    /**
+     * 更新时间字段
+     */
+    const UPDATED_AT = null;
+}

+ 93 - 0
app/Common/Models/ExportExcel.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace App\Common\Models;
+
+use Maatwebsite\Excel\Concerns\FromArray;
+use Maatwebsite\Excel\Concerns\WithColumnFormatting;
+use Maatwebsite\Excel\Concerns\WithEvents;
+use Maatwebsite\Excel\Concerns\WithHeadings;
+use Maatwebsite\Excel\Concerns\WithMapping;
+
+/**
+ * 通用导出EXCEL文件对象
+ * Class ExportObject
+ */
+class ExportExcel implements FromArray, WithMapping, WithHeadings, WithEvents, WithColumnFormatting
+{
+    private $dataList = [];
+    private $headerMap = [];
+    private $afterSheetStyle = [];
+    private $columnFormats = [];
+
+    /**
+     * 构造函数
+     * ExportObject constructor.
+     * @param array $headerMap 头部描述数据
+     * @param array $dataList 数据列表
+     */
+    public function __construct(array &$headerMap, array &$dataList, array &$afterSheetStyle = [], array &$columnFormats)
+    {
+        @ini_set("memory_limit", '512M');
+        set_time_limit(60);
+        $this->dataList = $dataList;
+        $this->headerMap = $headerMap;
+        $this->afterSheetStyle = $afterSheetStyle;
+        $this->columnFormats = $columnFormats;
+    }
+
+    /**
+     * 注册事件
+     * @return array
+     */
+    public function registerEvents(): array
+    {
+        return $this->afterSheetStyle;
+    }
+
+    /**
+     * 映射数据行
+     * @param mixed $row
+     * @return array
+     */
+    public function map($row): array
+    {
+        $o = [];
+        foreach ($this->headerMap as $k => $v) {
+            if (isset($row[$k])) {
+                if ($row[$k] !== null && $row[$k] === 0) {
+                    $row[$k] = '0';
+                } else if ($row[$k] === null) {
+                    $row[$k] = '';
+                }
+            } else {
+                $row[$k] = '';
+            }
+            $o[] = $row[$k];
+        }
+        return $o;
+    }
+
+    /**
+     * 返回头部
+     * @return array
+     */
+    public function headings(): array
+    {
+        return array_values($this->headerMap);
+    }
+
+    /**
+     * 数据源
+     * @return array
+     */
+    public function array(): array
+    {
+        return $this->dataList;
+    }
+
+
+    public function columnFormats(): array
+    {
+        return $this->columnFormats;
+    }
+}

+ 57 - 0
app/Common/Models/ImportExcel.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace App\Common\Models;
+
+use Illuminate\Support\Collection;
+use Maatwebsite\Excel\Concerns\ToCollection;
+use Maatwebsite\Excel\Concerns\WithChunkReading;
+
+/**
+ * 通用导入EXCEL文件对象
+ * Class ImportExcel
+ */
+class ImportExcel implements ToCollection, WithChunkReading
+{
+    private $callback;
+    private $readRows = 0;
+
+    public function __construct(\Closure $callback)
+    {
+        $this->callback = $callback;
+    }
+
+    /**
+     * 格式化日期
+     * @param int $value
+     * @param string $format
+     */
+    public static function transformDateTime(int $value, string $format = 'Y-m-d H:i:s')
+    {
+        if (!$value) {
+            return '';
+        }
+        $value--;
+        return date($format, strtotime("1900-01-00 00:00:00 +$value day"));
+    }
+
+    /**
+     * @param Collection $collection
+     */
+    public function collection(Collection $collection)
+    {
+        if ($this->readRows == 0) {
+            unset($collection[0]);
+            $this->readRows += count($collection);
+        }
+        $this->callback->call($this, $collection);
+    }
+
+    /**
+     * @return int
+     */
+    public function chunkSize(): int
+    {
+        return 1000;
+    }
+
+}

+ 114 - 0
app/Common/Providers/CommonProvider.php

@@ -0,0 +1,114 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2017/4/14
+ * Time: 10:56
+ */
+
+namespace App\Common\Providers;
+
+use App\Common\Facades\ComEmailCodeFacade;
+use App\Common\Facades\ComSmsFacade;
+use App\Common\Facades\OssFacade;
+use App\Common\Facades\PinYinFacade;
+use App\Common\Facades\UploadFileFacade;
+use App\Common\Facades\UserCenterFacade;
+use App\Common\Models\ComEmailCodeModel;
+use App\Common\Models\ComOssETagModel;
+use App\Common\Models\ComSmsModel;
+use App\Common\Services\ComEmailCodeService;
+use App\Common\Services\ComSmsService;
+use App\Common\Services\OssService;
+use App\Common\Services\PinYinService;
+use App\Common\Services\UploadFileService;
+use App\Common\Services\UserCenterService;
+use App\Providers\EventServiceProvider;
+
+class CommonProvider extends EventServiceProvider
+{
+
+    /**
+     * 注册绑定门面
+     */
+    public function register()
+    {
+        //注册上传接口
+        $this->registerUpload();
+        //OSS
+        $this->registerOss();
+        //注册公共短信服务
+        $this->registerComSms();
+        //注册短信验证码
+        $this->registerComEmailCode();
+        //注册用户中心服务
+        $this->registerUserCenter();
+
+        $this->registerPinYin();
+    }
+
+    /**
+     * 注册OSS
+     */
+    protected function registerOss(){
+
+        $this->app->bind(OssService::class, function () {
+            return new OssService(new ComOssETagModel());
+        });
+        $this->app->bind(OssFacade::class, function () {
+            return app()->make(OssService::class);
+        });
+    }
+
+    /**
+     * 注册OSS
+     */
+    protected function registerUpload(){
+        $this->app->bind(UploadFileFacade::class, function () {
+            return app()->make(UploadFileService::class);
+        });
+    }
+
+    /**
+     * 注册公共短信验证码发送服务
+     * */
+    protected function registerComSms(){
+        $this->app->bind(ComSmsService::class, function () {
+            return new ComSmsService(new ComSmsModel());
+        });
+        $this->app->bind(ComSmsFacade::class, function () {
+            return app()->make(ComSmsService::class);
+        });
+    }
+
+    /**
+     * 注册公共邮箱验证码发送服务
+     * */
+    protected function registerComEmailCode()
+    {
+        $this->app->bind(ComEmailCodeService::class, function () {
+            return new ComEmailCodeService(new ComEmailCodeModel());
+        });
+        $this->app->bind(ComEmailCodeFacade::class, function () {
+            return app()->make(ComEmailCodeService::class);
+        });
+    }
+
+    protected function registerUserCenter()
+    {
+        $this->app->bind(UserCenterFacade::class, function () {
+            return app()->make(UserCenterService::class);
+        });
+    }
+
+    /**
+     * 拼音
+     */
+    protected function registerPinYin()
+    {
+        $this->app->bind(PinYinFacade::class, function () {
+            return app()->make(PinYinService::class);
+        });
+    }
+
+}

+ 199 - 0
app/Common/Services/ComEmailCodeService.php

@@ -0,0 +1,199 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2017/4/14
+ * Time: 11:38
+ */
+
+namespace App\Common\Services;
+
+
+use AlibabaCloud\Client\Exception\ClientException;
+use App\Common\Models\ComEmailCodeModel;
+use App\Exceptions\ApiException;
+use App\Services\CommonService;
+use Illuminate\Database\QueryException;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Mail;
+
+class ComEmailCodeService extends CommonService
+{
+    protected $cache = true;
+
+    protected $cacheBucket = 'ComEmailCode:';
+
+    /**
+     * LogSendCodeService constructor.
+     * @param ComEmailCodeModel $model
+     */
+    public function __construct(ComEmailCodeModel $model)
+    {
+        $this->model = $model;
+    }
+
+    /**
+     * 发送验证码
+     * @param array $params phone email type 验证类型 0 手机验证 1邮箱验证
+     * @return bool|mixed
+     * @throws \App\Exceptions\ApiException;
+     * @throws ClientException
+     */
+    public function sendValidateCode($email,$isSend=true,$language='zh-cn',$footer='')
+    {
+        $resultData=['id'=>0,'code'=>''];
+        $code = mt_rand(100000, 999999);
+        $resultData['code']=$code;
+        $currentTime = time(); //当前时间
+        $title='邮箱验证码';//邮件标题
+        if(empty($email)){
+            throw new ApiException(10021);
+        }
+        if($isSend){
+            $httpData=[];//邮件内容变量参数
+            $httpData['email']=$email;
+            $httpData['code']=$code;
+            $httpData['footer']=$footer;
+            $mailTemp='mail.validateCodeModel';
+            if($language=='en-us'){
+                $mailTemp= 'mail.en.validateCodeModel';
+            }
+            Mail::send($mailTemp, $httpData, function ($message) use ($email,$title) {
+                $message->to($email)->subject($title);
+            });
+        }
+        $data = [
+            'email' => $email,
+            'code' => $code,
+            'error' => '',
+        ];
+        $data['status'] = 1;
+        $data['expire_time']=$currentTime + config('cache.sms_time');
+        $key=$email;
+        $cacheKey=md5($key);
+        $cacheTime=config('cache.sms_time');
+        $this->setCacheData($cacheKey,$data,$cacheTime);
+        $logId = $this->saveLogSendCode($data);
+        $resultData['id']=$logId;
+        $resultData['code']=$code;
+        $resultData['email']=$email;
+        return  $resultData;
+
+    }
+
+
+    /**
+     * 短信记录
+     * @param array $params 短信存储参数
+     * @return bool|mixed
+     * @throws App\Base\Exceptions\ApiException
+     */
+    public function saveLogSendCode($params = [])
+    {
+        //手机验证码信息
+        $nowTime=nowTime();
+        $codeData = [
+            'code' => $params['code'],
+            'email' =>empty($params['email']) ? '' : $params['email'],
+            'status' => $params['status'],
+            'error' => empty($params['error']) ? '' : $params['error'],
+            'create_time' => $nowTime
+        ];
+        if(!empty($params['expire_time'])){
+            $codeData['expire_time']= date('Y-m-d H:i:s', $params['expire_time']);
+        }
+        try {
+            $id=$this->model->insertGetId($codeData);
+            if (!$id) {
+                Log::info('method:saveLogSendCode:保存验证码失败');
+                throw new ApiException(500);
+            }
+            return $id;
+        } catch (QueryException $ex) {
+            //异常处理
+            Log::info('method:saveLogSendCode:' . $ex->getMessage());
+            Log::info('method:saveLogSendCode:' . $ex->getTraceAsString());
+            throw new ApiException('common.server_busy', '服务器忙,请稍候重试~');
+        }
+    }
+
+    /**
+     * 校验短信验证码是否有效
+     * @param string $key 手机号/邮箱
+     * @param string $code 验证码
+     * @param integer $type 验证类型 0 手机验证 1邮箱验证
+     * @return array  -1 失败重新获取验证码 -2验证码错误重新输入,1 成功是否验证成功
+     */
+    public  function validateCode($key, $code)
+    {
+        //todo 通过redis 缓存来验证
+        $result=['code'=>-1];
+        $current_time = time();
+        $cacheKey=md5($key);
+        $cacheData=$this->getCacheData($cacheKey);
+        if($cacheData){
+            if(!empty($cacheData['expire_time'])&&$cacheData['expire_time']>$current_time&&$code==$cacheData['code']){
+                $result['code']=1;
+                $this->removeByKey($cacheKey);
+            }else if($code!=$cacheData['code']){
+                $result['code']=-2;
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * 校验短信验证码一分钟内是否重复发送
+     * */
+    public function validatePhoneMinute($phone){
+        $result=['code'=>0];
+        $where=[
+            'phone' => $phone,
+            'status' => 1,
+        ];
+        $smsTime= config('cache.sms_time');
+        $addTime=($smsTime/60)-1;
+        $expireTime = date("Y-m-d H:i:s",strtotime("+".$addTime." minute",strtotime(nowTime()))); // 短信有效时间为30分钟。,当前时间+29
+        $smsData = $this->model->where($where)->where('expire_time','>',$expireTime)->first();
+        if(!empty($smsData)){
+            $result['code']=-1;
+        }
+        return $result;
+    }
+
+
+    /**
+     * 通用缓存方法
+     * @param string $key
+     * @param array $data
+     * */
+    public function setCacheData($key, $data,$cacheTime=0)
+    {
+        $cacheTime=empty($cacheTime)?config('cache.def_time'):$cacheTime;
+        Cache::put($this->getCacheKey($key), $data, $cacheTime);
+    }
+
+
+    /**
+     * 获取通用缓存数据key
+     * @param string $key
+     * @return string
+     * */
+    private function getCacheKey($key)
+    {
+        $prefix='VERIFICATION';
+        return $this->cacheBucket.$prefix.$key;
+    }
+
+    /**
+     * 通用缓存方法
+     * @param string $key
+     * @param array $data
+     * */
+    public function getCacheData($key)
+    {
+        $dataKey= $this->getCacheKey($key);
+        return Cache::get($dataKey);
+    }
+}

+ 299 - 0
app/Common/Services/ComSmsService.php

@@ -0,0 +1,299 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2017/4/14
+ * Time: 11:38
+ */
+
+namespace App\Common\Services;
+
+
+use AlibabaCloud\Client\AlibabaCloud;
+use AlibabaCloud\Client\Exception\ClientException;
+use App\Common\Models\ComSmsModel;
+use App\Exceptions\ApiException;
+use App\Services\CommonBaseService;
+use App\User\Models\SysAdminUserModel;
+use Illuminate\Database\QueryException;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Mail;
+
+class ComSmsService extends CommonBaseService
+{
+    protected $cache = true;
+
+    protected $cacheBucket = 'ComSms:';
+
+    protected $tokenBucket = 'Token:';
+
+    protected $activeBucket = "Active:";
+
+    /**
+     * 发送短信验证码
+     * @param string $phone
+     * @param string $accessKeyId
+     * @param string $accessKeySecret
+     * @param string $signName
+     * @param string $templateCode
+     * @param string $type
+     * @return bool|mixed
+     * @throws \App\Exceptions\ApiException
+     * @throws ClientException
+     */
+    public function sendSMSInfo($smsParams,$accessKeyId = '', $accessKeySecret = '', $signName = '', $templateCode = '' )
+    {
+        $phone=$smsParams['phone'];
+        if(isset($smsParams['is_send'])){
+            $isSend=$smsParams['is_send'];
+        }else{
+            $isSend=1;
+        }
+        $appCode=empty($smsParams['app_code'])?'':$smsParams['app_code'];
+        $countryCode=empty($smsParams['country_code'])?'86':$smsParams['country_code'];
+        $ret=['code'=>0,'data'=>[],'message'=>''];
+        try {
+            $code = mt_rand(100000, 999999);
+            if($isSend){
+                //发送短信
+
+                if (!empty($countryCode) && $countryCode != 86) {
+                    if (empty($accessKeyId)) {
+                        $accessKeyId = config('sms.foreign_access_key_id'); //阿里云短信公钥
+                    }
+                    if (empty($accessKeySecret)) {
+                        $accessKeySecret = config('sms.foreign_access_key_secret'); //阿里云短信私钥
+                    }
+                    if (empty($signName)) {
+                        $signName = config('sms.foreign_sign_name'); //阿里云短信 签名名称
+                    }
+                    if (empty($templateCode)) {
+                        $templateCode = config('sms.foreign_template_code'); //阿里云短信 短信模板ID
+                    }
+                }else{
+                    if (empty($accessKeyId)) {
+                        $accessKeyId = config('sms.access_key_id'); //阿里云短信公钥
+                    }
+                    if (empty($accessKeySecret)) {
+                        $accessKeySecret = config('sms.access_key_secret'); //阿里云短信私钥
+                    }
+                    if (empty($signName)) {
+                        $signName = config('sms.sign_name'); //阿里云短信 签名名称
+                        $appSignName=config('sms.app_sign_name');
+                        if($appSignName&&!empty($appCode)){
+                            if(!empty($appSignName[$appCode])) {
+                                $signName=$appSignName[$appCode];
+                            }
+                        }
+                    }
+                    if (empty($templateCode)) {
+                        $templateCode = config('sms.template_code'); //阿里云短信 短信模板ID
+                    }
+                }
+
+                AlibabaCloud::accessKeyClient($accessKeyId, $accessKeySecret)
+                    ->regionId('cn-hangzhou')// replace regionId as you need
+                    ->asDefaultClient();
+                $response = AlibabaCloud::rpc()
+                    ->product('Dysmsapi')
+                    // ->scheme('https') // https | http
+                    ->version('2017-05-25')
+                    ->action('SendSms')
+                    ->method('POST')
+                    ->host('dysmsapi.aliyuncs.com')
+                    ->options([
+                        'query' => [
+                            'SignName' => $signName, //阿里云短信 签名名称
+                            'TemplateCode' => $templateCode, //阿里云短信 短信模板ID
+                            'PhoneNumbers' => $phone, //手机号
+                            'TemplateParam' => '{"code":' . $code . '}',//验证码参数  json格式
+                        ],
+                    ])
+                    ->request();
+                $data = [
+                    'phone' => $phone,
+                    'code' => $code,
+                    'template_code' => $templateCode,
+                    'error' => empty($params['error']) ? '' : $params['error'],
+                    'type' => 0
+                ];
+                $responseData= $response->toArray();
+                if (isset($responseData['Code']) && $responseData['Code'] == 'OK') {
+                    $currentTime = time(); //当前时间
+                    $data['status'] = 1;
+                    $data['expire_time']=$currentTime + config('sms.expireTime');
+                    $key=$phone;
+                    $cacheKey=md5($key);
+                    $cacheTime=config('cache.sms_time');
+                    $this->setCacheData($cacheKey,$data,$cacheTime);
+                    $result = $this->updatePhoneCode($data);
+                    $resultData['id']=$result;
+                    $resultData['code']=$code;
+                    $resultData['phone']=$phone;
+                    $ret['data'] = $resultData;
+                    return $ret;
+                } else {
+                    $data['status'] = -1;
+                    $errorMessage = empty($response->Message) ? '' : $response->Message;
+                    $errorCode = empty($response->Code) ? '' : $response->Code;
+                    $errorRequestId = empty($response->RequestId) ? '' : $response->RequestId;
+                    $data['error'] = 'ErrorRequestId' . $errorRequestId . ';ErrorCode:' . $errorCode . ';Message:' . $errorMessage;
+                    Log::info('method:error:' . $response);
+                    // throw new ApiException(1001); //短信发送参数code错误
+                    $ret['code']=1010;
+                    return $ret;
+                }
+            }else{
+                $data = [
+                    'phone' => $phone,
+                    'code' => $code,
+                    'template_code' => '',
+                    'error' => '',
+                    'type' => 0
+                ];
+                $currentTime = time(); //当前时间
+                $data['status'] = 1;
+                $data['expire_time']=$currentTime + config('sms.expireTime');
+                $key=$phone;
+                $cacheKey=md5($key);
+                $cacheTime=config('cache.sms_time');
+                $this->setCacheData($cacheKey,$data,$cacheTime);
+                $result = $this->updatePhoneCode($data);
+                $resultData['id']=is_array($result)?0:$result;
+                $resultData['code']=$code;
+                $resultData['phone']=$phone;
+                $ret['data'] = $resultData;
+                return $ret;
+            }
+
+        } catch (\Exception $ex) {
+            Log::info('method:sendSMSInfo:' . $ex->getMessage());
+            Log::info('method:sendSMSInfo:' . $ex->getTraceAsString());
+            $ret['code']=1004;
+            $ret['message']=$ex->getMessage();
+            return $ret;
+            // throw new ApiException(500); //短信发送参数错误
+        }
+    }
+
+
+    /**
+     * 短信记录
+     * @param array $params 短信存储参数
+     * @return bool|mixed
+     * @throws \App\Exceptions\ApiException
+     */
+    public function updatePhoneCode($params = [])
+    {
+        //手机验证码信息
+        $codeData = [
+            'phone' => $params['phone'],
+            'code' => $params['code'],
+            'template_code' => $params['template_code'],
+            'status' => $params['status'],
+            'error' => empty($params['error']) ? '' : $params['error'],
+            'type' => empty($params['type']) ? 0 : $params['type'],
+            'create_time' => nowTime()
+        ];
+        if(!empty($params['expire_time'])){
+            $codeData['expire_time']= date('Y-m-d H:i:s', $params['expire_time']);
+        }
+        try {
+            $result=$this->model->insertGetId($codeData);
+            if (!$result) {
+                Log::info('method:updatePhoneCode:保存验证码失败');
+                $ret['code']=1004;
+                $ret['message']= '保存验证码失败';
+                return $ret;
+            }
+            return $result;
+        } catch (QueryException $ex) {
+            //异常处理
+            Log::info('method:updatePhoneCode:' . $ex->getMessage());
+            Log::info('method:updatePhoneCode:' . $ex->getTraceAsString());
+            $ret['code']=1004;
+            $ret['message']=$ex->getMessage();
+            return $ret;
+        }
+    }
+
+    /**
+     * 校验短信验证码是否有效
+     * @param string $phone 手机号
+     * @param string $code 短信验证码
+     * @return array  -1 失败重新获取验证码 -2验证码错误重新输入,1 成功是否验证成功
+     */
+    public  function validateCode($phone, $code)
+    {
+        //todo 通过redis 缓存来验证
+        $result=['code'=>-1];
+        $current_time = time();
+        $key=$phone;
+        $cacheKey=md5($key);
+        $cacheData=$this->getCacheData($cacheKey);
+        if($cacheData){
+            if(!empty($cacheData['expire_time'])&&$cacheData['expire_time']>$current_time&&$code==$cacheData['code']){
+                $result['code']=1;
+                $this->removeByKey($cacheKey);
+            }else if($code!=$cacheData['code']){
+                $result['code']=-2;
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * 校验短信验证码一分钟内是否重复发送
+     * */
+    public function validataPhoneMinute($phone,$type){
+        $result=['code'=>0];
+        $where=[
+            'phone' => $phone,
+            'type' => $type,
+            'status' => 1,
+        ];
+        $expireTime = date("Y-m-d H:i:s",strtotime("+4 minute",strtotime(nowTime()))); // 短信有效时间为5分钟。当前时间添加4分钟
+        $smsData = $this->model->where($where)->where('expire_time','>',$expireTime)->first();
+        if(!empty($smsData)){
+            $result['code']=-1;
+        }
+        return $result;
+    }
+
+    /**
+     * 通用缓存方法
+     * @param string $key
+     * @param array $data
+     * */
+    public function setCacheData($key, $data,$cacheTime=0)
+    {
+        $cacheTime=empty($cacheTime)?config('cache.def_time'):$cacheTime;
+        Cache::put($this->getCacheKey($key), $data, $cacheTime);
+    }
+
+
+    /**
+     * 获取通用缓存数据key
+     * @param string $key
+     * @return string
+     * */
+    private function getCacheKey($key)
+    {
+        $prefix='VERIFICATION';
+        return $this->cacheBucket.$prefix.$key;
+    }
+
+    /**
+     * 通用缓存方法
+     * @param string $key
+     * @param array $data
+     * */
+    public function getCacheData($key)
+    {
+        $dataKey= $this->getCacheKey($key);
+        return Cache::get($dataKey);
+    }
+}

+ 88 - 0
app/Common/Services/OssService.php

@@ -0,0 +1,88 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2018/4/12
+ * Time: 下午11:18
+ */
+namespace App\Common\Services;
+
+use App\Exceptions\ApiException;
+use App\Services\CommonBaseService;
+use OSS\Core\OssException;
+use OSS\OssClient;
+
+class OssService extends CommonBaseService
+{
+    /**
+     * 上传到云存储
+     * @param $filePath
+     * @param $fileName
+     * @param $etag
+     * @param string $type
+     * @return string
+     * @throws ApiException
+     */
+    public function uploadToOss($filePath, $fileName, &$etag, $type = 'share_center',$isReName=false)
+    {
+        if ($etag) {
+            $oss = $this->model->where([
+                'etag' => strtoupper($etag)
+            ])->first();
+            if ($oss) {
+                @unlink($filePath);
+                return $oss['url'];
+            }
+        }
+        $bucket = config('oss.bucketName');
+        $ext = pathinfo($fileName, PATHINFO_EXTENSION);
+        $oss = new OssClient(
+            config('oss.accessKeyId'),
+            config('oss.accessKeySecret'),
+            config('oss.ossServerInternal')
+        );
+        $disposition = 'attachment;filename=' . $fileName;
+        if (in_array(strtolower($ext), ['jpg', 'jpeg', 'gif', 'png'])) {
+            $disposition = "";
+        }
+        $options = [
+            OssClient::OSS_HEADERS => [
+                'Cache-Control' => 'max-age=365',
+                'Content-Disposition' => $disposition,
+//            'Content-Encoding' => 'utf-8',
+//            'Content-Language' => 'zh-CN',
+                'x-oss-server-side-encryption' => 'AES256',
+            ],
+        ];
+        if($isReName){
+            $object = config('oss.ossObject') .
+                "/$type/" . date('Y') . "/" . date('md') . "/" . uniqid() . ($ext ? "." . $ext : "");
+        }else{
+            $object = config('oss.ossObject') .
+                "/$type/" . date('Y') . "/" . date('md') . "/" .mt_rand(1000, 9999)."/". uniqid() . ($ext ? "/" . $fileName : "");
+        }
+        try {
+//        print_r([$bucket, $object, $filePath, $options]);die;
+            $response = $oss->uploadFile($bucket, $object, $filePath, $options);
+            $url = $response['info']['url']??'';
+            $url = str_replace(
+                config('oss.ossServerInternalHttp').$bucket
+                . "." . config('oss.ossServerInternal'),
+                config("oss.ossServerHttp") . config('oss.ossServer'),
+                $url
+            );
+            $this->save([
+                'etag' => trim($response['etag'], '"')??'',
+                'url' => $url,
+                'file_size' => filesize($filePath),
+            ]);
+            @unlink($filePath);
+            return $url;
+        } catch (OssException $e) {
+            throw new ApiException(40008, [
+                'message' => $e->getMessage(),
+                'code' => $e->getCode()
+            ]);
+        }
+    }
+}

Diff do ficheiro suprimidas por serem muito extensas
+ 88 - 0
app/Common/Services/PinYinService.php


+ 52 - 0
app/Common/Services/ShortyService.php

@@ -0,0 +1,52 @@
+<?php
+namespace App\Common\Services;
+
+class ShortyService
+{
+    private $host;
+
+    public function __construct()
+    {
+        $this->host = config('services.short_app.url');
+    }
+
+    /**
+     * 获取短地址url
+     */
+    public function getUrls($urls)
+    {
+        $host = $this->host . '/api/short';
+        $headers['X-Requested-With'] = 'XMLHttpRequest';
+        try {
+            $ret = httpClient('post', $host, [
+                "url" => $urls,
+                "from" => "independent_web_".config('app.name')
+            ], $headers);
+            $ret = json_decode($ret, true);
+        } catch (\Exception $e) {
+            \Log::info($e->getMessage());
+            return;
+        }
+        return $ret['data'] ?? [];
+    }
+
+    /**
+     * 获取短地址url访问量
+     */
+    public function getUrlsHits($urls)
+    {
+        $host = $this->host . '/api/short-hits';
+        $headers['X-Requested-With'] = 'XMLHttpRequest';
+        try {
+            $ret = httpClient('get', $host, [
+                "url" => $urls,
+                "from" => "independent_web_".config('app.name')
+            ], $headers);
+            $ret = json_decode($ret, true);
+        } catch (\Exception $e) {
+            \Log::info($e->getMessage());
+            return;
+        }
+        return $ret['data'] ?? [];
+    }
+}

+ 584 - 0
app/Common/Services/UploadFileService.php

@@ -0,0 +1,584 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2017/4/14
+ * Time: 11:38
+ */
+
+namespace App\Common\Services;
+
+
+use App\Exceptions\ApiException;
+use App\Models\BaseModel;
+use App\Services\CommonBaseService;
+use App\Common\Facades\OssFacade;
+use FFMpeg\Coordinate\TimeCode;
+use FFMpeg\FFMpeg;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Request;
+
+class UploadFileService extends CommonBaseService
+{
+
+    /**
+     * 文件mineType类型
+     * @var array
+     */
+    private $mineTypeList = [];
+
+    /**
+     * 文件扩展名
+     * @var array
+     */
+    private $fileExt = ['png', 'jpg', 'jpeg', 'gif', 'mp4', 'mp3', 'ico', '3gpp', 'ac3', '3gpp', 'ac3',
+        'asf', 'au', 'css', 'csv', 'doc', 'dot', 'dtd', 'dwg', 'dxf', 'gif', 'jp2', 'jpe',
+        'mp2', 'mp3', 'mp4', 'mpeg', 'mpg', 'mpp', 'ogg', 'pdf', 'pot', 'pps', 'ppt', 'rtf', 'svf',
+        'tif', 'tiff', 'txt', 'wdb', 'wps', 'xhtml', 'xlc', 'xlm', 'xls', 'xlt', 'xlw', 'xml',
+        'zip', 'xlsx','docx','webp'];
+
+    /**
+     * 文件扩展名
+     * @var string
+     */
+    private $nowFileExt = '';
+
+    /**
+     * 文件限制大小,默认20M
+     * @var int
+     */
+    private $limitSize = 1024 * 1024 * 100;
+
+    /**
+     * 保存的路径
+     * @var string
+     */
+    private $basePath = './';
+    /**
+     * 设置文件路径
+     * @var string
+     */
+    private $filePath = '/files';
+    /**
+     * 上传文件字段
+     * @var string
+     */
+    private $fileField = 'file';
+
+    public function __construct(BaseModel $model)
+    {
+        parent::__construct($model);
+        if(config('app.upload_limit_size')) {
+            $this->limitSize = 1024 * 1024 * config('app.upload_limit_size');
+        }
+    }
+
+    /**
+     * 获取本地路径
+     * @param $file
+     * @return string
+     */
+    public function getLocalPath($file = '')
+    {
+        $resourcesPath = 'upload';
+        if(config('app.language_path')) {
+            if(config('app.language_path') != 'zh-cn') {
+                $resourcesPath = config('app.language_path').'/upload';
+            }
+        }
+        return  resource_path($resourcesPath) . $file;
+    }
+
+    /**
+     * 上传图片文件
+     * @param $request
+     * @param string $fileField
+     * @param array|string $fileExt
+     * @param bool $isUploadOss
+     * @return array
+     */
+    public function upload($request, $fileField = 'file', $fileExt = [], $isUploadOss = true,$isReName=false)
+    {
+        $this->fileField = $fileField;
+        if (!empty($fileExt)) {
+            $this->setFileExt($fileExt);
+        }
+        $this->validate($request);
+        $result = $this->saveLocal($request,$isReName);
+        $localPath=$this->getLocalPath().$result['path'];
+        if (strpos($result['mimeType'], 'image') !== false) {
+            list($width, $height) = getimagesize($localPath);
+            $result['resolution'] = $width . '*' . $height;
+        }
+        $resourcesPath = '/resources';
+        if(config('app.language_path')) {
+            if(config('app.language_path') != 'zh-cn') {
+                $resourcesPath = $resourcesPath.'/'.config('app.language_path');
+            }
+        }
+        $result['url'] = $request->root() .$resourcesPath. $result['path'];
+        $path = $this->getLocalPath() . $this->filePath;
+        $useFfmpeg= config('app.USE_FFMPEG');
+        if ($useFfmpeg&&!empty($result['mimeType'])&&strpos($result['mimeType'], 'video') !== false) {
+            $ffmpeg=FFMpeg::create(array(
+                'ffmpeg.binaries'  => config('app.ffmpeg.ffmpeg_binaries'),
+                'ffprobe.binaries' => config('app.ffmpeg.ffprobe_binaries'),
+                'timeout'          =>config('app.ffmpeg.timeout'), // The timeout for the underlying process
+                'ffmpeg.threads'   => config('app.ffmpeg.ffmpeg_threads'),   // The number of threads that FFMpeg should use
+            ));
+            $picFilename = $this->getFilename($path,'jpg');
+            $video = $ffmpeg->open($localPath);
+
+            $fileSavePath=$path.'/'.$picFilename;
+            $video->frame(TimeCode::fromSeconds(1))->save($fileSavePath);
+            if(file_exists($fileSavePath)){
+                $checkFileNameStr=str_replace('.'.$result['ext'],'',$picFilename);
+                if (preg_match('/^.*[,\.#%\'\+\*\:;^`\{\}\(\)\[\]\s]/', $checkFileNameStr)) {
+                    $picFilename=  preg_replace('/[,\.#%\'\+\*\:;^`\{\}\(\)\[\]\s]/','-', $checkFileNameStr).'.'.$result['ext'];
+                }
+                $coverOssUrl = OssFacade::uploadToOss($fileSavePath, $picFilename, md5_file($fileSavePath),'share_center',$isReName);
+                $result['pic']=$coverOssUrl;//封面图
+            }
+        }
+        if ($isUploadOss) {
+            $filename = $result['original_filename'];
+            $checkFileNameStr=str_replace('.'.$result['ext'],'',$filename);
+            if (preg_match('/^.*[,\.#%\'\+\*\:;^`\{\}\(\)\[\]\s]/', $checkFileNameStr)) {
+                $filename=  preg_replace('/[,\.#%\'\+\*\:;^`\{\}\(\)\[\]\s]/','-', $checkFileNameStr).'.'.$result['ext'];
+            }
+            $ossUrl = OssFacade::uploadToOss($localPath, $filename, md5(file_get_contents($localPath)),'share_center',$isReName);
+            $result['url'] = $ossUrl;
+            //Log::info(json_encode($result));
+            if (!empty($result['mimeType'])&&strpos($result['mimeType'], 'video') !== false) {
+                // 视频截图
+                $picFilename = $this->getFilename($path,'jpg');
+                $fileSavePath = $path.'/'.$picFilename;
+                downloadFile($ossUrl.'?x-oss-process=video/snapshot,t_1000,f_jpg,w_800,h_600,m_fast', $fileSavePath);
+                if (file_exists($fileSavePath)) {
+                    $coverOssUrl = OssFacade::uploadToOss($fileSavePath, $picFilename, md5_file($fileSavePath),'share_center',$isReName);
+                    $result['pic']=$coverOssUrl;//封面图
+                }
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * 上传Base64图片
+     * @param $request
+     * @param string $fileField
+     * @param array|string $fileExt
+     * @param bool $isUploadOss
+     * @return array
+     */
+    public function uploadImgBase64($data, $fileExt = 'jpg', $host='',$selectFileName='',$isUploadOss = true)
+    {
+        if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $data, $result)) {
+            $type = $result[2];
+            if (in_array($type, array('pjpeg', 'jpeg', 'jpg', 'gif', 'bmp', 'png'))) {
+                $this->nowFileExt = strtolower($type);
+                $path =  $this->filePath;//$this->basePath.$this->filePath;
+                $path.="/" .date('md') . "/".uniqid()."/";
+                $localPath=$this->getLocalPath().$path;
+                if(empty($selectFileName)){
+                    $filename = $this->getFilename($localPath);
+                }else{
+                    $filename=  $selectFileName.'.'.$this->nowFileExt;
+                }
+
+                $filepath = $localPath . '/' . $filename;
+                if (!file_exists($localPath)) {
+                    mkdir($localPath, 0777, true);
+                }
+                if (file_put_contents($filepath, base64_decode(str_replace($result[1], '', $data)))) {
+                    $size = filesize($filepath);
+                    $s = getimagesize($filepath);
+                    $width = empty($s[0]) ? '' : $s[0];
+                    $height = empty($s[1]) ? '' : $s[1];
+                    $result = [
+                        'path' => $path.$filename,
+                        'size' => $size,
+                        'filename' => $filename,
+                        'original_filename' => $filename,
+                        'ext' => $this->nowFileExt,
+                        'resolution' => $width . '*' . $height,
+                        'url' => $host.'/resources'.$path.$filename,
+                        'mimeType' => 'image/' . $this->nowFileExt
+                    ];
+                    if ($isUploadOss) {
+                        $ossUrl = OssFacade::uploadToOss($filepath, $filename, md5(file_get_contents($filepath)));
+                        $result['url'] = $ossUrl;
+                    }
+                    return $result;
+//                    echo '图片上传成功</br><img src="' .$img_path. '">';
+                } else {
+                    throw new ApiException(1101);
+
+                }
+            } else {
+                //文件类型错误
+                throw new ApiException(1101);
+            }
+
+        } else {
+            //文件错误
+            throw new ApiException(1101);
+        }
+        return false;
+    }
+
+    /**
+     * 上传文本转为文件
+     * @param $request
+     * @param string $fileField
+     * @param array|string $fileExt
+     * @param bool $isUploadOss
+     * @return array
+     */
+    public function uploadTextToFile($data, $fileExt = '', $isUploadOss = true)
+    {
+        if (!empty($fileExt)) {
+            $this->nowFileExt = strtolower($fileExt);
+            $path = $this->getLocalPath() . $this->filePath;
+            $filename = $this->getFilename($path);
+            $filepath = $path . '/' . $filename;
+            if (!file_exists($path)) {
+                mkdir($path, 0777, true);
+            }
+            if (file_put_contents($filepath, $data)) {
+                $size = filesize($filepath);
+                $s = getimagesize($filepath);
+                $result = [
+                    'path' => $filepath,
+                    'size' => $size,
+                    'filename' => $filename,
+                    'original_filename' => $filename,
+                    'ext' => $this->nowFileExt,
+                    'url' => ''
+                ];
+                if ($isUploadOss) {
+                    $ossUrl = OssFacade::uploadToOss($filepath, $filename, md5(file_get_contents($filepath)));
+                    $result['url'] = $ossUrl;
+                }
+                return $result;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 设置扩展名
+     * @param array $fileExt
+     * @return $this
+     */
+    public function setFileExt($fileExt)
+    {
+        if (empty($fileExt)) {
+            return;
+        }
+        if (!is_array($fileExt)) {
+            $fileExt = explode(',', $fileExt);
+        }
+        $this->fileExt = $fileExt;
+        return $this;
+    }
+
+    /**
+     * 上传验证
+     * @param $request
+     * @return bool
+     * @throws ApiException
+     */
+    private function validate($request)
+    {
+        $fileInfo = $request->file($this->fileField);
+//        if (!$request->hasFile($this->fileField) || !$fileInfo->isValid()) {
+//            throw new ApiException(30002);
+//        }
+        if (!$this->checkFileExt($fileInfo)) {
+            throw new ApiException(1101);
+        }
+        if (!$this->checkLimitSize($fileInfo)) {
+            $limit = floor($this->limitSize / (1024 * 1024)) . 'M';
+            throw new ApiException(1102, ['limit' => $limit]);
+        }
+        $this->checkDirectory($this->filePath);
+        return true;
+    }
+
+    /**
+     * 检查文件扩展名
+     * @param $fileInfo
+     * @return bool
+     */
+    private function checkFileExt($fileInfo)
+    {
+        $fileType = strtolower($fileInfo->getClientOriginalExtension());
+        if (!$fileType || !in_array($fileType, $this->fileExt)) {
+            return false;
+        }
+        $this->nowFileExt = strtolower($fileType);
+        return true;
+    }
+
+    /**
+     * 验证文件大小
+     * @param $fileInfo
+     * @return bool
+     */
+    private function checkLimitSize($fileInfo)
+    {
+        $clientSize = $fileInfo->getSize();
+        if ($clientSize > $this->limitSize) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * 检验文件夹,不存在则创建
+     * @param $path
+     */
+    private function checkDirectory($path)
+    {
+        $folders = explode('/', $path);
+        $basePath = $this->getLocalPath();//$this->basePath;
+        foreach ($folders as $item) {
+            if (empty($item)) {
+                continue;
+            }
+            if (strrpos($basePath, '/') !== strlen($basePath) - 1) {
+                $basePath .= '/';
+            }
+            $basePath .= $item;
+            $this->makeDir($basePath);
+        }
+    }
+
+    /**
+     * 新增文件目录
+     * @param $path
+     * @return mixed
+     */
+    private function makeDir($path)
+    {
+        if (is_dir($path)) {
+            return $path;
+        } else {
+            mkdir($path, 0777, true);
+            return $path;
+        }
+    }
+
+    /**
+     * 上传保存到本地
+     * @param $request
+     * @return array
+     */
+    private function saveLocal($request,$isReName=false)
+    {
+        $fileInfo = $request->file($this->fileField);
+
+        $path = $this->filePath;//$this->basePath.$this->filePath;
+        $path.="/" .date('md') . "/";
+        $OldFilename = $fileInfo->getClientOriginalName();
+        if($isReName){
+            $localPath=$this->getLocalPath().$path;
+            $filename = $this->getFilename($localPath);
+        }else{
+            $path.= uniqid()."/";
+            $localPath=$this->getLocalPath().$path;
+            $filename= $OldFilename;
+        }
+        $fileSize = $fileInfo->getSize();
+        $mimeType = $fileInfo->getMimeType();
+        $fileInfo->move($localPath, $filename);
+//        Log::info($this->filePath.'/'.$filename);
+        return [
+            'path' => $path.$filename,
+            'size' => $fileSize,
+            'filename' => $filename,
+            'original_filename' => $OldFilename,
+            'ext' => $this->nowFileExt,
+            'mimeType' => $mimeType
+        ];
+    }
+
+    /**
+     * 获取文件名
+     * @param $path
+     * @return string
+     */
+    private function getFilename($path,$ext='')
+    {
+        $suffix=empty($ext)? $this->nowFileExt:$ext;
+        $fileName = date('YmdHis') . mt_rand(100, 999) . '.' . $suffix;
+        if (file_exists($path .'/'. $fileName)) {
+            return $this->getFileName($path,$ext);
+        }
+        return $fileName;
+    }
+
+    /**
+     * 设置文件路径
+     * @param string $path
+     * @return $this
+     */
+    public function setFilePath($path)
+    {
+        $this->filePath = $path;
+        return $this;
+    }
+
+    /**
+     * 设置文件大小
+     * @param string $maxSize
+     * @return $this
+     */
+    public function setMaxSize($maxSize = '')
+    {
+        if ($maxSize && is_numeric($maxSize)) {
+            $this->limitSize = $maxSize;
+        }
+        return $this;
+    }
+
+    /**
+     * 设置文件mineType
+     * @param array $mineType
+     * @return $this
+     */
+    public function setMineType($mineType = [])
+    {
+        if ($mineType) {
+            $this->mineTypeList = $mineType;
+        }
+        return $this;
+    }
+
+    /**
+     * 上传远程文件
+     * */
+    public function uploadRemoteImg($remoteUrl, $isReName = false)
+    {
+        $ossUrl = '';
+        $ext = getExtFromUrl($remoteUrl);
+        if (!empty($remoteUrl) && in_array($ext, $this->fileExt)) {
+            $path = $this->filePath;
+            $path .= "/" . date('md') . "/";
+            $OldFilename = basename($remoteUrl);
+            //如果包含中文则重命名
+            if(preg_match('/[\x{4e00}-\x{9fa5}]/u', $OldFilename)) {
+                $py = app()->make(PinYinService::class);
+                $newName = strtolower($py->getAllPY($OldFilename));
+                if(!empty($newName)) {
+                    $OldFilename = preg_replace('/[^a-zA-Z0-9_u4e00-u9fa5]/', '-', $newName).'.'.$ext;
+                } else {
+                    $isReName = true;
+                }
+            }
+            if ($isReName) {
+                $localPath = $this->getLocalPath().$this->filePath;
+                $path .= uniqid() . "/";
+                $filename = $this->getFilename($localPath, $ext);
+            } else {
+                $path .= uniqid() . "/";
+                $filename = $OldFilename;
+            }
+            $realPath = $this->getLocalPath().$path;
+            if (!file_exists($realPath)) {
+                mkdir($realPath, 0777, true);
+            }
+            $saveToUrl = $realPath . $filename;
+            if (str_contains($remoteUrl, 'data:image/png;')) {
+                try {
+                    // 分离出数据
+                    list($type, $data) = explode(';', $remoteUrl);
+                    list(, $data) = explode(',', $data);
+                    // 将Base64字符串解码成二进制数据
+                    $image = base64_decode($data);
+                    // 指定文件名和保存路径
+                    // 保存文件
+                    file_put_contents($saveToUrl, $image);
+                    $ossUrl = $path.$filename;
+                } catch (\Exception $e) {
+
+                }
+            } else {
+                if ((strpos($remoteUrl, "http://") !== false || strpos($remoteUrl, "https://") !== false)) {
+                    $remoteUrl = preg_replace('/\?v=.*/', '', $remoteUrl);
+                    $saveToUrl = mb_convert_encoding($saveToUrl, "UTF-8");
+                    downloadRemoteFile($remoteUrl, $saveToUrl);
+                    $ossUrl = $path.$filename;
+                }
+            }
+        }
+        if ($ossUrl) {
+            $ossUrl = Request::root() . '/resources' . $ossUrl;
+        }
+        return $ossUrl;
+    }
+
+    /**
+     * 上传图片文件
+     * @param $request
+     * @param string $fileField
+     * @param array|string $fileExt
+     * @param bool $isUploadOss
+     * @return array
+     */
+    public function uploadCustomImage($request, $fileField = 'file', $fileExt = [], $isUploadOss = true, $isReName = false)
+    {
+        $this->fileField = $fileField;
+        if (!empty($fileExt)) {
+            $this->setFileExt($fileExt);
+        }
+        $this->validateCustom($request);
+        $result = $this->saveCustomLocal($request);
+        $resourcesPath = '/resources';
+        if(config('app.language_path')) {
+            if(config('app.language_path') != 'zh-cn') {
+                $resourcesPath = $resourcesPath.'/'.config('app.language_path');
+            }
+        }
+        $result['url'] = $request->root() . $resourcesPath . $result['path'];
+        return $result;
+    }
+
+    private function validateCustom($request)
+    {
+        $fileInfo = $request->file($this->fileField);
+        if (!$this->checkFileExt($fileInfo)) {
+            throw new ApiException(1101);
+        }
+        $clientSize = $fileInfo->getSize();
+        $limitSize = 1024 * 1024 * 5;
+        if ($clientSize > $limitSize) {
+            throw new ApiException(1102, ['limit' => 5]);
+        }
+        $this->checkDirectory($this->filePath);
+        return true;
+    }
+
+    private function saveCustomLocal($request)
+    {
+        $fileInfo = $request->file($this->fileField);
+        $path = '/custom_files';//$this->basePath.$this->filePath;
+        $path .= "/" . date('Ymd') . "/".strtolower(uniqid())."/";
+        $OldFilename = $fileInfo->getClientOriginalName();
+        $localPath = $this->getLocalPath() . $path;
+        $filename = $this->getFilename($localPath);
+        $fileSize = $fileInfo->getSize();
+        $mimeType = $fileInfo->getMimeType();
+        $fileInfo->move($localPath, $filename);
+        return [
+            'path' => $path . $filename,
+            'size' => $fileSize,
+            'filename' => $filename,
+            'original_filename' => $OldFilename,
+            'ext' => $this->nowFileExt,
+            'mimeType' => $mimeType
+        ];
+    }
+}

+ 942 - 0
app/Common/Services/UserCenterService.php

@@ -0,0 +1,942 @@
+<?php
+
+namespace App\Common\Services;
+
+use App\Exceptions\ApiException;
+use Illuminate\Support\Facades\Log;
+
+class UserCenterService
+{
+    private $appCode;
+    private $appKey;
+    private $verKey;
+    private $operationKey;
+    private $url;
+
+    public function __construct()
+    {
+        $config = config('api.user_center_config');
+        $this->appCode = $config['app_code'];
+        $this->appKey = $config['app_key'];
+        $this->verKey = $config['ver_key'];
+        $this->url = $config['url'];
+        $this->operationKey = $config['operation_key'];
+        //Log::info(json_encode($config));
+    }
+
+    /**
+     * http请求
+     * @param $method 请求方式 GET|POST|PUT|DELETE
+     * @param $url 请求地址
+     * @param array $params 请求参数
+     * @param array $headers 请求带的header头
+     * @return string 返回数据
+     * @throws ApiException
+     */
+    public function request($method, $url, $params = [], $headers = [])
+    {
+        $headers['X-Requested-With'] = 'XMLHttpRequest';
+        return httpClient($method, $url, $params, $headers);
+    }
+
+    /**
+     * 登录
+     * @param $username
+     * @param $password
+     * @param string $redirect
+     * @param string $checkCode
+     * @return array
+     * @throws ApiException
+     */
+    public function pwdLogin($username, $password)
+    {
+        $ip = getClientIp(0, true);
+        $res = $this->request('post', $this->url . 'app/user/login', [
+            'user_name' => $username,
+            'password' => $password,
+            'app_code' => $this->appCode,
+            'app_key' => $this->appKey,
+            'ip' => $ip
+        ]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 是否返回成功
+     * @param $res
+     * @return bool
+     */
+    private function isSuccess($res)
+    {
+        if (!empty($res) && is_array($res) && $res['code'] == 0) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 登出
+     * @param $apiToken
+     * @return boolean
+     * @throws ApiException
+     */
+    public function logout($apiToken)
+    {
+
+        $res = $this->request('post', $this->url . 'app/user/logout', ['api_token' => $apiToken], ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 获取主帐号详情
+     * @return mixed|string
+     * @throws ApiException
+     */
+    public function getMasterSysUserInfo($apiToken)
+    {
+        $res = $this->request('post', $this->url . 'app/user/master-user-info', [], ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 获取帐号详情
+     * @return mixed|string
+     * @throws ApiException
+     */
+    public function getSysUserInfo($apiToken)
+    {
+        $res = $this->request('post', $this->url . 'app/user/base-info', [
+            'app_code' => $this->appCode
+        ], ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 授权接口
+     * @param $token
+     * @return array
+     * @throws ApiException
+     */
+    public function getAuth($apiToken)
+    {
+        $res = $this->request('post', $this->url . 'app/user/get-auth', [
+            'app_code' => $this->appCode,
+            'app_key' => $this->appKey
+        ], ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 心跳接口
+     * @param $apiToken
+     * @return mixed|string
+     * @throws ApiException
+     */
+    public function heartBeat($apiToken, $throwError = false)
+    {
+        $res = $this->request('post', $this->url . 'app/user/heart-beat', [], ['api-token' => $apiToken]);
+        //file_put_contents(storage_path('logs/hearBeat.log'), $res."\r\n", FILE_APPEND);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 修改密码
+     * @param integer $type 0 根据帐号 1根据手机 2根据旧邮箱
+     * @param string $password 密码
+     * @param string $confirm_password 确认密码
+     * @param string $oldPassword 旧密码 类型(0)必填
+     * @param string $validCode 验证码 类型(1、2)必填
+     * @return mixed|string|array
+     * @throws ApiException
+     */
+    public function updatePwd($type, $password, $confirm_password, $oldPassword = '', $validCode = '', $apiToken)
+    {
+        $res = $this->request('post', $this->url . 'app/user/update-pwd', [
+            'type' => $type,
+            'password' => $password,
+            'confirm_password' => $confirm_password,
+            'old_password' => $oldPassword,
+            'valid_code' => $validCode,
+        ], ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 手机号查看用户是否存在
+     * @param $phone
+     * @return mixed
+     * @throws ApiException
+     */
+    public function checkSysUserPhoneUnique($phone, $throwError = true)
+    {
+        $res = $this->request('post', $this->url . 'app/user/check-client-phone-unique', [
+            'phone' => $phone
+        ]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            if ($throwError) {
+                $message = empty($resData['message']) ? '' : $resData['message'];
+                throw new ApiException(1004,['msg'=> $message]);
+            } else {
+                //10012 手机号已被注册
+                return $resData;
+            }
+        }
+    }
+
+    /**
+     * 检查注册用户名是否存在 暂时不开发用户名注册
+     * @param $phone
+     * @return mixed
+     * @throws ApiException
+     */
+    public function checkSysUserUserNameUnique($userName, $throwError = true)
+    {
+        $res = $this->request('post', $this->url . 'app/user/check-client-user-name-unique', [
+            'user_name' => $userName
+        ]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            if ($throwError) {
+                $message = empty($resData['message']) ? '' : $resData['message'];
+                throw new ApiException(1004,['msg'=> $message]);
+            } else {
+                return $resData;
+            }
+        }
+    }
+
+    /**
+     * 检查注册邮箱是否存在
+     * @param $phone
+     * @return mixed
+     * @throws ApiException
+     */
+    public function checkSysUserEmailUnique($email, $throwError = true)
+    {
+        $res = $this->request('post', $this->url . 'app/user/check-client-email-unique', [
+            'email' => $email
+        ]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            if ($throwError) {
+                $message = empty($resData['message']) ? '' : $resData['message'];
+                throw new ApiException(1004,['msg'=> $message]);
+            } else {
+                //10014 邮箱已被注册
+                return $resData;
+            }
+        }
+    }
+
+    /**
+     * 检查帐号是否为主帐号
+     * @param $phone
+     * @return mixed
+     * @throws ApiException
+     */
+    public function checkSysUserIsMaster($params, $throwError = true)
+    {
+        $checkData = [];
+        if (!empty($params['user_name'])) {
+            $checkData['user_name'] = $params['user_name'];
+        }
+        if (!empty($params['email'])) {
+            $checkData['email'] = $params['email'];
+        }
+        if (!empty($params['phone'])) {
+            $checkData['phone'] = $params['phone'];
+        }
+        $res = $this->request('post', $this->url . 'app/user/check-client-user-is-master', $checkData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            if ($throwError) {
+                $message = empty($resData['message']) ? '' : $resData['message'];
+                throw new ApiException(1004,['msg'=> $message]);
+            } else {
+                return $resData;
+            }
+        }
+    }
+
+    /**
+     * 获取短信验证码
+     * @param $phone
+     * @param $type
+     * @return mixed
+     * @throws ApiException
+     */
+    public function getSmsCode($phone,$countryCode, $type = 0)
+    {
+        $res = $this->request('get', $this->url . 'common/get-sms-code', [
+            'phone' => $phone,
+            'type' => $type,
+            'country_code' => $countryCode,
+        ]);
+        // $res = '{"code":0,"message":"","data":1}';
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 获取短信验证码
+     * @param $email
+     * @return mixed
+     * @throws ApiException
+     */
+    public function getEmailCode($email,$language='zh-cn',$footer='')
+    {
+        $res = $this->request('get', $this->url . 'common/get-email-code', [
+            'email' => $email,
+            'language' => $language,
+            'footer' => $footer,
+        ]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            if($resData['code']==10021) {
+                throw new ApiException('common.email_empty', '邮箱不能为空');
+            }
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 登录
+     * @param $username
+     * @param $password
+     * @param string $redirect
+     * @param string $checkCode
+     * @return array ['status'=>1,'redirect'=>'']
+     * @throws ApiException
+     */
+    public function phoneLogin($phone, $validCode,$countryCode=86)
+    {
+        $ip = getClientIp(0, true);
+        $res = $this->request('post', $this->url . 'app/user/phone-login', [
+            'phone' => $phone,
+            'valid_code' => $validCode,
+            'country_code' => $countryCode,
+            'app_code' => $this->appCode,
+            'app_key' => $this->appKey,
+            'ip' => $ip
+        ]);
+        // $res = '{"code":0,"message":"","data":{"api_token":"d3506997fe83325eb0cca61de31737d8","is_admin":1,"expire":604800,"nick_name":"李肖明","user_name":"lixming","real_name":"","company_id":10327,"invalid_time":"2028-04-15 23:59:59"}}';
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 获取子帐号
+     * @param $companyId
+     * @return array
+     * @throws ApiException
+     */
+    public function getSysUserSubUser($keyword, $apiToken)
+    {
+        $res = $this->request('post', $this->url . 'app/user/get-client-sub-user', ['keyword' => $keyword], ['api-token' => $apiToken]);
+        //file_put_contents(storage_path('logs/aaa.log'), $res."\r\n", FILE_APPEND);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 修改子帐号
+     * @param array $params id nick_name password real_name status
+     * @param $apiToken
+     * @return array
+     * @throws ApiException
+     */
+    public function updateSubAccount($params = [], $apiToken)
+    {
+        $res = $this->request('post', $this->url . 'app/user/update-sub-account', $params, ['api-token' => $apiToken]);
+        //file_put_contents(storage_path('logs/aaa.log'), $res."\r\n", FILE_APPEND);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 删除
+     * @param integer $sysUserId 用户中心id
+     * @return array
+     * @throws ApiException
+     */
+    public function delSubAccount($sysUserId, $apiToken)
+    {
+        $res = $this->request('post', $this->url . 'app/user/del-sub-account', ['id' => $sysUserId], ['api-token' => $apiToken]);
+        //file_put_contents(storage_path('logs/aaa.log'), $res."\r\n", FILE_APPEND);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 添加子账号
+     * @param array params 子帐号参数
+     * @param array params.user_name 帐号
+     * @param array params.nick_name 昵称
+     * @param array params.password 密码
+     * @return array
+     * @throws ApiException
+     */
+    public function addSubAccount($params = [], $apiToken)
+    {
+        $subAccountData = [];
+        $subAccountData['user_name'] = $params['user_name'];
+        $subAccountData['nick_name'] = !empty($params['nick_name']) ? $params['nick_name'] : '';
+        $subAccountData['password'] = $params['password'];
+        $res = $this->request('post', $this->url . 'app/user/add-sub-account', $subAccountData, ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 注册用户
+     * @param array params 注册用户参数
+     * @param string params.password 密码
+     * @param string params.confirm_password 确认密码
+     * @param string params.phone 手机
+     * @param string params.email 邮箱
+     * @param string params.valid_code 验证码
+     * @param string params.nick_name 昵称
+     * @param string params.app_code 应用code
+     * @param string params.app_key 应用key
+     * @param string params.ver_Key 应用版本
+     * @param integer params.auto_login 自动登录
+     * */
+    public function registerSysUser($params)
+    {
+        $nowTime = nowTime();
+        $registerData = [];
+        if (!empty($params['password'])) {
+            $registerData['password'] = $params['password'];
+        }
+        if (!empty($params['confirm_password'])) {
+            $registerData['confirm_password'] = $params['confirm_password'];
+        }
+        if (!empty($params['phone'])) {
+            $registerData['phone'] = $params['phone'];
+        }
+        if (!empty($params['email'])) {
+            $registerData['email'] = $params['email'];
+        }
+        if (!empty($params['valid_code'])) {
+            $registerData['valid_code'] = $params['valid_code'];
+        }
+        if (!empty($params['nick_name'])) {
+            $registerData['nick_name'] = $params['nick_name'];
+        }
+        if (isset($params['auto_login'])) {
+            $registerData['auto_login'] = empty($params['auto_login']) ? 0 : $params['auto_login'];
+        }
+        $registerData['app_code'] = $this->appCode;
+        $registerData['app_key'] = $this->appKey;
+        $registerData['ver_Key'] = $this->verKey;
+        $registerData['start_time'] = $nowTime;
+        $registerData['end_time'] = '2099-12-31 23:59:59';
+        $res = $this->request('post', $this->url . 'app/user/register', $registerData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 忘记密码 获取重置密码的token
+     * @param array params 获取重置密码的token
+     * @param integer params.type 类型 1手机 2邮箱
+     * @param integer params.valid_code 验证码
+     * @param string params.email 邮箱
+     * @param string params.phone 手机
+     * */
+    public function getResetPwdToken($params)
+    {
+        $resetParams = [];
+        $resetParams['type'] = $params['type'];
+        $resetParams['valid_code'] = $params['valid_code'];
+        if (!empty($params['email'])) {
+            $resetParams['email'] = $params['email'];
+        }
+        if (!empty($params['phone'])) {
+            $resetParams['phone'] = $params['phone'];
+        }
+        $res = $this->request('post', $this->url . 'app/user/reset-pwd-validate', $resetParams);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 根据reset_token 重置密码
+     * @param string password 密码
+     * @param string confirm_password 确认密码
+     * @param string reset_token 重置tokem
+     * */
+    public function resetPasswordByToken($params)
+    {
+        $resetParams = [];
+        $resetParams['password'] = $params['password'];
+        $resetParams['confirm_password'] = $params['confirm_password'];
+        $resetParams['reset_token'] = $params['reset_token'];
+        $res = $this->request('post', $this->url . 'app/user/reset-pwd', $resetParams);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 绑定应用
+     * @param string apiToken
+     * */
+    public function appBind($apiToken)
+    {
+        $nowTime = nowTime();
+        $bindData = [];
+        $bindData['app_code'] = $this->appCode;
+        $bindData['app_key'] = $this->appKey;
+        $bindData['ver_key'] = $this->verKey;
+        $bindData['start_time'] = $nowTime;
+        $bindData['end_time'] = '2099-12-31 23:59:59';
+        $res = $this->request('post', $this->url . 'app/user/app-bind', $bindData, ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 绑定应用
+     * @param string apiToken
+     * */
+    public function appBindById($params)
+    {
+        $bindData = [];
+        $bindData['app_code'] = $params['app_code'];
+        $bindData['app_key'] = $params['app_key'];
+        $bindData['ver_key'] =  $params['ver_key'];
+        $bindData['start_time'] = $params['start_time'];;
+        $bindData['end_time'] = $params['end_time'];;
+        $bindData['user_id'] = $params['user_id'];;
+        $bindData['operation_key'] = $this->operationKey;
+        $res = $this->request('post', $this->url . 'app/user/app-bind-by-id', $bindData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 绑定应用
+     * @param string apiToken
+     * */
+    public function appBindUp($params,$throw=true)
+    {
+        $bindData = [];
+        $bindData['app_code'] = $this->appCode;
+        $bindData['app_key'] = $this->appKey;
+        $verKey=$params['ver_key'];
+        $bindData['ver_key'] = $verKey;
+        $bindData['bind_type'] =$params['bind_type'];//bind_type 绑定类型 0 新增 1续费 2升级 3降级
+        $bindData['user_id'] =$params['user_id'];//bind_type 绑定类型 0 新增 1续费 2升级 3降级
+        $bindData['operation_key'] = $this->operationKey;
+        if(isset($params['start_time'])){
+            $bindData['start_time'] = $params['start_time'];
+        }
+        if(isset($params['end_time'])){
+            $bindData['end_time'] = $params['end_time'];
+        }
+        $res = $this->request('post', $this->url . 'app/user/manage-up-app-bind', $bindData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            if($throw){
+                $message = empty($resData['message']) ? '' : $resData['message'];
+                throw new ApiException(1004,['msg'=> $message]);
+            }else{
+                return false;
+            }
+
+        }
+    }
+
+    /**
+     * 绑定应用
+     * @param string apiToken
+     * */
+    public function addExpandPacket($params)
+    {
+        $bindData = [];
+        $bindData['app_code'] = $this->appCode;
+        $bindData['app_key'] = $this->appKey;
+        $bindData['packet_key'] = $params['packet_key'];
+        $bindData['start_time'] =$params['start_time'];
+        $bindData['user_id'] =$params['user_id'];
+        $bindData['operation_key'] = $this->operationKey;
+        $res = $this->request('post', $this->url . 'app/user/add-expand-packet', $bindData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 变更绑定手机获取变更绑定的reset_token
+     * @param array params 获取重置密码的token
+     * @param integer params.type 类型 1当前手机 2邮箱
+     * @param string params.valid_code 验证码
+     * */
+    public function getBindPhoneResetToken($params, $apiToken)
+    {
+        $resetParams = [];
+        $resetParams['type'] = $params['type'];
+        $resetParams['valid_code'] = $params['valid_code'];
+        $res = $this->request('post', $this->url . 'app/user/get-bind-phone-reset-token', $resetParams, ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 绑定手机
+     * @param array params 获取重置密码的token
+     * @param integer params.type 类型 1手机 2邮箱
+     * @param string params.valid_code 验证码
+     * */
+    public function userPhoneBind($params, $apiToken)
+    {
+        $resetParams = [];
+        $resetParams['phone'] = $params['phone'];
+        $resetParams['valid_code'] = $params['valid_code'];
+
+        if (isset($params['reset_token'])) {
+            $resetParams['reset_token'] = $params['reset_token'];
+        }
+        $res = $this->request('post', $this->url . 'app/user/bind-phone', $resetParams, ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 变更绑定邮箱获取变更绑定的reset_token
+     * @param array params 获取重置密码的token
+     * @param integer params.type 类型 1当前手机 2邮箱
+     * @param string params.valid_code 验证码
+     * */
+    public function getBindEmailResetToken($params, $apiToken)
+    {
+        $resetParams = [];
+        $resetParams['type'] = $params['type'];
+        $resetParams['valid_code'] = $params['valid_code'];
+        $res = $this->request('post', $this->url . 'app/user/get-bind-email-reset-token', $resetParams, ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 绑定邮箱
+     * @param array params 获取重置密码的token
+     * @param integer params.type 类型 1当前手机 2邮箱
+     * @param string params.valid_code 验证码
+     * */
+    public function userEmailBind($params, $apiToken)
+    {
+        $resetParams = [];
+        $resetParams['email'] = $params['email'];
+        $resetParams['valid_code'] = $params['valid_code'];
+        if (isset($params['reset_token'])) {
+            $resetParams['reset_token'] = $params['reset_token'];
+        }
+        $res = $this->request('post', $this->url . 'app/user/bind-email', $resetParams, ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 子帐号修改密码
+     * */
+    public function subAccountUpdatePwd($params, $apiToken)
+    {
+        $updateParams = [];
+        $updateParams['password'] = $params['password'];
+        $updateParams['confirm_password'] = $params['confirm_password'];
+        $updateParams['old_password'] = $params['old_password'];
+        $res = $this->request('post', $this->url . 'app/user/sub-account-update-pwd', $updateParams, ['api-token' => $apiToken]);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 注册用户
+     * @param array params 注册用户参数
+     * @param string params.password 密码
+     * @param string params.confirm_password 确认密码
+     * @param string params.phone 手机
+     * @param string params.email 邮箱
+     * @param string params.valid_code 验证码
+     * @param string params.nick_name 昵称
+     * @param string params.app_code 应用code
+     * @param string params.app_key 应用key
+     * @param string params.ver_Key 应用版本
+     * @param integer params.auto_login 自动登录
+     * */
+    public function preRegister($params)
+    {
+        $nowTime = nowTime();
+        $registerData = [];
+        if (!empty($params['phone'])) {
+            $registerData['phone'] = $params['phone'];
+            $registerData['country_code'] = empty($params['country_code'])?'86':$params['country_code'];
+        }
+        if (!empty($params['email'])) {
+            $registerData['email'] = $params['email'];
+        }
+        if (!empty($params['valid_code'])) {
+            $registerData['valid_code'] = $params['valid_code'];
+        }
+        $registerData['app_code'] = $this->appCode;
+        $registerData['app_key'] = $this->appKey;
+        $registerData['ver_Key'] = $this->verKey;
+        $registerData['start_time'] = $nowTime;
+        $registerData['end_time'] = '2099-12-31 23:59:59';
+        $res = $this->request('post', $this->url . 'app/user/pre-register', $registerData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 确认注册
+     * */
+    public function confirmRegister($params){
+        $confirmParams = [];
+        $confirmParams['password'] = $params['password'];
+        $confirmParams['confirm_password'] = $params['confirm_password'];
+        $confirmParams['pre_register_key'] = $params['pre_register_key'];
+        if (!empty($params['auto_login'])) {
+            //是否自动登陆
+            $confirmParams['auto_login'] = $params['auto_login'];
+        }
+        $res = $this->request('post', $this->url . 'app/user/confirm-register', $confirmParams);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 更新用户数值权限使用量
+     * @return mixed
+     * @throws ApiException
+     * */
+    public function updateBindUse($params){
+        $updateBindData = [];
+        $updateBindData['app_code'] = $this->appCode;
+        $updateBindData['app_key'] = $this->appKey;
+        $updateBindData['operation_key'] = $this->operationKey;
+        $updateBindData['type_numerical_key'] = $params['type_numerical_key'];
+        $updateBindData['user_id'] = $params['user_id'];
+        $updateBindData['update_value'] = $params['update_value'];
+        $res = $this->request('post', $this->url . 'app/user/update-bind-use', $updateBindData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 获取用户数值权限使用量
+     * */
+    public function getBindUse($params){
+        $updateBindData = [];
+        $updateBindData['app_code'] = $this->appCode;
+        $updateBindData['app_key'] = $this->appKey;
+        $updateBindData['operation_key'] = $this->operationKey;
+        $updateBindData['user_id'] = $params['user_id'];
+        $updateBindData['type_numerical_key'] = empty($params['type_numerical_key'])?'':$params['type_numerical_key'];
+        $res = $this->request('post', $this->url . 'app/user/get-bind-use', $updateBindData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 获取用户已绑定(开通)应用
+     * @param string apiToken
+     * */
+    public function getBindAppList($userId)
+    {
+        $bindData = [];
+        $bindData['app_code'] = $this->appCode;
+        $bindData['user_id'] =$userId;
+        $bindData['operation_key'] = $this->operationKey;
+        $res = $this->request('post', $this->url . 'app/user/get-bind-app-list', $bindData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+    /**
+     * 获取应用的版本列表
+     * */
+    public function getAppVerList()
+    {
+        $bindData = [];
+        $bindData['app_code'] = $this->appCode;
+        $bindData['operation_key'] = $this->operationKey;
+        $res = $this->request('post', $this->url . 'app/user/get-app-ver-list', $bindData);
+        $resData = json_decode($res, true);
+        if ($this->isSuccess($resData)) {
+            return $resData['data'];
+        } else {
+            $message = empty($resData['message']) ? '' : $resData['message'];
+            throw new ApiException(1004,['msg'=> $message]);
+        }
+    }
+
+}

+ 199 - 0
app/Common/Services/WechatService.php

@@ -0,0 +1,199 @@
+<?php
+
+namespace App\Common\Services;
+use App\Common\Library\AES;
+use App\Exceptions\ApiException;
+use App\Services\CommonBaseService;
+use GuzzleHttp\Client;
+use GuzzleHttp\Exception\RequestException;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Log;
+
+class WechatService extends CommonBaseService
+{
+    protected $cache = true;
+
+    protected $cacheBucket = 'wechat:';
+
+    protected $client = null;
+
+    protected $appId = null;
+    protected $appSecret = null;
+    public function __construct()
+    {
+        $client = new Client();
+        $this->client = $client;
+        $this->appId = config('wechat.app_id');
+        $this->appSecret = config('wechat.app_secret');
+    }
+
+    public function getAccessToken()
+    {
+        $accessTokenKey = 'wechat:access:key';
+        $cache = Cache::get($accessTokenKey);
+        if($cache) {
+            return $cache;
+        }
+        $url = 'https://api.weixin.qq.com/cgi-bin/stable_token';
+        $formParams = [
+            'appid' => $this->appId,
+            'secret' => $this->appSecret,
+            'grant_type' => 'client_credential'
+        ];
+        try {
+            $options = [
+                'json' => $formParams,
+                'headers' => [
+                    'Content-Type' => 'application/json'
+                ]
+            ];
+            $response = $this->client->request('POST', $url, $options);
+            // 获取响应体并解码为数组
+            $body = json_decode($response->getBody()->getContents(), true);
+            if (!empty($body['errcode'])) {
+                $this->throwApiError($options, json_encode($body['errcode']).json_encode($body['errmsg']));
+            } else {
+                Cache::put($accessTokenKey, $body['access_token'], 240);
+                return $body['access_token'];
+            }
+        } catch (RequestException $e) {
+            $this->throwApiError($options, $e->getMessage(), $e->getCode());
+        }
+    }
+
+    public function getSessionInfo($code)
+    {
+        $url = 'https://api.weixin.qq.com/sns/jscode2session';
+        // 准备请求参数
+        $formParams = [
+            'appid' => $this->appId,
+            'secret' => $this->appSecret,
+            'js_code' => $code,
+            'grant_type' => 'authorization_code'
+        ];
+        try {
+            $options = [
+                'query' => $formParams
+            ];
+            $response = $this->client->request('GET', $url, $options);
+            // 获取响应体并解码为数组
+            $body = json_decode($response->getBody()->getContents(), true);
+            if (!empty($body['errcode'])) {
+                $this->throwApiError($options, json_encode($body['errcode']).json_encode($body['errmsg']));
+            } else {
+                // $openid = $data['openid'];
+                // $sessionKey = $data['session_key'];
+                return $body;
+            }
+        } catch (RequestException $e) {
+            $this->throwApiError($options, $e->getMessage(), $e->getCode());
+        }
+    }
+
+    public function getUserPhoneNumber($code)
+    {
+        $url = 'https://api.weixin.qq.com/wxa/business/getuserphonenumber';
+        // 准备请求参数
+        try {
+            $options = [
+                'query' => ['access_token' => $this->getAccessToken()],
+                'json' => ['code' => $code],
+                'headers' => [
+                    'Content-Type' => 'application/json'
+                ]
+            ];
+            $response = $this->client->request('POST', $url, $options);
+            // 获取响应体并解码为数组
+            $body = json_decode($response->getBody()->getContents(), true);
+            if (!empty($body['errcode'])) {
+                $this->throwApiError($options, json_encode($body['errcode']).json_encode($body['errmsg']));
+            } else {
+                /*
+                 * {
+                    "errcode":0,
+                    "errmsg":"ok",
+                    "phone_info": {
+                        "phoneNumber":"xxxxxx",
+                        "purePhoneNumber": "xxxxxx",
+                        "countryCode": 86,
+                        "watermark": {
+                            "timestamp": 1637744274,
+                            "appid": "xxxx"
+                        }
+                    }
+                   }
+                 */
+                return $body;
+            }
+        } catch (RequestException $e) {
+            $this->throwApiError($options, $e->getMessage(), $e->getCode());
+        }
+    }
+
+    public function getWxaQueryScheme($scheme)
+    {
+        //https://api.weixin.qq.com/wxa/queryscheme?access_token=ACCESS_TOKEN
+        //query_type
+        $keyScheme = 'key:wechat:key:scheme';
+        $cache = Cache::get($keyScheme);
+        if($cache) {
+            return $cache;
+        }
+        $url = 'https://api.weixin.qq.com/wxa/generatescheme';
+        // 准备请求参数
+        try {
+            $options = [
+                'query' => ['access_token' => $this->getAccessToken()],
+                'json' => ['expire_time' => strtotime('+28 day', time()), 'expire_type' => 0],
+                'headers' => [
+                    'Content-Type' => 'application/json'
+                ]
+            ];
+            $response = $this->client->request('POST', $url, $options);
+            // 获取响应体并解码为数组
+            $body = json_decode($response->getBody()->getContents(), true);
+            if (!empty($body['errcode'])) {
+                $this->throwApiError($options, json_encode($body['errcode']).json_encode($body['errmsg']));
+            } else {
+                Cache::put($keyScheme, $body['openlink'], 20 * 24 * 60 * 60);
+                return $body['openlink'] ?? '';
+            }
+        } catch (RequestException $e) {
+            $this->throwApiError($options, $e->getMessage(), $e->getCode());
+        }
+    }
+
+    public function decryptData(string $sessionKey, string $iv, string $encrypted)
+    {
+        $decrypted = AES::decrypt(
+            base64_decode($encrypted, false), base64_decode($sessionKey, false), base64_decode($iv, false)
+        );
+
+        $decrypted = json_decode($this->pkcs7Unpad($decrypted), true);
+
+        if (!$decrypted) {
+            $this->throwApiError([$sessionKey, $iv, $encrypted], '解密失败', 500);
+        }
+
+        return $decrypted;
+    }
+
+    public function pkcs7Unpad(string $text)
+    {
+        $pad = ord(substr($text, -1));
+        if ($pad < 1 || $pad > 32) {
+            $pad = 0;
+        }
+
+        return substr($text, 0, (strlen($text) - $pad));
+    }
+
+    private function throwApiError($options, $error, $code = 200)
+    {
+        $key = createGuid();
+        Log::info('Wechat第三方接口出错key:'.$key);
+        Log::info('Wechat第三方接口出错params:'.json_encode($options));
+        Log::info('Wechat第三方接口出错:'.$code.','.$error);
+        throw new ApiException(500);
+    }
+}

Diff do ficheiro suprimidas por serem muito extensas
+ 1716 - 0
app/Common/helpers.php


+ 18 - 0
app/Common/routes.php

@@ -0,0 +1,18 @@
+<?php
+$app = app();
+$app->router->group([
+    'namespace' => 'App\Common\Controllers',
+    'prefix' => 'api',
+], function ($router) {
+    /*公共接口*/
+    //获取短信验证码
+    $router->get('common/get-sms-code', ['uses' => 'CommonController@getSMSCode']);
+    //获取邮箱验证码
+    $router->get('common/get-email-code', ['uses' => 'CommonController@getEmailCode']);
+    $router->get('common/get-wxa-query-scheme', ['uses' => 'CommonController@getWxaQuerySchemeFade']);
+    $router->group([
+        'middleware' => ['admin.auth']
+    ], function ($router) {
+    });
+
+});

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


+ 126 - 0
app/Console/Commands/MakeApacheConfCommand.php

@@ -0,0 +1,126 @@
+<?php
+
+namespace App\Console\Commands;
+use Illuminate\Console\Command;
+
+class MakeApacheConfCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    //部署文件目录名称,需手动输入
+    protected $signature = 'make:apache-conf {dir}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '自动生成apache配置文件';
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $url = config('app.web_url');
+        $parsedUrl = parse_url($url);
+        $host = $parsedUrl['host'];
+        $dir = $this->argument('dir');
+        $project = base_path();
+        $project = explode('/', $project);
+        array_pop($project);
+        $project[] = $dir;
+        $project = implode('/', $project);
+        $conf =
+'<VirtualHost *:80>
+   DocumentRoot "'.$project.'/public/"
+   ServerName '.$host.'
+   Alias "/resources/" "'.$project.'/resources/upload/"
+   Alias "/admin" "'.$project.'/public/dist"
+   Alias "/static/" "'.$project.'/public/dist/static/"
+   ErrorLog logs/mp-website-admin-error_log
+   CustomLog logs/mp-website-admin-access_log combined
+   <Directory "'.$project.'/public/">
+       AllowOverride All
+       Require all granted
+   </Directory>
+   <Directory "'.$project.'/resources/upload/">
+    RewriteEngine On
+        RewriteCond %{QUERY_STRING} responseContentType=application%2Foctet-stream [NC]
+        RewriteRule .* - [E=DOWNLOAD:yes]
+        Header set Content-Disposition "attachment" env=DOWNLOAD
+    AllowOverride All
+    Require all granted
+   </Directory>
+   <Directory "'.$project.'/public/dist">
+    AllowOverride All
+    DirectoryIndex index.html
+    Require all granted
+   </Directory>
+        <Directory "'.$project.'/public/dist/static/">
+            AllowOverride All
+                Require all granted
+        </Directory>
+</VirtualHost>
+<VirtualHost *:443>
+    DocumentRoot "'.$project.'/public/"
+    ServerName '.$host.'
+        Alias "/resources/" "'.$project.'/resources/upload/"
+        Alias "/admin" "'.$project.'/public/dist"
+        Alias "/static/" "'.$project.'/public/dist/static/"
+    LimitRequestLine 40940
+    LimitRequestFieldSize 40940
+
+    SSLEngine on
+    SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
+    SSLCertificateFile "/etc/httpd/cert/matchexpo.cn/matchexpo.cn.crt"
+    SSLCertificateKeyFile "/etc/httpd/cert/matchexpo.cn/matchexpo.cn.key"
+    SSLCertificateChainFile "/etc/httpd/cert/matchexpo.cn/CA-Bundle.crt"
+    #SSLCACertificateFile "/etc/httpd/cert/call/TencentQQAuthCA.crt"
+    #SSLVerifyClient require
+
+    <FilesMatch "\.(cgi|shtml|pl|asp|php)$">
+        SSLOptions +StdEnvVars
+    </FilesMatch>
+
+    BrowserMatch ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
+
+    <Directory "'.$project.'/public/">
+        Options FollowSymLinks
+        AllowOverride All
+        Order allow,deny
+        Allow from all
+    </Directory>
+        <Directory "'.$project.'/resources/upload/">
+         RewriteEngine On
+        RewriteCond %{QUERY_STRING} responseContentType=application%2Foctet-stream [NC]
+        RewriteRule .* - [E=DOWNLOAD:yes]
+        Header set Content-Disposition "attachment" env=DOWNLOAD
+            AllowOverride All
+        Require all granted
+        </Directory>
+        <Directory "'.$project.'/public/dist">
+                AllowOverride All
+                DirectoryIndex index.html
+                Require all granted
+        </Directory>
+        <Directory "'.$project.'/public/dist/static/">
+                AllowOverride All
+                Require all granted
+        </Directory>
+</VirtualHost>
+';
+        $path = resource_path('conf/');
+        if (!is_dir($path)) {
+            mkDirs($path);
+        }
+        $fileName = $path . $dir .'_apache.conf';
+        file_put_contents($fileName, $conf);
+        echo 'echo IncludeOptional '.$fileName.' >> /etc/httpd/conf/httpd.conf'.PHP_EOL;
+    }
+}

+ 39 - 0
app/Console/Commands/MakeBlogUvCommand.php

@@ -0,0 +1,39 @@
+<?php
+
+
+namespace App\Console\Commands;
+
+
+use App\Web\Facades\BlogFacade;
+use App\Web\Facades\HelpFacade;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+
+class MakeBlogUvCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'make:blog_uv';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '自动增加文章虚拟阅读量';
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        BlogFacade::blogUvFactory();
+        HelpFacade::helpUvFactory();
+        echo 'ok ';
+    }
+}

+ 95 - 0
app/Console/Commands/MakeNginxConfCommand.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace App\Console\Commands;
+use Illuminate\Console\Command;
+
+class MakeNginxConfCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    //部署文件目录名称,需手动输入
+    protected $signature = 'make:nginx-conf {dir}';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '自动生成nginx配置文件';
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $url = config('app.web_url');
+        $parsedUrl = parse_url($url);
+        $host = $parsedUrl['host'];
+        $dir = $this->argument('dir');
+        $project = base_path();
+        $project = explode('/', $project);
+        array_pop($project);
+        $project[] = $dir;
+        $project = implode('/', $project);
+        $certKey = resource_path('cert').'/apiclient_key.pem';
+        $certPem = resource_path('cert').'/apiclient_cert.pem';
+        $conf =
+'server {
+    listen       80;
+    server_name ' . $host . ';
+    return 301 https://' . $host . '$request_uri;
+}
+server{
+    listen               443 ssl;
+    server_name ' . $host . ';
+    ssl_certificate "'.$certPem.'";
+    ssl_certificate_key "'.$certKey.'";
+    ssl_session_timeout 5m;
+    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
+    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+    ssl_prefer_server_ciphers on;
+    index index.html index.htm index.php;
+    root  ' . $project . '/public;
+
+        location = /favicon.ico {
+                log_not_found off;
+                access_log off;
+        }
+
+        location /resources/ {
+                alias  "' . $project . '/resources/upload/";
+        }
+        location /admin_me_15446 {
+                 alias "' . $project . '/public/dist";
+                index  index.html;
+        }
+
+        location /static/ {
+                alias "' . $project . '/public/dist/static/";
+        }
+        location / {
+                try_files $uri $uri/ /index.php?$query_string;
+        }
+        location ~ .*\.(php|php5)?$
+        {
+                index  index.html index.htm index.php;
+                fastcgi_pass 127.0.0.1:9000;
+                fastcgi_index index.php;
+                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+                include fastcgi.conf;
+        }
+}';
+        $path = resource_path('conf/');
+        if (!is_dir($path)) {
+            mkDirs($path);
+        }
+        $fileName = $path . $dir .'_nginx.conf';
+        file_put_contents($fileName, $conf);
+        echo 'echo "include '.$fileName.';" >> /etc/nginx/nginx.conf'.PHP_EOL;
+    }
+}

+ 53 - 0
app/Console/Kernel.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace App\Console;
+
+use App\Console\Commands\MakeApacheConfCommand;
+use App\Console\Commands\MakeBlogUvCommand;
+use App\Console\Commands\MakeNginxConfCommand;
+use App\Form\Services\FormRecordService;
+use App\Web\Services\WebUtmService;
+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 = [
+        MakeBlogUvCommand::class,
+        MakeNginxConfCommand::class,
+        MakeApacheConfCommand::class
+        //
+    ];
+
+    /**
+     * Define the application's command schedule.
+     *
+     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
+     * @return void
+     */
+    protected function schedule(Schedule $schedule)
+    {
+        //* * * * * cd /mp_website && php artisan schedule:run >> /dev/null 2>&1
+        //发送邮件
+        $schedule->call(function () {
+            $service = app()->make(FormRecordService::class);
+            $service->processRecordEmailSend();
+        })->everyMinute()
+            ->name('SendFormRecordEmail')
+            ->withoutOverlapping(6000);
+
+        //统计utm
+        // */5 * * * * curl -k https://mp-website-test-munich.matchexpo.cn/cron/web-utm/persistence
+//        $schedule->call(function () {
+//            $service = app()->make(WebUtmService::class);
+//            $service->persistenceUtmPvUv();
+//        })->everyFiveMinutes()
+//            ->name('persistenceUtmPvUv')
+//            ->withoutOverlapping(6000);
+    }
+}

+ 10 - 0
app/Events/Event.php

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

+ 16 - 0
app/Events/ExampleEvent.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Events;
+
+class ExampleEvent extends Event
+{
+    /**
+     * Create a new event instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+}

+ 32 - 0
app/ExcelData/Providers/ExcelDataProvider.php

@@ -0,0 +1,32 @@
+<?php
+namespace App\ExcelData\Providers;
+
+
+use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;
+
+class ExcelDataProvider extends ServiceProvider
+{
+    //路由文件名
+    protected $routes = 'routes.php';
+
+
+    public function boot()
+    {
+        //自动载入路由
+        $func = new \ReflectionClass(get_class($this));
+        $path = str_replace($func->getShortName() . '.php', '', $func->getFileName());
+        $routesFile = $path . '../' . $this->routes;
+        if (file_exists($routesFile)) {
+            require $routesFile;
+        }
+        parent::boot();
+    }
+
+    /**
+     * 注册
+     */
+    public function register()
+    {
+
+    }
+}

+ 65 - 0
app/ExcelData/Services/FormRecordSumService.php

@@ -0,0 +1,65 @@
+<?php
+
+
+namespace App\ExcelData\Services;
+
+
+use App\Exceptions\ApiException;
+use App\Common\Facades\OssFacade;
+use App\Form\Facades\FormRecordFacade;
+use Maatwebsite\Excel\Concerns\Exportable;
+use Maatwebsite\Excel\Concerns\WithMultipleSheets;
+
+class FormRecordSumService implements WithMultipleSheets
+{
+    use Exportable;
+    protected $params = [];
+
+    /**
+     * 设置参数
+     * @param $params 详见:\App\Forms\Services\FormInfoService::getList
+     * @return $this
+     */
+    public function setParams($params)
+    {
+        $this->params = $params;
+        return $this;
+    }
+
+    /**
+     * 设置sheet
+     * @return array
+     */
+    public function sheets(): array
+    {
+        $sheets = [];
+        //通过查询条件获取sheets基本信息
+        $params = $this->params;
+        $params['format_list'] = false;
+        $data = FormRecordFacade::getFormRecordList($params);
+        empty($data) && $data = [];
+        if(empty($data)){
+            throw new ApiException(40013);
+        }
+        $forms = mapByKey($data, 'form_id');
+        /*$formIds = array_column($data, 'form_id');
+        !empty($formIds) && $formIds = array_unique($formIds);*/
+
+        $params = $this->params;
+        foreach ($forms as $id => $form) {
+            $params['form_id'] = $id;
+            $sheets[] = (new FormService())->setParams($params);
+        }
+        return $sheets;
+    }
+
+    public function toOss()
+    {
+        $filename = 'form'.date('ymdHis').rand(0,99).'.xlsx';
+        $filePath = storage_path('app/').$filename;
+        $this->store($filename);
+        $etag = md5_file($filePath);
+        $url = OssFacade::uploadToOss($filePath, $filename, $etag);
+        return $url;
+    }
+}

+ 145 - 0
app/ExcelData/Services/FormService.php

@@ -0,0 +1,145 @@
+<?php
+
+
+namespace App\ExcelData\Services;
+
+
+use App\Form\Facades\FormInfoFacade;
+use App\Form\Facades\FormItemFacade;
+use App\Form\Facades\FormRecordFacade;
+use Maatwebsite\Excel\Concerns\Exportable;
+use Maatwebsite\Excel\Concerns\FromArray;
+use Maatwebsite\Excel\Concerns\FromQuery;
+use Maatwebsite\Excel\Concerns\WithHeadings;
+use Maatwebsite\Excel\Concerns\WithMapping;
+use Maatwebsite\Excel\Concerns\WithTitle;
+
+class FormService implements FromArray, WithTitle, WithHeadings, WithMapping
+{
+    use Exportable;
+    const DEFAULT_TITLE = 'sheet';
+
+    protected $params;
+    protected $title = '';
+    protected $items = [];
+    protected $heads = [];
+    protected $website = [];
+    protected $websiteLanguage = '';
+    protected $reverseHead = [];
+    protected $timeKey = '提交时间';
+    protected $languageKey = '网站语言';
+
+    /**
+     * 设置参数
+     * @param $params 详见: FormInfoFacade::getList
+     * @return $this
+     */
+    public function setParams($params)
+    {
+        $this->params = $params;
+        $title = '';
+        $items = [];
+        $heads = [];
+        if (!empty($this->params['form_id'])) {
+            $form = FormInfoFacade::findOneBy(['id' => $this->params['form_id']], 'id,name');
+            if (!empty($form)) {
+                $title = $form['name'] ?: self::DEFAULT_TITLE.$this->params['form_id'];
+                //获取items
+                $items =  FormItemFacade::getData($this->params['form_id']);
+                empty($items) && $items = [];
+                $heads = array_column($items, 'name');
+                $heads = array_unique($heads);
+                !empty($heads) && array_unshift($heads, $this->timeKey, $this->languageKey);
+                //获取语言信息
+                $this->websiteLanguage ='自定义表单';
+            }
+        }
+        //超出31表单导不出
+        if(strlen($title) > 31){
+            $title = substr($title, -17).uniqid();
+        }
+        $this->title = $title;
+        $this->items = mapByKey($items, 'id');
+        $this->heads = $heads;
+        !empty($heads) && $this->reverseHead = array_flip($heads);
+        return $this;
+    }
+
+    /**
+     * 查询query
+     * @return \Illuminate\Database\Query\Builder
+     */
+    public function array():array
+    {
+        $items = FormRecordFacade::getList($this->params);
+        FormRecordFacade::formatList($items);
+        return $items;
+    }
+
+    /**
+     * 标题
+     * @return string
+     */
+    public function title(): string
+    {
+        return $this->title;
+    }
+
+    /**
+     * 字段名
+     * @return array
+     */
+    public function headings(): array
+    {
+        return $this->heads;
+    }
+
+    /**
+     * 遍历数据
+     * @param mixed $row
+     * @return array
+     */
+    public function map($row): array
+    {
+        $defaultValue = '';
+        $defaultGlue = ',';
+
+        //根绝head排序数据,若为空,不进行处理
+        $items = $row['items'];
+        $data = [];
+        $tmp = [];
+        //设置提交时间
+        $index = $this->reverseHead[$this->timeKey];
+        $data[$index] = $row['create_time']?:'';
+        //设置语言
+        $index = $this->reverseHead[$this->languageKey];
+        $data[$index] = $this->websiteLanguage;
+        foreach ($items as &$item) {
+            $id = $item['form_item_id'];
+            is_array($item['content']) && $item['content'] = implode($defaultGlue, $item['content']);
+            if (isset($this->items[$id])) {
+                //获取在heads中的位置
+                $name = $this->items[$id]['name'];
+                $index = $this->reverseHead[$name];
+                if (!empty($data[$index])) {
+                    $data[$index] .= $defaultGlue.$item['content'];
+                } else {
+                    $data[$index] = $item['content'];
+                }
+            }
+            $tmp[] = $item['content'];
+        }
+
+        if (empty($this->heads)) {
+            return $tmp;
+        }
+
+        //默认值处理
+        $returnData = [];
+        foreach ($this->heads as $key => $v) {
+            $returnData[] = $data[$key] ?? $defaultValue;
+        }
+
+        return $returnData;
+    }
+}

+ 66 - 0
app/ExcelData/Services/FormSheetsService.php

@@ -0,0 +1,66 @@
+<?php
+
+
+namespace App\ExcelData\Services;
+
+
+use App\Exceptions\ApiException;
+use App\Common\Facades\OssFacade;
+use App\Form\Facades\FormRecordFacade;
+use Maatwebsite\Excel\Concerns\Exportable;
+use Maatwebsite\Excel\Concerns\WithMultipleSheets;
+
+class FormSheetsService implements WithMultipleSheets
+{
+    use Exportable;
+    protected $params = [];
+
+    /**
+     * 设置参数
+     * @param $params 详见:\App\Forms\Services\FormInfoService::getList
+     * @return $this
+     */
+    public function setParams($params)
+    {
+        $this->params = $params;
+        return $this;
+    }
+
+    /**
+     * 设置sheet
+     * @return array
+     */
+    public function sheets(): array
+    {
+        $sheets = [];
+        //通过查询条件获取sheets基本信息
+        $params = $this->params;
+        $params['format_list'] = false;
+        $data = FormRecordFacade::getList($params);
+        empty($data) && $data = [];
+        if(empty($data)){
+            throw new ApiException(40013);
+        }
+        $forms = mapByKey($data, 'form_id');
+        /*$formIds = array_column($data, 'form_id');
+        !empty($formIds) && $formIds = array_unique($formIds);*/
+
+        $params = $this->params;
+        foreach ($forms as $id => $form) {
+            $params['form_id'] = $id;
+            $sheets[] = (new FormService())->setParams($params);
+        }
+        return $sheets;
+    }
+
+    public function toOss()
+    {
+        $filename = 'form'.date('ymdHis').rand(0,99).'.xlsx';
+        //$filePath =    resource_path('upload/') . $filename;;
+        return $this->download($filename);
+//      //  $etag = md5_file($filePath);
+//      //  $url = OssFacade::uploadToOss($filePath, $filename, $etag);
+//        $url='/resources/upload/'.$filename;
+//        return $url;
+    }
+}

+ 66 - 0
app/ExcelData/Services/ImportHeadingExcel.php

@@ -0,0 +1,66 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: luxf
+ * Date: 2022/3/29
+ * Time: 9:53 AM
+ */
+
+namespace App\ExcelData\Services;
+
+
+use Illuminate\Support\Collection;
+use Maatwebsite\Excel\Concerns\ToCollection;
+use Maatwebsite\Excel\Concerns\WithHeadingRow;
+
+/**
+ * 通用导入EXCEL文件对象
+ * Class ImportExcel
+ * @package App\Base\Models
+ */
+class ImportHeadingExcel implements ToCollection
+{
+    private $callback;
+    private $readRows = 0;
+
+    public function __construct(\Closure $callback)
+    {
+        $this->callback = $callback;
+    }
+
+    /**
+     * 格式化日期
+     * @param int $value
+     * @param string $format
+     * @return Carbon|false|string
+     */
+    public static function transformDateTime(int $value, string $format = 'Y-m-d H:i:s')
+    {
+        if (!$value) {
+            return '';
+        }
+        $value--;
+        return date($format, strtotime("1900-01-00 00:00:00 +$value day"));
+    }
+
+    /**
+     * @param  Collection $collection
+     */
+    public function collection(Collection $collection)
+    {
+        if ($this->readRows == 0) {
+            //unset($collection[0]);
+            $this->readRows += count($collection);
+        }
+        $this->callback->call($this, $collection);
+    }
+
+    /**
+     * @return int
+     */
+    public function chunkSize(): int
+    {
+        return 2000;
+    }
+
+}

+ 131 - 0
app/Exceptions/ApiException.php

@@ -0,0 +1,131 @@
+<?php
+
+
+namespace App\Exceptions;
+
+
+class ApiException extends \Exception
+{
+    /**
+     * 错误信息参数
+     * @var array
+     */
+    protected $params = [];
+    /**
+     * 携带的参数
+     * @var array
+     */
+    protected $data = [];
+
+    /** 处理异常错误
+     * @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 = [])
+    {
+        $locale = $locale ?: app('translator')->getLocale();
+        $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 = $errors[$code];
+        if (!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');
+        }
+    }
+}

+ 129 - 0
app/Exceptions/Handler.php

@@ -0,0 +1,129 @@
+<?php
+
+namespace App\Exceptions;
+
+use App\Exceptions\ApiException;
+use Illuminate\Auth\Access\AuthorizationException;
+use Illuminate\Database\Eloquent\ModelNotFoundException;
+use Illuminate\Http\Response;
+use Illuminate\Validation\ValidationException;
+use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Throwable;
+
+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  \Throwable  $exception
+     * @return void
+     *
+     * @throws \Exception
+     */
+    public function report(Throwable $exception)
+    {
+        parent::report($exception);
+    }
+
+    /**
+     * Render an exception into an HTTP response.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Throwable  $e
+     * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
+     *
+     * @throws \Throwable
+     */
+    public function render($request, Throwable $e)
+    {
+        $content = [
+            'code' => $e->getCode() == 0 ? '500' : $e->getCode(),
+            'message' => $e->getMessage(),
+            'data'=> []
+        ];
+        if ($e instanceof HttpResponseException) {
+            $content['data'] = $e->getResponse();
+        } elseif ($e instanceof ModelNotFoundException || $e instanceof NotFoundHttpException) {
+            $content['code'] = 404;
+            $content['message'] = '您访问的页面地址不存在';
+            return redirect('/');
+        } elseif ($e instanceof AuthorizationException) {
+            $content['code'] = 403;
+        } elseif ($e instanceof ValidationException && $e->getResponse()) {
+            $content['code'] = 422;
+//            $content['message'] = '验证不通过';
+            $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['message'] = reset($msg);
+            $content['code'] = reset($ret) ?: 422;
+            if (is_array($content['message'])) {
+                if (count($content['message']) == 2) {
+                    $content['code'] = $content['message'][0];
+                    $content['message'] = $content['message'][1];
+                }
+            }
+        } elseif($e instanceof ApiException){
+            $content['data'] = $e->getData();
+        }
+
+        $trace = $e->getTraceAsString();
+        if (isset($content['message']) && $content['message']) {
+            $track = json_encode($trace, JSON_UNESCAPED_UNICODE);
+            if (stristr($content['message'], 'connection')
+                || stristr($content['message'], 'SQLSTATE')
+                || stristr($track, 'PDOException: SQLSTATE')
+            ) {
+                $content['code'] = -100;
+                //发短信通知
+//                errorHandleSms($content['message']);
+            }
+        }
+
+        if (config('app.app_debug')) { // 开发模式增加track输出
+            $content['track'] = $trace;
+        }
+        $response = new Response(json_encode($content));
+        $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');
+        $response->header('Access-Control-Expose-Headers', '*');
+        $response->exception = $e;
+        return $response;
+       // return parent::render($request, $e);
+    }
+}

+ 181 - 0
app/Form/Controllers/FormController.php

@@ -0,0 +1,181 @@
+<?php
+
+namespace App\Form\Controllers;
+
+
+use App\Form\Facades\FormRecordFacade;
+use App\Http\Controllers\BaseController;
+use App\Form\Services\FormInfoService;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Log;
+
+class FormController extends BaseController
+{
+    private $service;
+    public function __construct(FormInfoService $service)
+    {
+        $this->service = $service;
+    }
+
+    public function getList(Request $request)
+    {
+        $params=$request->only(['page_size','page','keyword','date']);
+        $rst = $this->service->getList($params);
+        return $this->jsonResponse('ok', $rst);
+    }
+
+    /**
+     * @param Request $request
+     * @return mixed
+     */
+    public function formById(Request $request)
+    {
+        $this->validate($request,[
+            'id' => 'required'
+        ],[
+            'id.required'    => 'id不能为空'
+        ]);
+        $ret=$this->service->getData($request->input('id'));
+         return $this->jsonResponse('ok', $ret);
+    }
+
+    /**
+     * @param Request $request
+     * @return mixed
+     */
+    public function addForm(Request $request)
+    {
+        $params=$request->only(['id','name','form_items','status','sort','seo_data', 'seo_id']);
+        $rst=$this->service->addData($params);
+        return $this->jsonResponse('ok', $rst);
+    }
+
+    /**
+     * @param Request $request
+     * @return mixed
+     */
+    public function addItem(Request $request)
+    {
+
+        $this->validate($request,[
+            'form_id' => 'required'
+        ],[
+            'form_id.required'    => 'form_id不能为空'
+        ]);
+        $params=$request->all();
+        $rst= $this->service->addItem($params);
+        return $this->jsonResponse('ok', $rst);
+    }
+
+    /**
+     * 删除表单项
+     * @param Request $request
+     * @return mixed
+     */
+    public function delItem(Request $request)
+    {
+
+        $this->validate($request,[
+            'form_id' => 'required',
+            'id' => 'required'
+        ],[
+            'form_id.required'    => 'form_id不能为空',
+            'id.required'    => 'id不能为空',
+        ]);
+        $params=$request->all();
+        $rst= $this->service->delItem($params);
+        return $this->jsonResponse('ok', $rst);
+    }
+
+    /**
+     * @param Request $request
+     * @return mixed
+     */
+    public function sortItem(Request $request)
+    {
+        $this->validate($request,[
+            'ids' => 'required'
+        ],[
+            'ids.required'    => 'ids不能为空'
+        ]);
+        return $this->service->sortItem($request->input('ids'));
+    }
+
+    /**
+     * @param Request $request
+     * @return mixed
+     */
+    public function sortItemDetail(Request $request)
+    {
+        $this->validate($request,[
+            'id' => 'required'
+        ],[
+            'id.required'    => 'id不能为空'
+        ]);
+        return $this->service->sortItemDetail($request->input('id'));
+    }
+
+    /**
+     * @param Request $request
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function formByEditor(Request $request)
+    {
+        $data = $this->service->setFormByEditor($request->all());
+        return $this->jsonResponse('OK', $data);
+    }
+
+    /**
+     * 更新表单状态
+     * */
+    public function stopAndDelFormById(Request $request){
+        $this->validate($request,[
+            'id' => 'required',
+            'status'=>'required'
+        ],[
+            'id.required'    => 'id不能为空',
+            'status.required'    => 'status不能为空'
+        ]);
+        $id=$request->input('id');
+        $status=$request->input('status');
+        $data = $this->service->stopAndDelFormById($id,$status);
+        return $this->jsonResponse('OK', $data);
+    }
+
+    /**
+     * 获取对应公司下询盘列表
+     * @param Request $request
+     * */
+    public function correspondCompanyFormList(Request $request) {
+        $params=$request->all();
+        $rst = $this->service->correspondCompanyFormList($params);
+        return $this->jsonResponse('ok',$rst);
+    }
+
+    /**
+     * 获取未读询盘数据总数量
+     * */
+    public function getCompanyFormRecordNotReadCount() {
+        $rst = $this->service->getCompanyFormRecordNotReadCount();
+        return $this->jsonResponse('ok',$rst);
+    }
+
+    /**
+     * 添加表单数据
+     * */
+    public function addFormRecord(Request $request)
+    {
+        $this->validate($request, [
+            'form_id' => 'required',
+            'form_data' => 'array'
+        ]);
+        $params = $request->all();
+        $referer = $request->header('referer');
+        $params['referer']=$referer;
+        //dd($params);
+        $ip=getClientIp(0, true);
+        $ret= FormRecordFacade::addFormRecord($params,$ip);
+        return $this->jsonResponse('ok',$ret);
+    }
+
+}

+ 133 - 0
app/Form/Controllers/FormRecordController.php

@@ -0,0 +1,133 @@
+<?php
+
+
+namespace App\Form\Controllers;
+
+
+use App\Http\Controllers\BaseController;
+use App\ExcelData\Services\FormService;
+use App\ExcelData\Services\FormSheetsService;
+use App\ExcelData\Services\FormRecordSumService;
+use App\Form\Facades\FormItemFacade;
+use App\Form\Services\FormRecordService;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+
+class FormRecordController extends BaseController
+{
+    private $service;
+
+    /**
+     * UserController constructor.
+     * @param FormRecordService $service
+     */
+    public function __construct(FormRecordService $service)
+    {
+        $this->service = $service;
+    }
+
+    public function getList(Request $request)
+    {
+        $params=$request->all();
+        $rst = $this->service->getList(
+            $params
+        );
+        $formId = $params['form_id'] ?? '';
+        if($formId){
+            $rst['columns']=FormItemFacade::getItemsByFromId($formId);
+        }else{
+            $rst['columns']=[];
+        }
+        return $this->jsonResponse('ok', $rst);
+    }
+
+    /**
+     * form_id 表单id
+     * */
+    public function exportList(Request $request)
+    {
+        $params = $request->all();
+        $ret = (new FormSheetsService())->setParams($params)->toOss();
+        return $ret;
+    }
+
+    /**
+     * 获取提交记录详情
+     * @param Request $request
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function getRecord(Request $request)
+    {
+        $this->validate(
+            $request,
+            [
+                'id' => 'required'
+            ]
+        );
+        $rst = $this->service->getRecord(
+            $request->input('id')
+        );
+        return $this->jsonResponse('ok', $rst);
+    }
+
+    public function addRecord(Request $request)
+    {
+
+    }
+
+    /**
+     * 删除记录
+     * @param Request $request
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function delRecord(Request $request)
+    {
+        $this->validate(
+            $request,
+            [
+                'id' => 'required'
+            ]
+        );
+        $rst = $this->service->changeStatus(
+            $request->input('id'),
+            2
+        );
+        return $this->jsonResponse('ok', $rst);
+    }
+
+    /**
+     * 修改状态值
+     * @param Request $request
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function changeRecordStatus(Request $request)
+    {
+        $this->validate($request, [
+            'id' => 'required',
+            'status' => 'required|in:0,1'
+        ]);
+        $rst = $this->service->changeStatus(
+            $request->input('id'),
+            $request->input('status')
+        );
+        return $this->jsonResponse('ok', $rst);
+    }
+
+    /**
+     * 设置为已读
+     * @param Request $request
+     * @return \Illuminate\Http\JsonResponse
+     * */
+    public function recordSetIsRead(Request $request){
+        $ids = $request->input('ids', []);
+        $rst = $this->service->recordSetIsRead($ids);
+        return $this->jsonResponse('ok', $rst);
+    }
+
+    public function centerExportList(Request $request)
+    {
+        $params = $request->all();
+        $rst = (new FormRecordSumService())->setParams($params)->toOss();
+        return $this->jsonResponse('ok', $rst);
+    }
+}

+ 13 - 0
app/Form/Facades/FormContentFacade.php

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

+ 13 - 0
app/Form/Facades/FormInfoFacade.php

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

+ 13 - 0
app/Form/Facades/FormItemDetailFacade.php

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

+ 13 - 0
app/Form/Facades/FormItemFacade.php

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

+ 13 - 0
app/Form/Facades/FormProductDetailFacade.php

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

+ 13 - 0
app/Form/Facades/FormProductFacade.php

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

+ 13 - 0
app/Form/Facades/FormRecordFacade.php

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

+ 15 - 0
app/Form/Models/FormContentModel.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Form\Models;
+
+use App\Models\BaseModel;
+
+class FormContentModel extends BaseModel
+{
+    protected $table = 'form_content';
+
+    /**
+     * 状态字段
+     */
+    const DELETED_AT = null;
+}

+ 26 - 0
app/Form/Models/FormInfoModel.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Form\Models;
+
+use App\Models\BaseModel;
+
+class FormInfoModel extends BaseModel
+{
+    protected $table = 'form_info';
+
+
+    /**
+     * 检查 名称是否唯一
+     * */
+    public function checkNameUnique($name,$id=0){
+        $where=[];
+        if(!empty($id)){
+            $where[]=['id', '<>', $id];
+        }
+        $where[]=['status', '<', 2];
+        $where[]=['name', '=', trim($name)];
+        return $this->checkFieldUnique('name',$where);
+    }
+
+
+}

+ 10 - 0
app/Form/Models/FormItemDetailModel.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App\Form\Models;
+
+use App\Models\BaseModel;
+
+class FormItemDetailModel extends BaseModel
+{
+    protected $table = 'form_item_detail';
+}

+ 10 - 0
app/Form/Models/FormItemModel.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App\Form\Models;
+
+use App\Models\BaseModel;
+
+class FormItemModel extends BaseModel
+{
+    protected $table = 'form_item';
+}

+ 13 - 0
app/Form/Models/FormProductDetailModel.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Form\Models;
+
+
+
+use App\Models\BaseModel;
+
+class FormProductDetailModel extends BaseModel
+{
+    protected $table = 'form_product_detail';
+    //use ApiSoftDeletes;
+}

+ 13 - 0
app/Form/Models/FormProductModel.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Form\Models;
+
+
+
+use App\Models\BaseModel;
+
+class FormProductModel extends BaseModel
+{
+    protected $table = 'form_product';
+    //use ApiSoftDeletes;
+}

+ 12 - 0
app/Form/Models/FormRecordModel.php

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

+ 87 - 0
app/Form/Providers/FormServiceProvider.php

@@ -0,0 +1,87 @@
+<?php
+
+
+namespace App\Form\Providers;
+
+
+use App\Form\Facades\FormContentFacade;
+use App\Form\Facades\FormItemDetailFacade;
+use App\Form\Facades\FormItemFacade;
+use App\Form\Facades\FormProductDetailFacade;
+use App\Form\Facades\FormProductFacade;
+use App\Form\Facades\FormRecordFacade;
+use App\Form\Models\FormContentModel;
+use App\Form\Models\FormItemDetailModel;
+use App\Form\Models\FormItemModel;
+use App\Form\Models\FormProductDetailModel;
+use App\Form\Models\FormProductModel;
+use App\Form\Models\FormRecordModel;
+use App\Form\Services\FormContentService;
+use App\Form\Services\FormInfoService;
+use App\Form\Models\FormInfoModel;
+use App\Form\Facades\FormInfoFacade;
+use App\Form\Services\FormItemDetailService;
+use App\Form\Services\FormItemService;
+use App\Form\Services\FormProductDetailService;
+use App\Form\Services\FormProductService;
+use App\Form\Services\FormRecordService;
+
+use App\Providers\EventServiceProvider;
+
+class FormServiceProvider extends EventServiceProvider
+{
+    public function register()
+    {
+        $this->app->bind(FormInfoService::class,function(){
+            return new FormInfoService(new FormInfoModel);
+        });
+        $this->app->bind(FormInfoFacade::class,function(){
+            return app()->make(FormInfoService::class);
+        });
+
+        $this->app->bind(FormItemService::class,function(){
+            return new FormItemService(new FormItemModel());
+        });
+        $this->app->bind(FormItemFacade::class,function(){
+            return app()->make(FormItemService::class);
+        });
+
+        $this->app->bind(FormItemDetailService::class,function(){
+            return new FormItemDetailService(new FormItemDetailModel());
+        });
+        $this->app->bind(FormItemDetailFacade::class,function(){
+            return app()->make(FormItemDetailService::class);
+        });
+
+        $this->app->bind(FormContentService::class,function(){
+            return new FormContentService(new FormContentModel());
+        });
+        $this->app->bind(FormContentFacade::class,function(){
+            return app()->make(FormContentService::class);
+        });
+
+        $this->app->bind(FormRecordService::class, function () {
+            return new FormRecordService(new FormRecordModel());
+        });
+        $this->app->bind(FormRecordFacade::class, function () {
+            return app()->make(FormRecordService::class);
+        });
+
+
+
+        $this->app->bind(FormProductDetailFacade::class, function () {
+            return app()->make(FormProductDetailService::class);
+        });
+        $this->app->bind(FormProductDetailService::class, function () {
+            return new FormProductDetailService(new FormProductDetailModel());
+        });
+
+        $this->app->bind(FormProductService::class, function () {
+            return new FormProductService(new FormProductModel());
+        });
+        $this->app->bind(FormProductFacade::class, function () {
+            return app()->make(FormProductService::class);
+        });
+
+    }
+}

+ 39 - 0
app/Form/Services/FormContentService.php

@@ -0,0 +1,39 @@
+<?php
+
+
+namespace App\Form\Services;
+
+use App\Services\CommonUserBaseService;
+use App\Form\Facades\FormItemFacade;
+
+class FormContentService extends CommonUserBaseService
+{
+    /**
+     * 获取列表数据
+     * @param array $recordId
+     * @return array
+     */
+    public function getListByRecordIds(array $recordId)
+    {
+        $list = $this->model->alias('a')
+            ->leftJoin('form_item as b', 'a.form_item_id', '=', 'b.id')
+            ->whereIn('a.record_id',$recordId)
+            ->orderBy('b.sort')->selectRaw('a.*')->get()->toArray();
+        $formItemModel=FormItemFacade::getModel();
+        if (!empty($list)) {
+            $itemIds = array_column($list, 'form_item_id');
+            $items=$formItemModel->whereIn('id',$itemIds)->selectRaw('id,name')->orderBy('sort')->get()->toArray();
+            !empty($items) && $items = mapByKey($items, 'id');
+
+            foreach ($list as &$v) {
+                $v['item_name'] = isset($items[$v['form_item_id']]) ? $items[$v['form_item_id']]['name']: '';
+                $v['content'] = json_decode($v['content'], true);
+            }
+        }
+        $data = [];
+        foreach ($list as $l) {
+            $data[$l['record_id']][] = $l;
+        }
+        return $data;
+    }
+}

+ 340 - 0
app/Form/Services/FormInfoService.php

@@ -0,0 +1,340 @@
+<?php
+
+
+namespace App\Form\Services;
+
+use App\Exceptions\ApiException;
+use App\Services\CommonUserBaseService;
+use App\Form\Facades\FormItemDetailFacade;
+use App\Form\Facades\FormItemFacade;
+use App\Form\Facades\FormRecordFacade;
+use App\Website\Facades\WebsitePageFacade;
+
+class FormInfoService extends CommonUserBaseService
+{
+    protected $cache = true;
+
+    protected $cacheBucket = 'FormInfo:';
+
+    protected $tokenBucket = 'Token:';
+
+    protected $activeBucket = "Active:";
+
+    const DEF_FORM_NAME_EN='表单';
+
+    public function getList($params)
+    {
+        $pageSize = !empty($params['page_size']) ? $params['page_size'] : 10; //页面大小,不传默认一页10条记录
+        $pageNo = !empty($params['page']) ? $params['page'] : 1; //页码,不传默认第1页
+        $skip = ($pageNo - 1) * $pageSize; //页面记录的开始位置,即偏移量
+        $query = $this->getModel()->alias('a');
+        if(!empty($params['status'])){
+            $query= $query->where('a.status','=',$params['status']);
+        }else{
+            $query= $query->where('a.status','<',2);
+        }
+
+        if(!empty($params['keyword'])){
+            $query= $query->where('a.name','like','%'.$params['keyword'].'%');
+        }
+        if(!empty($params['date'])){
+            $date=$params['date'];
+            $beginTime=$date.' 00:00:00';
+            $endTime=$date.' 23:59:59';
+            $query=$query->whereBetween('a.create_time',[$beginTime,$endTime ]);
+        }
+        $totalCount=$query->count();
+        $data=$query->selectRaw('a.*,0 as count,0 as not_read_count')
+            ->skip($skip)
+            ->limit($pageSize)
+            ->orderByDesc('a.data_update_time')
+            ->orderByDesc('a.create_time')
+            ->get()->toArray();
+        $resultData = [];
+        if(!empty($data)){
+            $resultData =  mapByKey($data, 'id');
+            $formIds = array_keys($resultData);
+            $recordCountData = $this->getFormRecordCount($formIds);
+            $recordNotReadCountData = $this->getFormRecordNotReadCount($formIds);
+            $resultData = combineArray($resultData, $recordCountData, 'id');
+            $resultData = combineArray($resultData, $recordNotReadCountData, 'id');
+            $resultData = array_values($resultData);
+        }
+        $result = buildPage($resultData, $skip, $pageNo, $pageSize, $totalCount);
+        return $result;
+    }
+
+    public function setDataUpdateTime($formId){
+        $nowTime=nowTime();
+       $ret= $this->model->where(['id'=>$formId])->update(['data_update_time'=>$nowTime,'update_time'=>$nowTime]);
+       return $ret;
+    }
+
+    public function setFormIdsUpdateTime($formIds){
+        $nowTime=nowTime();
+       $ret= $this->model->whereIn('id',$formIds)->update(['data_update_time'=>$nowTime,'update_time'=>$nowTime]);
+       return $ret;
+    }
+
+    private function getFormRecordCount($formIds)
+    {
+        $formRecordModel = FormRecordFacade::getModel();
+        $data= $formRecordModel->whereIn('form_id',$formIds)
+            ->where('status','<',2)
+            ->selectRaw('form_id as id, count(1) as count')
+            ->groupBy('form_id')->get()->toArray();
+        return $data;
+    }
+    /**
+     * 获取未读询盘数据数量
+     * */
+    private function getFormRecordNotReadCount($formIds)
+    {
+        $formRecordModel = FormRecordFacade::getModel();
+        $data= $formRecordModel->whereIn('form_id',$formIds)
+            ->where('is_read',0)
+            ->where('status','<',2)
+            ->selectRaw('form_id as id, count(0) as not_read_count')
+            ->groupBy('form_id')->get()->toArray();
+        return $data;
+    }
+
+    /**
+     * 获取未读询盘数据总数量
+     * */
+    public function getCompanyFormRecordNotReadCount()
+    {
+        $formRecordModel = FormRecordFacade::getModel();
+        $result= $formRecordModel->alias('a')
+            ->where('a.is_read',0)
+            ->where('b.status','=',0)
+            ->selectRaw('count(0) as not_read_count')
+            ->first()->toArray();
+        return $result;
+    }
+
+    /**
+     * 获取表单数据
+     * @param $id
+     * @return mixed
+     */
+    public function getData($id)
+    {
+        $form = $this->findOneBy(['id' => $id, 'status' => 0], 'id,name');
+        $form['items'] = FormItemFacade::getData($id);
+        return $form;
+    }
+
+    /**
+     * 添加表单信息
+     * @param array $params
+     * @param null $userId
+     * @return mixed
+     */
+    public function addData($params = [], $userId = null)
+    {
+        $userId = $userId ?? $this->getAuthUserId();
+        $data= $this->buildFormData($params,$userId);
+        if(!empty($data['id'])){
+            $id = $data['id'];
+            $this->model->where('id','=',$id)->update($data);
+        } else {
+            $id = $this->save($data)->id;
+        }
+        if(!empty($params['form_items'])){
+             FormItemFacade::addList($id, $params['form_items'], $userId);
+        }
+        return $id;
+    }
+
+    private function buildFormData($params,$userId = null){
+        $userId = $userId ?? $this->getAuthUserId();
+        $add = [];
+        $add['update_time']=nowTime();
+        if(!empty($params['id'])){
+            $add['id']=$params['id'];
+            if(isset($params['name'])){
+                $add['name'] = $params['name'];
+                if(empty($params['name'])){
+                    throw new ApiException(12001);
+                }
+                $nameUnique =$this->model->checkNameUnique($params['name'],$params['id']);
+                if (!$nameUnique) {
+                    throw new ApiException(10018, ['name' => $params['name']]);
+                }
+            }
+        }else{
+            if(empty($params['name'])){
+                throw new ApiException(12001);
+            }
+            $nameUnique =$this->model->checkNameUnique($params['name'],'');
+            if (!$nameUnique) {
+                throw new ApiException(10018, ['name' => $params['name']]);
+            }
+
+            $add['name'] = $params['name'];
+            $add['user_id']=$userId;
+        }
+        return $add;
+    }
+
+    public function setFormByEditor($param, $userId = null)
+    {
+
+        $formParams = $param ?? [];
+        $formParams['name'] = $formParams['form_name'] ?? '';
+        $formParams['id'] = $formParams['form_id'] ?? '';
+        $return = $this->addData($formParams, true,$userId);
+        return $return;
+    }
+
+    /**
+     * 处理页面保存的表单信息
+     * @param $element
+     * @param null $userId
+     * @return mixed
+     */
+    public function processElement($element, $userId = null)
+    {
+        $userId = $userId ?? $this->getAuthUserId();
+        foreach ($element as &$el){
+            if(isset($el['type']) && isset($el['value'])){
+                //如果组件类型是表单
+                if($el['type'] == 'form' || $el['type'] == 'contactForm'){
+                    $el['value'] = $this->setFormByEditor($el['value'],  $userId);
+                } else if($el['type'] == 'itemList' || $el['type'] == 'item'){
+                    foreach ($el['value'] as &$elm){
+                        $elm = $this->processElement($elm, $userId);
+                    }
+                }
+            }
+        }
+        return $element;
+    }
+
+    /**
+     * 添加表单项,单条
+     * @param array $params
+     * @return mixed
+     */
+    public function addItem($params = [])
+    {
+        $userId=$this->getAuthUserId();
+        $formId = $params['form_id'] ?? '';
+        $id=FormItemFacade::addData($params['form_id'] ?? '', $params);
+        if($id&&!empty($params['content'])){
+            FormItemDetailFacade::addList($formId,$id, $params['content'], $userId);
+        }
+        return $id;
+    }
+
+    /**
+     *  删除表单项,单条
+     * */
+    public function delItem($params){
+        $formId = $params['form_id'] ?? '';
+        $itemId = $params['id'] ?? 0;
+        $ret=FormItemFacade::delData($formId,$itemId);
+        return $ret;
+    }
+
+    /**
+     * 表单项排序
+     * @param $id
+     * @return mixed
+     */
+    public function sortItem($id)
+    {
+        return FormItemFacade::sortItem($id);
+    }
+
+    /**
+     * 表单项详情排序
+     * @param $id
+     * @return mixed
+     */
+    public function sortItemDetail($id)
+    {
+        return FormItemDetailFacade::sortItemDetails($id);
+    }
+
+    public function setIsRemove($id){
+        if(!is_array($id)){
+            $id=[$id];
+        }
+       $ret= $this->model->whereIn('id',$id)->update(['is_remove'=>1]);
+       return $ret;
+    }
+
+    /**
+     * 禁用或删除表单
+     * */
+    public function stopAndDelFormById($id,$status=1){
+        $model = $this->getModel();
+        $updateData=[];
+        $updateData['status']=$status;
+        $updateData['update_time']=nowTime();
+        $ret= $model->where('id','=',$id)->update($updateData);
+        return $ret;
+    }
+
+    /**
+     * 当有表单提交的时候重新激活禁用状态的表单
+     * */
+    public function reActivateFormById($id){
+        $model = $this->getModel();
+        $updateData=[];
+        $updateData['status']=0;
+        $updateData['update_time']=nowTime();
+        $ret= $model->where('id','=',$id)->where('status','=','1')->update($updateData);
+        return $ret;
+    }
+    /**
+     * 获取对应公司下询盘列表
+     * */
+    public function correspondCompanyFormList($params)
+    {
+        $pageSize = !empty($params['page_size']) ? $params['page_size'] : 10; //页面大小,不传默认一页10条记录
+        $pageNo = !empty($params['page']) ? $params['page'] : 1; //页码,不传默认第1页
+        $skip = ($pageNo - 1) * $pageSize; //页面记录的开始位置,即偏移量
+        $model = $this->getModel();
+
+
+
+        $query= $model->alias('a')
+            ->leftJoin('website_page as b', 'a.website_page_id', '=', 'b.id')
+            ->where('a.status','=',0);
+        if(!empty($params['keyword'])){
+            $query= $query->where('a.name','like','%'.$params['keyword'].'%');
+        }
+        if(!empty($params['date'])){
+            $date=$params['date'];
+            $beginTime=$date.' 00:00:00';
+            $endTime=$date.' 23:59:59';
+
+            $query=$query->whereBetween('a.create_time',[$beginTime,$endTime ]);
+        }
+
+        $totalCount=$query->count();
+        $data=$query->selectRaw('a.*,b.page_name,0 as count,0 as not_read_count,b.urla,b.link,c.name as website_name ')
+            ->skip($skip)
+            ->limit($pageSize)
+            ->orderByDesc('a.data_update_time')
+            ->orderByDesc('a.create_time')
+            ->get()->toArray();
+        $resultData = [];
+        if(!empty($data)){
+            $resultData =  mapByKey($data, 'id');
+            $formIds = array_keys($resultData);
+            $recordCountData = $this->getFormRecordCount($formIds);
+            $recordNotReadCountData = $this->getFormRecordNotReadCount($formIds);
+            $resultData = combineArray($resultData, $recordCountData, 'id');
+            $resultData = combineArray($resultData, $recordNotReadCountData, 'id');
+            $resultData = array_values($resultData);
+        }
+        $result = buildPage($resultData, $skip, $pageNo, $pageSize, $totalCount);
+        return $result;
+    }
+
+
+}

+ 144 - 0
app/Form/Services/FormItemDetailService.php

@@ -0,0 +1,144 @@
+<?php
+
+
+namespace App\Form\Services;
+
+use App\Services\CommonUserBaseService;
+
+class FormItemDetailService extends CommonUserBaseService
+{
+    protected $cache = true;
+
+    protected $cacheBucket = 'FormItemDetail:';
+
+    protected $tokenBucket = 'Token:';
+
+    protected $activeBucket = "Active:";
+
+    /**
+     * 获取表单项详情数据
+     * @param $formId
+     * @param null $itemId
+     * @param null $formItemDetailId
+     * @return array
+     */
+    public function getData($formId, $itemId = null, $formItemDetailId = null)
+    {
+        $map = [];
+        $map['form_id'] = $formId;
+        if(isset($itemId)){
+            $map['form_item_id'] = $itemId;
+        }
+        if(isset($formItemDetailId)){
+            $formItemDetailId = is_array($formItemDetailId) ? $formItemDetailId : explode(',', $formItemDetailId);
+            $map['id'] = array(['in', $formItemDetailId]);
+        }
+        $map['status'] = $this->model::STATUS_ENABLED;
+        return $this->model->buildQuery($map)->orderBy('sort', 'desc')
+          ->selectRaw('id,content,sort')
+          ->get()->toArray();
+    }
+
+    /**
+     * 添加单项数据
+     * @param $formId
+     * @param $formItemId
+     * @param $item
+     * @param null $userId
+     * @return mixed
+     */
+    public function addData($formId, $formItemId, $item,  $userId = null)
+    {
+        $userId = $userId ?? $this->getAuthUserId();
+        $add = [];
+        $add['form_id'] = $formId;
+        $add['form_item_id'] = $formItemId;
+        $add['content'] = $item['content'] ?? '';
+        if(isset($item['sort'])){
+            $add['sort'] = $item['sort'];
+        }
+        if(!empty($item['id']) && $item['id'] > 0){
+            $id = $item['id'];
+            $add['status'] = $this->model::STATUS_ENABLED;
+            $this->updateBy([
+                'id' => $id,
+            ], $add);
+        } else {
+            $add['user_id'] = $userId;
+            $id = $this->save($add)->id;
+        }
+        return $id;
+    }
+
+    /**
+     * 添加多项数据
+     * @param $formId
+     * @param $formItemId
+     * @param array $items
+     * @param null $userId
+     * @return array
+     */
+    public function addList($formId, $formItemId, $items = [],  $userId = null)
+    {
+        if(!empty($items)){
+            $sort = 1;
+            foreach($items as $i => $item){
+                $item['sort'] = $sort++;
+                $item['id'] = $this->addData($formId, $formItemId, $item, $userId);
+                $items[$i] = $item;
+            }
+            $ids = array_column($items, 'id');
+            $allIds = $this->getData($formId, $formItemId);
+            $allIds = array_column($allIds, 'id');
+            $delIds = array_diff($allIds, $ids);
+            if(!empty($delIds)){
+                //删除已取消的数据
+                $this->delItemDetails($formId, $formItemId, $delIds);
+            }
+        }
+        return $items;
+    }
+
+    /**
+     * 排序
+     * @param $ids
+     * @return int
+     */
+    public function sortItemDetails($ids)
+    {
+        $sort = 1;
+        foreach ($ids as $id){
+            $this->updateBy([
+                'id' => $id,
+                'status' => $this->model::STATUS_ENABLED
+            ], ['sort' => $sort++]);
+        }
+        return $sort;
+    }
+
+    /**
+     * 删除表单项目
+     * @param $formId
+     * @param null $itemId
+     * @param null $formItemDetailId
+     * @return bool
+     */
+    public function delItemDetails($formId, $itemId = null, $formItemDetailId = null)
+    {
+        $map = [];
+        $map['form_id'] = $formId;
+        //删除整个表单项
+        if(isset($itemId)){
+            $itemId = is_array($itemId) ? $itemId : explode(',', $itemId);
+            $map['form_item_id'] = array(['in', $itemId]);
+        }
+        //删除单个表单项
+        if(isset($formItemDetailId)){
+            $formItemDetailId = is_array($formItemDetailId) ? $formItemDetailId : explode(',', $formItemDetailId);
+            $map['id'] = array(['in', $formItemDetailId]);
+        }
+        return $this->updateBy($map,
+            ['status' => $this->model::STATUS_DELETED]
+        );
+    }
+}

+ 188 - 0
app/Form/Services/FormItemService.php

@@ -0,0 +1,188 @@
+<?php
+
+
+namespace App\Form\Services;
+
+use App\Services\CommonUserBaseService;
+use App\Form\Facades\FormItemDetailFacade;
+
+class FormItemService extends CommonUserBaseService
+{
+    const TYPE_RADIO = 200;
+    const TYPE_CHECKBOX = 201;
+    const TYPE_SELECT = 202;
+    protected $cache = true;
+    protected $cacheBucket = 'FormItem:';
+    protected $tokenBucket = 'Token:';
+    protected $activeBucket = "Active:";
+
+    /**
+     * 添加多项数据
+     * @param $formId
+     * @param array $items
+     * @param null $userId
+     * @return array
+     */
+    public function addList($formId, $items = [], $userId = null)
+    {
+        if (!empty($items)) {
+            $sort = 1;
+            foreach ($items as $k => $item) {
+                $item['sort'] = $sort++;
+                $item['id'] = $this->addData($formId, $item, $userId);
+                if (!empty($item['content']) && is_array($item['content'])) {
+                    $item['content'] = FormItemDetailFacade::addList($formId, $item['id'], $item['content'], $userId);
+                }
+                $items[$k] = $item;
+            }
+            $ids = array_column($items, 'id');
+            $allIds = $this->getData($formId);
+            $allIds = array_column($allIds, 'id');
+            $delIds = array_diff($allIds, $ids);
+            if (!empty($delIds)) {
+                //删除已取消的数据
+                $this->delData($formId, $delIds);
+            }
+        }
+        return $items;
+    }
+
+    /**
+     * 添加表单项数据
+     * @param $formId
+     * @param $item
+     * @param null $userId
+     * @return mixed
+     */
+    public function addData($formId, $item, $userId = null)
+    {
+        $userId = $userId ?? $this->getAuthUserId();
+        $add = [];
+        $add['form_id'] = $formId;
+        if (isset($item['type'])) {
+            $add['type'] = $item['type'];
+        }
+        if (isset($item['sort'])) {
+            $add['sort'] = $item['sort'];
+        }
+        if (isset($item['name'])) {
+            $add['name'] = $item['name'];
+        }
+        if (isset($item['is_required'])) {
+            $add['is_required'] = empty($item['is_required']) ? '0' : $item['is_required'];
+        }
+        if (isset($item['is_email'])) {
+            $add['is_email'] = empty($item['is_email']) ? '0' : '1';
+        }
+        if (isset($item['is_phone'])) {
+            $add['is_phone'] = $item['is_phone'];
+        }
+        if (isset($item['is_url'])) {
+            $add['is_url'] = $item['is_url'];
+        }
+        if (isset($item['is_min'])) {
+            $add['is_min'] = $item['is_min'];
+        }
+        if (isset($item['is_max'])) {
+            $add['is_max'] = $item['is_max'];
+        }
+        if (isset($item['min_val'])) {
+            $add['min_val'] = $item['min_val'];
+        }
+        if (isset($item['max_val'])) {
+            $add['max_val'] = $item['max_val'];
+        }
+        if (isset($item['status'])) {
+            $add['status'] = $item['status'];
+        }
+        if (!empty($item['id']) && $item['id'] > 0) {
+            $id = $item['id'];
+            $this->model->where('id', '=', $id)->update($add);
+        } else {
+            $add['user_id'] = $userId;
+            $id = $this->save($add)->id;
+        }
+        return $id;
+    }
+
+    /**
+     * 获取表单项数据
+     * @param $formId
+     * @return array
+     */
+    public function getData($formId)
+    {
+        $map = [];
+        $map['form_id'] = $formId;
+        $map['status'] = $this->model::STATUS_ENABLED;
+        $items = $this->model->buildQuery($map)
+            ->selectRaw('id,form_id,type,sort,name,is_required,is_email,is_phone,is_url,is_min,is_max,min_val,max_val,is_code_valid,create_time')
+            ->orderBy('sort', 'desc')
+            ->get()->toArray();
+        if (!empty($items)) {
+            foreach ($items as &$item) {
+                if ($item['type'] == self::TYPE_RADIO || $item['type'] == self::TYPE_CHECKBOX || $item['type'] == self::TYPE_SELECT) {
+                    $itemDetail = FormItemDetailFacade::getData($formId, $item['id']);
+                    if (!empty($itemDetail)) {
+                        usort($itemDetail, function($a, $b) {
+                            return $a['sort'] <=> $b['sort'];
+                        });
+                        $item['item_options'] = $itemDetail;
+                    }
+                }
+            }
+        }
+        return $items;
+    }
+
+    /**
+     * 删除数据
+     * @param $formId
+     * @param $itemId
+     * @return bool
+     */
+    public function delData($formId, $itemId)
+    {
+        $map = [];
+        $map['form_id'] = $formId;
+        //删除整个表单项
+        if (isset($itemId) && !empty($itemId)) {
+            if (is_array($itemId)) {
+                $map['id'] = [['in', $itemId]];
+            } else {
+                $map['id'] = $itemId;
+            }
+        }
+        FormItemDetailFacade::delItemDetails($formId, $itemId);
+        return $this->updateBy($map,
+            ['status' => $this->model::STATUS_DELETED]
+        );
+    }
+
+    /**
+     * 排序
+     * @param $ids
+     * @return array|int
+     */
+    public function sortItem($ids)
+    {
+        $sort = 1;
+        foreach ($ids as $id) {
+            $this->updateBy([
+                'id' => $id,
+                'status' => $this->model::STATUS_ENABLED
+            ], ['sort' => $sort++]);
+        }
+        return [
+            'code' => 0,
+            'msg' => '排序成功'
+        ];
+    }
+
+    public function getItemsByFromId($fromId)
+    {
+        $data = $this->model->where(['form_id' => $fromId, 'status' => 0])->orderBy('sort')->get()->toArray();
+        return $data;
+    }
+
+}

+ 57 - 0
app/Form/Services/FormProductDetailService.php

@@ -0,0 +1,57 @@
+<?php
+
+
+namespace App\Form\Services;
+
+use App\Services\CommonUserBaseService;
+use App\Web\Facades\ProductFacade;
+
+
+class FormProductDetailService extends CommonUserBaseService
+{
+    protected $cache = true;
+
+    protected $cacheBucket = 'FormProductDetail:';
+
+    public function relationProductDetail($params){
+        if(!empty($params['product_ids'])){
+            foreach ($params['product_ids'] as $value){
+                $data['form_product_id'] = $params['form_product_id'];
+                $data['product_id'] = $value;
+                $this->addData($data);
+            }
+            return true;
+        }else{
+            return false;
+        }
+    }
+    public function addData($item){
+        $add = [];
+        $add['product_id'] = $item['product_id'];
+        $add['form_product_id'] = $item['form_product_id'];
+        $add['status'] = 0;
+        if(!empty($item['id']) && $item['id'] > 0){
+            $id = $item['id'];
+            $this->updateBy([
+                'id' => $id
+            ], $add);
+        } else {
+            $id = $this->save($add)->id;
+        }
+        return $id;
+    }
+    /**
+     * 根据FormProductId 获取产品详情
+     * */
+    public function getListByRelativeIds($formProductIds){
+        $formProductDetailData= $this->model->whereIn('form_product_id',$formProductIds)
+            ->selectRaw('id,form_product_id,product_id')->get()->toArray();
+        foreach ($formProductDetailData as $value){
+            $productIds[$value['form_product_id']][]=$value['product_id'];
+        }
+        foreach ($productIds as $key=>$ids){
+            $data[$key] = ProductFacade::getProductByIds($ids);
+        }
+        return $data;
+    }
+}

+ 40 - 0
app/Form/Services/FormProductService.php

@@ -0,0 +1,40 @@
+<?php
+
+
+namespace App\Form\Services;
+
+
+use App\Form\Facades\FormProductDetailFacade;
+use App\Services\CommonUserBaseService;
+
+class FormProductService extends CommonUserBaseService
+{
+    protected $cache = true;
+
+    protected $cacheBucket = 'FormProduct:';
+
+    public function addFormProduct($params){
+
+        $add['form_id'] = $params['form_id'];
+        if(isset($params['status'])){
+            $add['status'] = empty($params['status'])?'0':$params['status'];
+        }else{
+            $add['status'] = 0;
+        }
+        if(!empty($params['id']) && $params['id'] > 0){
+            $id = $params['id'];
+            $this->updateBy([
+                'id' => $id,
+            ], $add);
+        } else {
+            $id = $this->save($add)->id;
+        }
+        if(!empty($id)){
+            $data['product_ids']=$params['product_ids'];
+            $data['form_product_id']=$id;
+            FormProductDetailFacade::relationProductDetail($data);
+        }
+        return $id;
+    }
+
+}

+ 813 - 0
app/Form/Services/FormRecordService.php

@@ -0,0 +1,813 @@
+<?php
+
+
+namespace App\Form\Services;
+
+use App\Common\Facades\ComSmsFacade;
+use App\Common\Library\DES;
+use App\Exceptions\ApiException;
+use App\Form\Facades\FormProductDetailFacade;
+use App\Form\Facades\FormProductFacade;
+use App\Form\Models\FormRecordModel;
+use App\Services\CommonUserBaseService;
+use App\Form\Facades\FormContentFacade;
+use App\Form\Facades\FormInfoFacade;
+use App\Form\Facades\FormItemFacade;
+use App\Web\Facades\SysGlobalConfigFacade;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Mail;
+
+class FormRecordService extends CommonUserBaseService
+{
+    protected $cache = true;
+
+    protected $cacheBucket = 'FormRecord:';
+
+    const RELATIVE_TABLE_ORDER = 'website_order';
+
+    const RELATIVE_FORM_PRODUCT = 'form_product';
+
+    const DES_KEY = '1dgDG2ksghw';
+
+    const SYS_MAIL_BOX = 'cXdWWktRNno0eitSL1Q1LzVKalRnUUlleTdESzZ5S2NDdW89OmZhY3A3WjNza0x6YmdJb0U0djlCR3c9PQ==';
+
+    public function __construct(FormRecordModel $model)
+    {
+        parent::__construct($model);
+        $this->cacheBucket = config('cache.prefix').'_'.$this->cacheBucket;
+    }
+
+
+    /**
+     * 获取列表数据
+     * @param $params
+     */
+    public function getList($params,$getQuery = false)
+    {
+        $formId = $params['form_id'] ?? '';
+        $keyword = $params['keyword'] ?? '';
+        $pageSize = $params['page_size'] ?? 0;
+        $date = $params['date'] ?? '';
+        $formatList = $params['format_list'] ?? true;//是否进行格式化列表
+        $order = $params['order'] ?? 'desc';
+        $sort = $params['sort'] ?? 'id';
+        $status = $params['status'] ?? '';
+        $formId && $where['r.form_id'] = $formId;
+        $keyword && $where['c.content'] = [['like', "%{$keyword}%"]];
+        !empty($date) &&  $where['r.create_time'] = [['between', [$date.' 00:00:00', $date.' 23:59:59']]];
+        if ($status) {
+            is_array($status) && $status = [$status];
+        } else {
+            $status = [['<', 2]];
+        }
+        if(!empty($params['id_list'])){
+            $where['r.id']=[['in',$params['id_list']]];
+        }
+        $where['r.status'] = $status;
+
+        $fields = 'r.*,c.content';
+        $model = $this->getModel();
+        $model = $model->buildQuery($where)
+            ->from($this->getTable().' as r')
+            ->select(DB::raw($fields))
+            ->leftJoin('form_content as c', function ($join) {
+            return $join->on('r.id', '=', 'c.record_id');
+        });
+
+        $model = $model->groupBy('r.id');
+        if ($sort) {
+            $model = $model->orderBy('is_read', 'asc')->orderBy($sort, $order);
+        }
+        if ($getQuery) {
+            return $model;
+        }
+        if ($pageSize) {
+            $rst = $model->paginate($pageSize)->toArray();
+            $formatList && $this->formatList($rst['data']);
+        } else {
+            $rst = $model->get()->toArray();
+            $formatList && $this->formatList($rst);
+        }
+        return $rst;
+    }
+
+    /**
+     * 格式化列表
+     * @param $list
+     */
+    public function formatList(&$list)
+    {
+        if (empty($list)) {
+            return ;
+        }
+        $recordIds = array_column($list, 'id');
+        //获取所有表单内的内容
+        $contentList = FormContentFacade::getListByRecordIds($recordIds);
+        $formProductIds = [];
+        foreach ($list as $value){
+            if($value['relative_table'] == FormRecordService::RELATIVE_FORM_PRODUCT){
+                $formProductIds[] = $value['relative_id'];
+            }
+        }
+        if (!empty($formProductIds)){
+            $formProductList = FormProductDetailFacade::getListByRelativeIds($formProductIds);
+        }
+        foreach ($list as &$v) {
+            $v['items'] = $contentList[$v['id']] ?? [];
+            $v['content'] = json_decode($v['content'], true);
+            is_array($v['content']) && $v['content'] = implode(';', $v['content']);
+            if($v['relative_table'] == FormRecordService::RELATIVE_FORM_PRODUCT && !empty($formProductList)){
+                $v['product_list'] = $formProductList[$v['relative_id']];
+                if(!empty($v['product_list'])){
+                    $v['is_product'] = 1;
+                }else{
+                    $v['is_product'] = 0;
+                }
+            }else{
+                $v['is_product'] = 0;
+            }
+        }
+    }
+
+    /**
+     * 导出数据
+     * @param $params
+     */
+    public function exportList($params)
+    {
+        $list = $this->getList($params);
+       if (!empty($list)) {
+            $ids = array_column($list, 'id');
+            $itemList = FormContentFacade::getListByRecordIds([$ids]);
+            foreach ($list as &$v) {
+                if (isset($itemList[$v['id']])) {
+                    $v['items'][] = $v;
+                } else {
+                    $v['items'][] = [];
+                }
+            }
+        }
+        return $list;
+        //导出excel处理
+    }
+
+    /**
+     * 获取记录
+     * @param $id
+       * @return mixed
+     */
+    public function getRecord($id)
+    {
+        $data = $this->findOneBy([
+            'id' => $id,
+        ]);
+        if (empty($data)) {
+            return [];
+        }
+        $itemList = FormContentFacade::getListByRecordIds([$id]);
+        $data['items'] = $itemList[$id] ?? [];
+        return $data;
+    }
+
+    public function addRecord($params)
+    {
+        $params['id'] = !empty($params['id']) ? $params['id'] : getClientIp(0, true);
+        $ret= $this->addRecordJob($params);
+        return $ret;
+    }
+
+    /**
+     * 添加记录
+     * @param $params
+     * @return int
+     * @throws ApiException
+     */
+    public function addRecordJob($params)
+    {
+        $formId = $params['form_id'] ?? '';
+        $formData = $params['form_data'] ?? [];//表单数据
+        $relativeId =  $params['r_id'] ?? 0;//关联表id
+        $relativeTable =  $params['r_ta'] ?? '';//关联表
+        $productIds =  $params['product_ids'] ?? '';//产品id数组
+        //自定义表单数据处理
+        if(empty($formData)){
+            $formData = [];
+            foreach ($params as $k => $v){
+                if(strpos($k, '_') !== false){
+                    if(strpos($k, 'option') !== false){
+                        $options = explode('_', $k);
+                        if(isset($options[1]) && isset($options[2])){
+                            $formData[$options[1]][] =  $options[2].':'.$v.';';
+                        }
+                        if(isset($options[1]) && !isset($options[2])){
+                            $formData[$options[1]] = $v ?? '';
+                        }
+
+                    } else if(strpos($k, 'name') !== false){
+                        $options = explode('_', $k);
+                        if(isset($options[0])){
+                            $formData[$options[1]] = $v ?? '';
+                        }
+                    }
+                }
+            }
+
+            if(!empty($formData)){
+                foreach ($formData as $m => $n){
+                    if(is_array($n)){
+                        $formData[$m] = implode('', $n);
+                    }
+                }
+            }
+        }
+
+        $formInfo = FormItemFacade::getData($formId);
+        if (empty($formInfo)) {
+            throw new ApiException(1000);
+        }
+
+
+        $data = [];
+        $data_to=[];
+        foreach ($formInfo as $v) {
+            $content = $formData[$v['id']] ?? '';
+            if ($v['is_required'] && $content === '') {
+                throw new ApiException(1002);
+            }
+            if ($v['is_email']&&!empty(trim($content)) && strpos($content, '@') === false) {
+                throw new ApiException(1002);
+            }
+            if($v['is_phone']&&!empty($v['is_code_valid'])){
+                //短信验证
+
+                if(empty($params['valid_code'])){
+                    throw new ApiException(10021);
+                }
+                $validateCode=empty($params['valid_code'])?'':$params['valid_code'];
+                $smsRet = ComSmsFacade::validateCode(trim($content), $validateCode);
+                if ($smsRet['code'] !== 1) {
+                    if ($smsRet['code'] == -2) {
+                        throw new ApiException(10002);
+                    } else {
+                        throw new ApiException(10003);
+                    }
+                }
+            }
+            if ($v['is_max'] && $content > $v['max_val']) {
+                throw new ApiException(1002);
+            }
+            if ($v['is_min'] && $content < $v['is_min']) {
+                throw new ApiException(1002);
+            }
+            $data[] = [
+                'form_id' => $formId,
+                'form_item_id' => $v['id'],
+                'content' => json_encode($content, JSON_UNESCAPED_UNICODE)
+            ];
+            $data_to[] = [
+                'name' => $v['name'],
+                'content' => json_encode($content, JSON_UNESCAPED_UNICODE)
+            ];
+
+        }
+        if (!empty($productIds)){
+            $formProductData['product_ids'] = $productIds;
+            $formProductData['form_id'] = $formId;
+            $formProductId = FormProductFacade::addFormProduct($formProductData);
+            // 产品表单提交,重新组成关联表和关联表id
+            $relativeId =  $formProductId;//关联表id
+            $relativeTable =  FormRecordService::RELATIVE_FORM_PRODUCT;//关联表
+        }
+        $record = $this->save([
+            'form_id' => $formId,
+            'relative_id' => $relativeId,
+            'relative_table' => $relativeTable,
+            'ip' => $params['ip'] ?? getClientIp(0, true),
+            'status' => 0
+        ]);
+        if (empty($record)) {
+            throw new ApiException(1001);
+        }
+        FormInfoFacade::setDataUpdateTime($formId);
+        foreach ($data as &$v) {
+            $v['record_id'] = $record['id'];
+        }
+        FormContentFacade::saveAll($data);
+        if(!empty($formId)){
+            FormInfoFacade::reActivateFormById($formId);
+        }
+        // todo 系统通知邮箱
+        //系统通知邮箱
+        $referer = empty($params['referer']) ? '' : $params['referer'];
+        $this->addToEmailSendRedis($record['id'], $referer, $data_to);
+        return $record['id'];
+    }
+
+    /**
+     * 发询盘邮件
+     * @param $data
+     * @return mixed
+     * */
+    public function mailInvite($data){
+       // print_r($data);die;
+        $mail = $data['email'];
+        $mail_user = $data['mail_user'];
+        $mail_pwd = $data['mail_pwd'];
+        $smtp_host = $data['smtp_host'];
+        $smtp_port = $data['smtp_port'];
+        $title='您有一封新的询盘邮件';
+        $httpData['data'][]='';
+        foreach (array_reverse($data['data']) as $v) {
+            $httpData['data'][] = $v['name'] .':'.$v['content'];
+        }
+        $httpData['name']= $data['name'];
+        $httpData['referer']= empty($data['referer'])?'':$data['referer'];
+
+        // 邮件底部信息
+        $httpData['logo']= 'https://oss.matchpages.cn/matchpages/share_center/2023/0323/1821/641bc6b30f79f/logo-starify2X.webp';
+
+        $mailSwift=Mail::getSwiftMailer();
+        $mailgunTransport = $mailSwift->getTransport();
+        $mailgunTransport->setUsername($mail_user);
+        $mailgunTransport->setPassword($mail_pwd);
+        $mailgunTransport->setHost($smtp_host);
+        $mailgunTransport->setPort($smtp_port);
+        if($smtp_port==465){
+            $mailgunTransport->setEncryption('ssl');
+        }else{
+            $mailgunTransport->setEncryption('tls');
+        }
+        Mail::send('mail.inviteModel', $httpData, function ($message) use ($mail,$title,$mail_user) {
+            $message->from($mail_user,$mail_user);
+            $message->to($mail)->subject($title);
+        });
+
+      /*  $mailgunDomainArray=config('mail.mailgun_domain_array');
+        $email_from_config = config('mail.from');
+        $email_from = $email_from_config[mt_rand(0, (count($email_from_config) - 1))];
+        $mailSwift= Mail::getSwiftMailer();
+        $mailgunTransport = $mailSwift->getTransport();
+        $keys=array_keys($mailgunDomainArray);
+        $max= count($keys)-1;
+        $index= $keys[mt_rand(0, $max)];
+        $sendDomain=$mailgunDomainArray[$index]['domain'];
+        $mailgunTransport->setDomain($sendDomain);
+        $mailgunTransport->setKey($mailgunDomainArray[$index]['secret']);
+        Mail::send('mail.invitationModel',$httpData,
+            function($message)use($mail,$email_from,$title){
+                $message->from(trim($email_from['address']),$email_from['name']);
+                $message->to(trim($mail))->subject($title);
+            });
+        $error= Mail::failures();
+        if (!empty($error)) {
+            Log::info('mail_error:'.json_encode($error));
+        }*/
+      /*  Mail::send('mail.inviteModel', $httpData, function ($message) use ($mail,$title) {
+            $message->to($mail)->subject($title);
+        });*/
+    }
+
+    /**
+     * 删除记录
+     * @param $id
+     */
+    public function changeStatus($id, $status)
+    {
+        if(!is_array($id)){
+            $id=[$id];
+        }
+        $where = [
+            'id' => [['in',$id]]
+        ];
+        $data = [
+            'status' => $status
+        ];
+        return $this->updateBy($where, $data);
+
+    }
+
+    /**
+     * 根据id列表获取记录
+     * */
+    public function getRecordByIds($ids){
+        if(!is_array($ids)){
+            $ids=[$ids];
+        }
+        $itemList = FormContentFacade::getListByRecordIds($ids);
+        return $itemList;
+    }
+
+    /**
+     * 根据订单id获取对应提交表单数据
+     *
+     * */
+    public function getRecordDataByRelativeTable($relativeId,$relativeTable){
+        $list=[];
+        $data = [];
+        $where=[];
+        $where['relative_table']=$relativeTable;
+        if(!is_array($relativeId)){
+            $relativeId=[$relativeId];
+        };
+        $formData= $this->model->where($where)
+            ->whereIn('relative_id',$relativeId)
+            ->selectRaw('id,relative_id')->get();
+        if(!empty($formData)){
+            $formData=  $formData->toArray();
+            if(!empty($formData)){
+                foreach ($formData as $v){
+                    $ids[]=$v['id'];
+                    $rids[$v['id']]=$v['relative_id'];
+                }
+                $list= $this->getRecordByIds($ids);
+            }
+            foreach ($list as $k=>$l) {
+                $data[$rids[$k]] = $l;
+            }
+        }
+        return $data;
+    }
+    /**
+     * 询盘数据设置为已读
+     * */
+    public function recordSetIsRead($ids){
+        $formData= $this->model
+            ->whereIn('id',$ids)
+            ->pluck('form_id');
+        $formIds = array_unique(json_decode($formData));
+        FormInfoFacade::setFormIdsUpdateTime($formIds);
+        $updateData=[];
+        $updateData['is_read']=1;
+        if(!empty($ids)){
+            $ret = $this->model
+                ->whereIn('id',$ids)
+                ->update($updateData);
+        }else{
+            $ret=[];
+        }
+        return $ret;
+    }
+
+
+    /**
+     * 用于记录恢复
+     * @param $websiteId
+     * @param $formId
+     * @return mixed
+     */
+    public function getRecordByTmp($formId)
+    {
+        return $records = $this->model->buildQuery(
+            [
+                'form_id' => $formId,
+                'status' => 0,
+                'create_time' => array(['>', '2021-09-01'])
+            ]
+        )
+            ->get()->toArray();
+    }
+
+    /**
+     * 用于记录恢复
+     * @param $params
+     * @throws ApiException
+     */
+    public function addRecordJobTmp($params)
+    {
+        $formId = $params['form_id'] ?? '';
+        $formData = $params['form_data'] ?? [];//表单数据
+        $relativeId =  $params['r_id'] ?? 0;//关联表id
+        $relativeTable =  $params['r_ta'] ?? '';//关联表
+        //自定义表单数据处理
+        if(empty($formData)){
+            $formData = [];
+            foreach ($params as $k => $v){
+                if(strpos($k, '_') !== false){
+                    if(strpos($k, 'option') !== false){
+                        $options = explode('_', $k);
+                        if(isset($options[1]) && isset($options[2])){
+                            $formData[$options[1]][] =  $options[2].':'.$v.';';
+                        }
+                        if(isset($options[1]) && !isset($options[2])){
+                            $formData[$options[1]] = $v ?? '';
+                        }
+
+                    } else if(strpos($k, 'name') !== false){
+                        $options = explode('_', $k);
+                        if(isset($options[0])){
+                            $formData[$options[1]] = $v ?? '';
+                        }
+                    }
+                }
+            }
+
+            if(!empty($formData)){
+                foreach ($formData as $m => $n){
+                    if(is_array($n)){
+                        $formData[$m] = implode('', $n);
+                    }
+                }
+            }
+        }
+        $formInfo = FormItemFacade::getData($formId);
+
+        if (empty($formInfo)) {
+            throw new ApiException(1000);
+        }
+        $data = [];
+        foreach ($formInfo as $v) {
+            $content = $formData[$v['id']] ?? '';
+            if ($v['is_required'] && $content === '') {
+                throw new ApiException(1002);
+            }
+            if ($v['is_email']&&!empty(trim($content)) && strpos($content, '@') === false) {
+                throw new ApiException(1002);
+            }
+            if ($v['is_max'] && $content > $v['max_val']) {
+                throw new ApiException(1002);
+            }
+            if ($v['is_min'] && $content < $v['is_min']) {
+                throw new ApiException(1002);
+            }
+            $data[] = [
+                'form_id' => $formId,
+                'form_item_id' => $v['id'],
+                'content' => json_encode($content, JSON_UNESCAPED_UNICODE)
+            ];
+            $data_to[] = [
+                'name' => $v['name'],
+                'content' => json_encode($content, JSON_UNESCAPED_UNICODE)
+            ];
+
+        }
+
+//        $relativeId =  $params['r_id'] ?? 0;//关联表id
+//        $relativeTable =  $params['r_ta'] ?? '';//关联表
+
+        $recordId = $params['record_id'] ?? 0;
+        if(!empty($params['remote_addr'])){
+            $this->model->buildQuery(['id' => $recordId])
+                ->update(['ip' => $params['remote_addr']]);
+        }
+        // print_r($recordId);
+
+        foreach ($data as &$v) {
+            $v['record_id'] = $recordId;
+        }
+        FormContentFacade::saveAll($data);
+        if(!empty($formId)){
+            FormInfoFacade::reActivateFormById($formId);
+        }
+
+        FormInfoFacade::setDataUpdateTime($formId);
+    }
+
+    /**
+     * 获取列表数据
+     * @param $params
+     */
+    public function getFormRecordList($params, $getQuery = false)
+    {
+        if(!empty($params['id'])){
+            $where['r.form_id']=[['in',$params['id']]];
+        }
+        $where['r.status'] = 0;
+        $where['b.status'] = 0;
+        $fields = 'r.*,c.content';
+        $model = $this->getModel();
+        $model = $model->buildQuery($where)
+            ->from($this->getTable().' as r')
+            ->select(DB::raw($fields))
+            ->leftJoin('form_content as c', function ($join) {
+                return $join->on('r.id', '=', 'c.record_id');
+            });
+
+        $model = $model->groupBy('r.id');
+        $rst = $model->get()->toArray();
+        return $rst;
+    }
+
+    /**
+     * @param $ip
+     * @param $formId
+     * @param $limit
+     * @return bool
+     */
+    public function countRecordByIp($ip, $formId, $limit = 100)
+    {
+        $count = 0;
+        if($formId) {
+            $count = $this->count([
+                'form_id' => $formId,
+                'ip' => $ip,
+                'create_time' => array([
+                    '>',
+                    date('Y-m-d',
+                        strtotime('-1 day', time()))
+                ])
+            ]);
+        }
+        return $count < $limit;
+    }
+
+    /**
+     * 添加表单记录
+     * */
+    public function addFormRecord($params,$ip){
+        try {
+            $formId = $params['form_id'] ?? 0;
+            $rst = $this->countRecordByIp($ip, $formId, 10);
+            if($rst) {
+                $params['ip']=$ip;
+                $rst = $this->addRecord($params);
+            } else {
+                 Log::info('检测到该IP恶意提交'.$ip.',addRecordFail::::'.json_encode($params));
+            }
+            return $rst;
+        } catch (\Exception $e){
+            $code=$e->getCode();
+            if($code==1001){
+                $params['ip']=$ip;
+                $this->addFormRecordToRedis($params);
+            }
+            throw new ApiException(1004,['msg'=> $e->getMessage()]);
+        }
+    }
+
+    /**
+     * 保存失败的输入扔入 redis
+     * @param $method
+     * @param $url
+     * @param $params
+     * @return mixed
+     */
+    public function addFormRecordToRedis($params)
+    {
+        $cacheKey = $this->cacheBucket.'form_record_list';
+        $redis = app('redis')->connection('default');
+        $data = json_encode(
+            array([
+                'params' => $params
+            ])
+        );
+        return $redis->rpush($cacheKey, $data);
+    }
+
+    /**
+     * 将Redis缓存的数据存入数据库
+     */
+    public function processRedisFormRecord()
+    {
+        set_time_limit(0);
+        ignore_user_abort(true);
+        $processingKey =  $this->cacheBucket.'form_record_list_processing';
+        $reProcessing = Cache::get($processingKey);
+        if($reProcessing){
+            return 'processing......';
+        }
+        Cache::put($processingKey, 1, 60*12);
+        $cacheKey =  $this->cacheBucket.'form_record_list';
+        $redis = app('redis')->connection('default');
+        $length = $redis->llen($cacheKey);
+        $data = $redis->blpop($cacheKey, 1);
+        $i = 0;
+        while($data && $data[1]){
+            $data[1] = json_decode($data[1], true);
+            if(!empty($data[1][0]) && !empty($data[1][0]['params'])){
+                try {
+                    $this->addRecord($data[1][0]['params']);
+                } catch (\Exception $e){
+                    $this->addFormRecordToRedis($data[1][0]['params']);
+                }
+            }
+            //每次运行最多处理8000条数据
+            if($i < $length && $i < 8000){
+                $data = $redis->blpop($cacheKey, 1);
+            } else {
+                break;
+            }
+            usleep(50);
+            $i++;
+        }
+        Cache::forget($processingKey);
+        return $i;
+    }
+
+    /**
+     * 把需要发送邮件的表单输入扔入redis
+     * @param $recordId
+     * @param $referer
+     * @param $dataTo
+     * @param $processCount
+     * @return mixed
+     */
+    public function addToEmailSendRedis($recordId, $referer, $dataTo, $processCount = 0)
+    {
+        $cacheKey = $this->cacheBucket.'record_send_list';
+        $redis = app('redis')->connection('default');
+        $data = json_encode(
+            array([
+                'record_id' => $recordId, //表单记录ID
+                'referer' => $referer, //表单引用地址
+                'data_to' => $dataTo, //数据指向
+                'process_count' => $processCount //执行次数记录
+            ])
+        );
+        return $redis->rpush($cacheKey, $data);
+    }
+
+    /**
+     * 系统邮箱通知
+     * @param $recordId
+     * @param $dataTo
+     * @return int
+     */
+    public function sendRecordToEmail($recordId, $referer, $dataTo)
+    {
+        $globalParams = [];
+        $globalData = SysGlobalConfigFacade::getGlobalConfig($globalParams);
+        $globalData = mapByKey($globalData, 'global_key');
+        $sysDesKey = config('mail.des_key');
+        $sysMailBox = config('mail.sys_mail_box');
+        if (empty($sysDesKey)) {
+            $sysDesKey = self::DES_KEY;
+        }
+        if (empty($sysMailBox)) {
+            $sysMailBox = self::SYS_MAIL_BOX;
+        }
+        if (!empty($globalData['FORM_RECEIVE_MAIL']['value']) && !empty($recordId)) { // 插入数据成功,并且有邮箱。发送邮件
+            $mail_pwd = empty($globalData['FORM_RECEIVE_MAIL']['value']['mail_pwd']) ? '' : $globalData['FORM_RECEIVE_MAIL']['value']['mail_pwd'];
+            $mail_user = empty($globalData['FORM_RECEIVE_MAIL']['value']['mail_user']) ? '' : $globalData['FORM_RECEIVE_MAIL']['value']['mail_user'];
+            $smtp_host = empty($globalData['FORM_RECEIVE_MAIL']['value']['smtp_host']) ? '' : $globalData['FORM_RECEIVE_MAIL']['value']['smtp_host'];
+            $smtp_port = empty($globalData['FORM_RECEIVE_MAIL']['value']['smtp_port']) ? '' : $globalData['FORM_RECEIVE_MAIL']['value']['smtp_port'];
+            if (!empty($mail_pwd) && !empty($mail_user) && !empty($smtp_host) && !empty($smtp_port)) {
+                $getter = new DES($sysDesKey);
+                if ($mail_user == $getter->decrypt($sysMailBox)) {
+                    $mail_pwd = $getter->decrypt($mail_pwd);
+                }
+                foreach ($globalData['FORM_RECEIVE_MAIL']['value']['to_mail'] as $value) {
+                    $mail_data['email'] = $value;
+                    $mail_data['name'] = $value;
+                    $mail_data['mail_user'] = $mail_user;
+                    $mail_data['mail_pwd'] = $mail_pwd;
+                    $mail_data['smtp_host'] = $smtp_host;
+                    $mail_data['smtp_port'] = $smtp_port;
+                    $mail_data['referer'] = $referer;
+                    $mail_data['data'] = $dataTo;
+                    $this->mailInvite($mail_data);
+                }
+            }
+        }
+    }
+
+    /**
+     * @return int|string
+     */
+    public function processRecordEmailSend()
+    {
+        set_time_limit(0);
+        ignore_user_abort(true);
+        $processingKey = $this->cacheBucket . 'record_email_send_processing';
+        $reProcessing = Cache::get($processingKey);
+        if ($reProcessing) {
+            return 'processing......';
+        }
+        Cache::put($processingKey, 1, 60 * 12);
+        $cacheKey = $this->cacheBucket . 'record_send_list';
+        $redis = app('redis')->connection('default');
+        $length = $redis->llen($cacheKey);
+        $data = $redis->blpop($cacheKey, 1);
+        $i = 0;
+        while ($data && $data[1]) {
+            $data[1] = json_decode($data[1], true);
+            if (!empty($data[1][0]) && !empty($data[1][0]['record_id'])) {
+                $recordId = $data[1][0]['record_id'];
+                $referer = $data[1][0]['referer'];
+                $dataTo = $data[1][0]['data_to'];
+                $processCount = $data[1][0]['process_count'];
+                try {
+                    $this->sendRecordToEmail($recordId, $referer, $dataTo);
+                } catch (\Exception $e) {
+                    if ($data[1][0]['process_count'] > 3) {
+                        Log::info('process_error_for_email_send_message::'.$e->getMessage().',CODE:'.$e->getCode());
+                        Log::info('process_error_for_email_send::' . json_encode($data[1][0], JSON_UNESCAPED_UNICODE));
+                    } else {
+                        $this->addToEmailSendRedis($recordId, $referer, $dataTo, ++$processCount);
+                    }
+                }
+            }
+            //每次运行最多处理8000条数据
+            if ($i < $length && $i < 8000) {
+                $data = $redis->blpop($cacheKey, 1);
+            } else {
+                break;
+            }
+            usleep(50);
+            $i++;
+        }
+        Cache::forget($processingKey);
+        return $i;
+    }
+}

+ 58 - 0
app/Form/routes.php

@@ -0,0 +1,58 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywlministrator
+ * Date: 2023/3/10
+ * Time: 11:05
+ */
+$app = app();
+$languagePath = '';
+if(config('app.language_path')) {
+    $languagePath = config('app.language_path').'/';
+}
+$app->router->group(['namespace' => 'App\Form\Controllers',
+    'prefix' => $languagePath.'api'], function ($router) {
+
+    //编辑器保存表单结构
+    $router->post('form/agent-record', 'FormController@addFormRecord');
+
+    //表单项详情排序
+    $router->post('form/item_detail_sort', 'FormController@sortItemDetail');
+
+    $router->get('form/record/list/export', 'FormRecordController@exportList');
+
+    $router->group(['middleware' =>['admin.auth']], function ($router) {
+        //保存表单结构
+        $router->post('form/save-info', 'FormController@addForm');
+        //保持单个表单项
+        $router->post('form/save-item', 'FormController@addItem');
+        //删除表单项
+        $router->post('form/del-item', 'FormController@delItem');
+        //获取表单结构
+        $router->get('form/get-info','FormController@formById');
+        //表单项排序
+        $router->post('form/item_sort', 'FormController@sortItem');
+        //获取表单列表
+        $router->get('form/list', 'FormController@getList');
+        //删除或者禁用表单
+        $router->post('form/stop_and_del', 'FormController@stopAndDelFormById');
+        //导出
+        $router->get('form/record/export', 'FormRecordController@exportList');
+        //记录列表
+        $router->get('form/record/list', 'FormRecordController@getList');
+        //设置为已读
+        $router->post('form/record/record-set-is-read', 'FormRecordController@recordSetIsRead');
+        //记录列表导出
+        //记录详情
+        $router->get('form/record', 'FormRecordController@getRecord');
+        //记录删除
+        $router->delete('form/record', 'FormRecordController@delRecord');
+        $router->post('form/record/status', 'FormRecordController@changeRecordStatus');
+        //获取对应公司下询盘列表
+        $router->get('form/correspond/company/list', 'FormController@correspondCompanyFormList');
+        //获取对应公司下询盘列表
+        $router->get('form/record/company/not-read-count', 'FormController@getCompanyFormRecordNotReadCount');
+        //导出
+        $router->post('form/record/center/export', 'FormRecordController@centerExportList');
+    });
+});

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

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Laravel\Lumen\Routing\Controller;
+
+class BaseController extends Controller
+{
+    //
+    /**
+     * 成功返回Json数据
+     * @param string $message 提示信息
+     * @param array $data 数据
+     * @param int $code 成功返回时的代码
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function jsonResponse($message = '', $data = [], $code = 0)
+    {
+        if(empty($message)&&!empty($code)){
+            $message=config('error')[(int) $code];
+        }
+        return response()->json([
+            'code'    => $code,
+            'message' => $message,
+            'data'    => $data,
+        ]);
+    }
+}

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

@@ -0,0 +1,10 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Laravel\Lumen\Routing\Controller as BaseController;
+
+class Controller extends BaseController
+{
+    //
+}

+ 18 - 0
app/Http/Controllers/ExampleController.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Http\Controllers;
+
+class ExampleController extends Controller
+{
+    /**
+     * Create a new controller instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+
+    //
+}

+ 53 - 0
app/Http/Middleware/AdminUserAuthenticate.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use App\User\Facades\SysAdminUserFacade;
+use Closure;
+use Illuminate\Contracts\Auth\Factory as Auth;
+use App\Exceptions\ApiException;
+
+class AdminUserAuthenticate
+{
+    /**
+     * 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)
+    {
+        if(empty($request->user())){
+            throw new ApiException(401);
+        }
+        $user = $request->user();
+        $userId = $user['id'];
+        $token = $request->header('token');
+        //如果不是最近登录的token
+        if (config('app.login_singleton') && !SysAdminUserFacade::isLastToken($userId, $token)) {
+            SysAdminUserFacade::logout($token);
+            throw new ApiException(10008);
+        }
+        return $next($request);
+    }
+}

+ 39 - 0
app/Http/Middleware/AuthCheck.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2017/4/21
+ * Time: 下午10:38
+ */
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Support\Facades\DB;
+use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Facades\JWTAuth;
+use Tymon\JWTAuth\JWT;
+use Tymon\JWTAuth\Payload;
+
+class AuthCheck
+{
+    public function handle($request, Closure $next)
+    {
+        $auth = JWTAuth::parseToken();
+        $payload = $auth->getPayload();
+        $tokenExpired = $payload->get('exp');
+        //  本应用 auth token 失效都要刷新 token
+        if ($tokenExpired < time() - 3600) {
+            try {
+                $token = $auth->refresh();
+            } catch (JWTException $e) {
+                throw new UnauthorizedHttpException('jwt-auth', $e->getMessage(), $e, $e->getCode());
+            }
+            $response = $next($request);
+            $response->headers->set('Authorization', 'Bearer ' . $token);
+            return $response;
+        }
+        return $next($request);
+    }
+}

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

@@ -0,0 +1,48 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Contracts\Auth\Factory as Auth;
+use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Facades\JWTAuth;
+use Tymon\JWTAuth\JWT;
+use Tymon\JWTAuth\Payload;
+
+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
+     * @return void
+     */
+    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
+     */
+    public function handle($request, Closure $next, $guard = null)
+    {
+        if ($this->auth->guard($guard)->guest()) {
+            return response('Unauthorized.', 401);
+        }
+        return $next($request);
+    }
+}

+ 20 - 0
app/Http/Middleware/ExampleMiddleware.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+
+class ExampleMiddleware
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        return $next($request);
+    }
+}

+ 44 - 0
app/Http/Middleware/NormalUserAuthenticate.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Contracts\Auth\Factory as Auth;
+use App\Exceptions\ApiException;
+
+class NormalUserAuthenticate
+{
+    /**
+     * 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)
+    {
+        if(empty($request->user())){
+            throw new ApiException(401);
+        }
+        return $next($request);
+    }
+}

+ 103 - 0
app/Http/Middleware/Response.php

@@ -0,0 +1,103 @@
+<?php
+
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+/**
+ * 数据返回中间件
+ * Class Response
+ * @package SocialBird\Middleware\Middleware
+ */
+class Response
+{
+    /**
+     * @param Request $request
+     * @param Closure $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        //去除请求参数左右两边空格
+        $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();
+        }
+        $response = $next($request);
+        if ($response->getStatusCode()==200 && empty($response->exception)) {
+
+            if($this->mTrans($request)) {
+                DB::commit();
+            }
+            if($request->ajax() && !($response instanceof StreamedResponse)) {
+//                $content = $this->isJson($content) ? json_decode($content, true) : $content;
+
+               $content = json_encode([
+                   'code' => 0,
+                   'message' => 'success.',
+                   'data' => $response->getOriginalContent()
+               ]);
+
+                // $content = json_encode($response->getOriginalContent());
+                // $response->setContent($content);
+            }
+
+        }else{
+            if($this->mTrans($request)) {
+                DB::rollBack();
+            }
+            if(!$request->ajax() && !($response instanceof StreamedResponse)){
+                $content = json_decode($response->getContent(),true);
+                $response->setContent($content['message']??'');
+            }
+        }
+        if(method_exists($response,'withHeaders')){
+            $headersArr=$response->headers->all();
+            $withHeadersArr=[
+                '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,X-Requested-With,Authorization',
+                'Access-Control-Expose-Headers' => '*'
+            ];
+            if(empty($headersArr['content-type'])){
+                $withHeadersArr['Content-Type']=$request->ajax()?'application/json;charset:UTF-8':'text/html; charset=UTF-8';
+            }
+            $response->withHeaders($withHeadersArr);
+        }
+        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;
+        }
+    }
+
+}

+ 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()
+    {
+        //
+    }
+}

+ 24 - 0
app/Jobs/Job.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Jobs;
+
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+
+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;
+}

+ 31 - 0
app/Listeners/ExampleListener.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Listeners;
+
+use App\Events\ExampleEvent;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Queue\InteractsWithQueue;
+
+class ExampleListener
+{
+    /**
+     * Create the event listener.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+
+    /**
+     * Handle the event.
+     *
+     * @param  \App\Events\ExampleEvent  $event
+     * @return void
+     */
+    public function handle(ExampleEvent $event)
+    {
+        //
+    }
+}

+ 97 - 0
app/Models/ApiSoftDeletes.php

@@ -0,0 +1,97 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: ywl
+ * Date: 2017/4/14
+ * Time: 12:46
+ */
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+trait ApiSoftDeletes
+{
+    use SoftDeletes;
+
+
+    public static function bootSoftDeletes()
+    {
+        static::addGlobalScope(new ApiSoftDeletingScope);
+    }
+
+    public function initializeSoftDeletes(){
+        return $this->dates;
+    }
+    /**
+     * 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();
+    }
+}

+ 153 - 0
app/Models/ApiSoftDeletingScope.php

@@ -0,0 +1,153 @@
+<?php
+/**
+ * 软删除 socpe
+ * User: ywl
+ * Date: 2017/4/14
+ * Time: 16:31
+ */
+
+namespace App\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);
+        });
+    }
+}

+ 275 - 0
app/Models/BaseModel.php

@@ -0,0 +1,275 @@
+<?php
+
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Schema;
+use DateTimeInterface;
+
+class BaseModel extends Model
+{
+    use Criteria;
+    //use ApiSoftDeletes; laravels 下常驻内存 as Name 会出问题
+    /**
+     * 插入时间字段
+     */
+    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;
+
+    /**
+     * 该模型是否被自动维护时间戳
+     *
+     * @var bool
+     */
+    public $timestamps = true;
+    /**
+     * 别名名称
+     * @var null
+     */
+    protected $aliasName = null;
+
+    /**
+     * 模型的日期字段保存格式。
+     *
+     * @var string
+     */
+    protected $dateFormat = 'Y-m-d H:i:s';
+
+   /* public function __construct(array $attributes = [])
+    {
+        parent::__construct($attributes);
+        //自动添加字段
+        $this->setFillable();
+    }*/
+
+    /**
+     * 自动获取数据库表字段
+     */
+    protected function setFillable()
+    {
+        $key = get_class($this);
+        $cache = Cache::store('file')->get($key);
+        if (empty($cache)) {
+            $columns = Schema::getColumnListing($this->table);
+            $cache = $columns;
+            Cache::store('file')->put($key, $columns, config('cache.columns'));
+        }
+        $this->fillable = $cache;
+    }
+
+    /**
+     * 过滤掉非数据库字段的数据
+     * @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;
+    }
+
+    /**
+     * 别名
+     * @param $name
+     */
+    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;
+    }
+    /*  @param DateTimeInterface $date
+     *
+     * @return string
+     */
+    protected function serializeDate(DateTimeInterface $date)
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+
+
+    /**
+     * 获取分页行数
+     * @param $data
+     * @return int
+     */
+    protected function getPageSize($data)
+    {
+        return $data['page_size'] ?? config('app.app_rows');
+    }
+
+    /**
+     * 获取页码
+     * @param $data
+     * @return int
+     */
+    protected function getPageNo($data)
+    {
+        return $data['page'] ?? config('app.app_page_no');
+    }
+
+    /**
+     * 获取查询偏移量
+     * @param $data
+     * @return int
+     */
+    protected function getSkip($page,$pageSize)
+    {
+        return ($page - 1) * $pageSize;;
+    }
+
+    /**
+     * 获取分页参数
+     * */
+    protected function getPaginatorParams($params){
+
+        $pageSize = $this->getPageSize($params);
+        $page =  $this->getPageNo($params);
+        $skip =  $this->getSkip($page,$pageSize);
+        return array($pageSize, $page,$skip);
+    }
+
+    /**
+     * 构造返回页面数据
+     * @param $data //数据
+     * @param $formNb //开始数量
+     * @param $page //页数
+     * @param $perPageSize //每页数量
+     * @param $total //总数量
+     * @return mixed
+     */
+    public function buildPaginator($data = [], $formNb = 0, $page = 1, $perPageSize = 10, $total = 0)
+    {
+        $lastPage = ceil($total / $perPageSize);
+        $toNb = $page * $perPageSize;
+        if (empty($formNb)) {
+            $formNb = $toNb - $perPageSize;
+        }
+        if ($toNb > $total) {
+            $toNb = $total;
+        }
+        $resultDdata = array(
+            'current_page' => $page,
+            'data' => $data,
+            'from' => $formNb,
+            'last_page' => $lastPage,
+            'per_page' => $perPageSize,
+            'to' => $toNb,
+            'total' => $total,
+        );
+        return $resultDdata;
+    }
+
+    /**
+     * 字段唯一性性验证
+     * 返回ture 代表数据库不存在,是唯一的 false 代表数据库存在,不是唯一的
+     * @param $field
+     * @param $value
+     * @param $criteria
+     * @return bool
+     */
+    public function checkFieldUnique($field, $criteria)
+    {
+        $existData = $this->newInstance()->buildQuery($criteria)->selectRaw(DB::raw($field))->exists();
+        if ($existData) {
+            return false;
+        }else{
+            return true;
+        }
+    }
+
+}

+ 292 - 0
app/Models/BaseMongoModel.php

@@ -0,0 +1,292 @@
+<?php
+
+
+namespace App\Models;
+
+
+use Illuminate\Support\Facades\DB;
+use Jenssegers\Mongodb\Eloquent\Model;
+
+class BaseMongoModel extends Model
+{
+    protected $connection = 'mongodb';
+    protected $db = null;
+
+    public function __construct()
+    {
+        parent::__construct();
+        $this->db =   DB::connection('mongodb');
+    }
+
+    /**
+     * @function 公用查询model
+     * @param array $param
+     * @param array $fields 返回字段名数组['_id', 'res_count'],一维数组
+     * @param array $sort
+     * @param string $pageNo
+     * @param string $perPage
+     * @param array $group
+     * @param array $lookup
+     * @param array $unwind
+     * @param array $match
+     * @return array totalCount表示返回如果有分页总条数,data返回查询后的数据
+     */
+    public function getListByCondition(
+        $param = [],
+        $fields = [],
+        $sort = [],
+        $pageNo = '',
+        $perPage = '',
+        $group = [],
+        $lookup = [],
+        $unwind = [],
+        $match = []
+    ) {
+        $result = [];
+        $condition = [];
+        //过滤条件,过滤字段
+        if ($param) {
+            $condition [] = $param;
+        }
+
+        //连表查询
+        if ($lookup && is_array($lookup)) {
+            $condition[] = ['$lookup' => $lookup];
+        }
+
+        if ($unwind && is_array($unwind)) {
+            $condition[] = ['$unwind' => $unwind];
+        }
+
+        if ($match && is_array($match)) {
+            $condition[] = ['$match' => $match];
+        }
+
+        //是否有分组过滤
+        if ($group && is_array($group)) {
+            $condition[] = ['$group' => $group];
+        }
+
+        //是否有排序
+        if ($sort && is_array($sort)) {
+            $condition[] = ['$sort' => $sort];
+        }
+
+        //是否有分页
+        if ($pageNo) {
+            //有分页需要获取总条数
+            $totalCount = $this->statCountByCondition($condition);
+            $result['totalCount'] = $totalCount;
+            $perPage = $this->getPerPage($perPage);
+            $pageNo = $this->getPage($pageNo);
+            $condition[] = ['$skip' => (int)(($pageNo - 1) * $perPage)];
+            $condition[] = ['$limit' => $perPage];
+        }
+
+        //字段过滤
+        if ($fields && is_array($fields)) {
+            $project = [];
+            foreach ($fields as $value) {
+                $project[$value] = 1;
+            }
+            $condition[] = ['$project' => $project];
+        }
+
+        $respData = $this->db->selectCollection($this->collection)->aggregate($condition)->toArray();
+        $respData = $respData ? $respData : [];
+        $result['data'] = $respData;
+        return $result;
+    }
+
+    /**
+     * @function 根据条件来获取相应的数据
+     * @param array $param
+     * @param array $fields
+     * @param bool $is_one
+     * @param string $sortField
+     * @param string $sort
+     * @return bool|mixed
+     */
+    public function getInfoByCondition($param = [], $fields = [], $is_one = false, $sortField = '', $sort = 'desc')
+    {
+        if (empty($param)) {
+            return false;
+        }
+        if ($is_one) {
+            $respData = $this->whereRaw($param)->first($fields);
+        } else {
+            if ($sortField) {
+                $respData = $this->whereRaw($param)->orderBy($sortField, $sort)->get($fields);
+            } else {
+                $respData = $this->whereRaw($param)->get($fields);
+            }
+        }
+        return $respData ? json_decode($respData, true) : [];
+    }
+
+    /**
+     * @function 根据条件统计条数
+     * @param array $param
+     * @param array $field
+     * @return int
+     */
+    public function statCountByCondition($param = [], $field = ['$count' => 'id'])
+    {
+        $condition = $param;
+        $condition[] = $field;
+        $result = $this->db->selectCollection($this->collection)->aggregate($condition)->toArray();
+        $resCount = 0;
+
+        if ($result) {
+            foreach ($result as $value) {
+                foreach ($value as $item) {
+                    $resCount = $item;
+                }
+            }
+        }
+
+        return $resCount;
+    }
+
+    /**
+     * @function 更新数据updateData
+     * @param array $condition
+     * @param array $updateData
+     * @param bool $isOne
+     * @return bool
+     */
+    public function updateData($condition = [], $updateData = [], $isOne = true)
+    {
+        if (!$updateData || !is_array($updateData)) {
+            return false;
+        }
+        if ($isOne) {
+            return $this->db->selectCollection($this->collection)->updateOne($condition, $updateData);
+        } else {
+            return $this->db->selectCollection($this->collection)->updateMany($condition, $updateData);
+        }
+    }
+
+
+    /**
+     * @function 插入数据
+     * @param array $insertData 要插入的数据
+     * @param bool $isOne 是否插入单条数据
+     * @param bool $getId 是否返回插入ID(单条有效)
+     * @return bool 返回数据
+     */
+    public function insertData($insertData = [], $isOne = true, $getId = false)
+    {
+        if (!$insertData || !is_array($insertData)) {
+            return false;
+        }
+        //插入一条数据
+        if ($isOne) {
+            $result = $this->db->selectCollection($this->collection)->insertOne($insertData);
+            if ($getId) {
+                $result = $result->getInsertedId();
+            }
+        } else {
+            $result = $this->db->selectCollection($this->collection)->insertMany($insertData);
+        }
+        return $result;
+    }
+
+    /**
+     * 获取分页行数
+     * @param $data
+     * @return int
+     */
+    protected function getPageSize($data)
+    {
+        return $data['page_size'] ?? config('app.app_rows');
+    }
+
+    /**
+     * 获取页码
+     * @param $data
+     * @return int
+     */
+    protected function getPageNo($data)
+    {
+        return $data['page'] ?? config('app.app_page_no');
+    }
+
+    /**
+     * 获取查询偏移量
+     * @param $data
+     * @return int
+     */
+    protected function getSkip($page,$pageSize)
+    {
+        return ($page - 1) * $pageSize;;
+    }
+
+    /**
+     * 获取分页参数
+     * */
+    protected function getPaginatorParams($params){
+
+        $pageSize = $this->getPageSize($params);
+        $page =  $this->getPageNo($params);
+        $skip =  $this->getSkip($page,$pageSize);
+        return array($pageSize, $page,$skip);
+    }
+
+    /**
+     * @function 获取页数
+     * @author lin
+     * @param $pageNo
+     * @return int
+     */
+    public function getPage($pageNo)
+    {
+        $pageNo = intval($pageNo);
+        return $pageNo ? $pageNo : 1;
+    }
+
+    /**
+     * @function 获取分页
+     *
+     * @author lin
+     * @param string $perPage
+     * @return int|mixed
+     */
+    public function getPerPage($perPage = '')
+    {
+        $perPage = intval($perPage);
+        return $perPage ? $perPage : config('app.app_rows');
+    }
+
+    /**
+     * 构造返回页面数据
+     * @param $data //数据
+     * @param $formNb //开始数量
+     * @param $page //页数
+     * @param $perPageSize //每页数量
+     * @param $total //总数量
+     * @return mixed
+     */
+    public function buildPaginator($data = [], $formNb = 0, $page = 1, $perPageSize = 10, $total = 0)
+    {
+        $lastPage = ceil($total / $perPageSize);
+        $toNb = $page * $perPageSize;
+        if (empty($formNb)) {
+            $formNb = $toNb - $perPageSize;
+        }
+        if ($toNb > $total) {
+            $toNb = $total;
+        }
+        $resultDdata = array(
+            'current_page' => $page,
+            'data' => $data,
+            'from' => $formNb,
+            'last_page' => $lastPage,
+            'per_page' => $perPageSize,
+            'to' => $toNb,
+            'total' => $total,
+        );
+        return $resultDdata;
+    }
+
+}

+ 216 - 0
app/Models/Criteria.php

@@ -0,0 +1,216 @@
+<?php
+
+
+namespace App\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)
+    {
+        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 '>':
+                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);
+        }
+        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);
+        }
+        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_string($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()
+    {
+
+    }
+}

+ 50 - 0
app/Models/User.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Auth\GenericUser;
+use Tymon\JWTAuth\Contracts\JWTSubject;
+
+/**
+ * 授权用户
+ */
+class User extends GenericUser implements JWTSubject,\ArrayAccess
+{
+    public function getJWTIdentifier()
+    {
+        $name = $this->getAuthIdentifierName();
+
+        return $this->attributes[$name];
+    }
+
+    /**
+     * token的附加属性
+     * @return array
+     */
+    public function getJWTCustomClaims()
+    {
+        return [
+            'api_token'=>$this->attributes['api_token']
+        ];
+    }
+
+    public function offsetExists($offset)
+    {
+        return isset($this->attributes[$offset]);
+    }
+
+    public function offsetGet($offset)
+    {
+        return $this->attributes[$offset];
+    }
+
+    public function offsetSet($offset, $value)
+    {
+        return $this->attributes[$offset] = $value;
+    }
+
+    public function offsetUnset($offset)
+    {
+        unset($this->attributes[$offset]);
+    }
+}

+ 18 - 0
app/Providers/AppServiceProvider.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Providers;
+
+use Illuminate\Support\ServiceProvider;
+
+class AppServiceProvider extends ServiceProvider
+{
+    /**
+     * Register any application services.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        //
+    }
+}

+ 113 - 0
app/Providers/AppUserProvider.php

@@ -0,0 +1,113 @@
+<?php
+
+namespace App\Providers;
+
+use App\Models\User;
+use Illuminate\Auth\DatabaseUserProvider;
+use Illuminate\Contracts\Auth\Authenticatable as UserContract;
+use Illuminate\Support\Facades\Auth;
+
+class AppUserProvider extends DatabaseUserProvider
+{
+    /**
+     * Retrieve a user by their unique identifier.
+     *
+     * @param  mixed $identifier
+     * @return \Illuminate\Contracts\Auth\Authenticatable|null
+     */
+    public function retrieveById($identifier)
+    {
+        $user = $this->conn->table($this->table)->where([
+            'sys_user_id' => $identifier,
+        ])->where('status', '<>', 2)->first();
+        $user->api_token = Auth::getPayload()->get('api_token');
+        return $this->getGenericUser($user);
+    }
+
+    /**
+     * Retrieve a user by their unique identifier and "remember me" token.
+     *
+     * @param  mixed $identifier
+     * @param  string $token
+     * @return \Illuminate\Contracts\Auth\Authenticatable|null
+     */
+    public function retrieveByToken($identifier, $token)
+    {
+        return null;
+    }
+
+    /**
+     * Retrieve a user by the given credentials.
+     *
+     * @param  array $credentials
+     * @return \Illuminate\Contracts\Auth\Authenticatable|null
+     */
+    public function retrieveByCredentials(array $credentials)
+    {
+        if ($credentials['sys_user_id']) {
+            // 用户中心已验证好、直接授权
+            $user = [
+                'id'=>$credentials['sys_user_id'],
+                'sys_user_id'=>$credentials['sys_user_id'],
+                'user_name'=>$credentials['user_name'],
+                'nick_name'=>$credentials['nick_name'],
+                'api_token'=>$credentials['api_token'],
+                'password'=>'',
+                'phone'=>$credentials['phone'],
+                'email'=>$credentials['email'],
+                'create_time'=>$credentials['create_time'],
+                'update_time'=>$credentials['update_time']
+            ];
+        } else {
+            // 查数据库授权
+            $query = $this->conn->table($this->table);
+            $user = $query->where('username', $credentials['username'])
+                ->where('status', '<>', 2)->first();
+        }
+        return $this->getGenericUser($user);
+    }
+
+    /**
+     * Get the generic user.
+     *
+     * @param  mixed $user
+     * @return \Illuminate\Auth\GenericUser|null
+     */
+    protected function getGenericUser($user)
+    {
+        if (!is_null($user)) {
+            return new User((array)$user);
+        }
+    }
+
+    protected $model = User::class;
+
+    /**
+     * Gets the name of the Eloquent user model.
+     *
+     * @return string
+     */
+    public function getModel()
+    {
+        return $this->model;
+    }
+
+    /**
+     * Sets the name of the Eloquent user model.
+     *
+     * @param  string $model
+     * @return $this
+     */
+    public function setModel($model)
+    {
+        $this->model = $model;
+
+        return $this;
+    }
+
+    public function validateCredentials(UserContract $user, array $credentials)
+    {
+        return true;
+    }
+
+}

+ 64 - 0
app/Providers/AuthServiceProvider.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace App\Providers;
+
+use App\Common\Facades\UserCenterFacade;
+use App\Exceptions\ApiException;
+use App\User;
+use App\User\Facades\SysAdminUserFacade;
+use App\User\Facades\UserFacade;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\ServiceProvider;
+
+class AuthServiceProvider extends ServiceProvider
+{
+    /**
+     * Register any application services.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        //配合 auth providers driver
+       /* $this->app['auth']->provider('custom_database', function() {
+            return new AppUserProvider(DB::connection(), $this->app['hash'], 'user');
+        });*/
+    }
+
+    /**
+     * Boot the authentication services for the application.
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        $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');
+            $token_update_time= config('cache.token_update_time');
+            if(empty($token)){
+                $token = $request->input('token');
+            }
+             if ($token) {
+                $data = SysAdminUserFacade::findOneByToken($token);
+                if (!empty($data)) {
+                    //超过半小时 发送heartbeat
+                    $time=time();
+                    if ($data['expiration_time'] <($time + $token_update_time)) {
+                        $data =   SysAdminUserFacade::updateUserInfoCache($token, $data);
+                    }
+                    return $data;
+                }
+            }
+            return null;
+        });
+    }
+}

+ 0 - 0
app/Providers/EventServiceProvider.php


Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff