kenan пре 1 година
родитељ
комит
0ab860990f
100 измењених фајлова са 11560 додато и 2 уклоњено
  1. BIN
      .DS_Store
  2. 331 0
      Modules/AgentApi/Controllers/AgentController.php
  3. 81 0
      Modules/AgentApi/Controllers/AppController.php
  4. 291 0
      Modules/AgentApi/Controllers/AuthController.php
  5. 141 0
      Modules/AgentApi/Controllers/BaseController.php
  6. 65 0
      Modules/AgentApi/Controllers/ShareUrlController.php
  7. 336 0
      Modules/AgentApi/Controllers/TotalController.php
  8. 413 0
      Modules/AgentApi/Controllers/WithdrawController.php
  9. 110 0
      Modules/AgentApi/Middlewares/AgentAuthMiddleware.php
  10. 57 0
      Modules/AgentApi/Requests/BaseRequest.php
  11. 51 0
      Modules/AgentApi/Responses/AgentDetailResponse.php
  12. 65 0
      Modules/AgentApi/Responses/AgentPagesResponse.php
  13. 37 0
      Modules/AgentApi/Responses/CommonSuccessUidResponse.php
  14. 29 0
      Modules/AgentApi/Responses/ConfigSuccessResponse.php
  15. 47 0
      Modules/AgentApi/Responses/NotIncrementIdDetailResponse.php
  16. 30 0
      Modules/AgentApi/Responses/ShareUrlsResponse.php
  17. 45 0
      Modules/AgentApi/Responses/SuccessIdResponse.php
  18. 27 0
      Modules/AgentApi/Responses/TotalPagesResponse.php
  19. 46 0
      Modules/AgentApi/Responses/WithdrawBankInfoResponse.php
  20. 52 0
      Modules/AgentApi/Responses/WithdrawLogResponse.php
  21. 112 0
      Modules/AgentApi/Services/AgentAuthService.php
  22. 17 0
      Modules/Api/Controllers/BaseController.php
  23. 57 0
      Modules/Api/Requests/BaseRequest.php
  24. 45 0
      Modules/Api/Responses/SuccessIdResponse.php
  25. 12 0
      Modules/Api/Services/DemoService.php
  26. 26 0
      Modules/InnerApi/Controllers/BaseController.php
  27. 44 0
      Modules/InnerApi/Controllers/ChannelsController.php
  28. 51 0
      Modules/InnerApi/Controllers/PayController.php
  29. 98 0
      Modules/InnerApi/Controllers/UsersController.php
  30. 68 0
      Modules/InnerApi/Middlewares/InnerApiAuthMiddleware.php
  31. 57 0
      Modules/InnerApi/Requests/BaseRequest.php
  32. 19 0
      Modules/InnerApi/Requests/ChannelPackageNotifyRequest.php
  33. 15 0
      Modules/InnerApi/Requests/CommonAppDeploy.php
  34. 17 0
      Modules/InnerApi/Requests/CommonGetAdListRequest.php
  35. 35 0
      Modules/InnerApi/Requests/PayIncRequest.php
  36. 18 0
      Modules/InnerApi/Requests/PayUpStatusRequest.php
  37. 30 0
      Modules/InnerApi/Requests/UserIncRequest.php
  38. 25 0
      Modules/InnerApi/Requests/UserLoginRequest.php
  39. 18 0
      Modules/InnerApi/Requests/UserUpRequest.php
  40. 31 0
      Modules/InnerApi/Responses/SuccessAdsResponse.php
  41. 37 0
      Modules/InnerApi/Responses/SuccessCommonResponse.php
  42. 45 0
      Modules/InnerApi/Responses/SuccessIdResponse.php
  43. 179 0
      Modules/InnerApi/Services/ChannelPackageService.php
  44. 47 0
      Modules/InnerApi/Services/CommonService.php
  45. 44 0
      Modules/InnerApi/Services/DeductionService.php
  46. 12 0
      Modules/InnerApi/Services/DemoService.php
  47. 223 0
      Modules/InnerApi/Services/PayService.php
  48. 191 0
      Modules/InnerApi/Services/UserService.php
  49. 70 0
      Modules/ManageApi/Commands/PermissionResourceCommand.php
  50. 537 0
      Modules/ManageApi/Controllers/AgentController.php
  51. 316 0
      Modules/ManageApi/Controllers/AnnounceController.php
  52. 552 0
      Modules/ManageApi/Controllers/AppController.php
  53. 289 0
      Modules/ManageApi/Controllers/AppDeductionRuleController.php
  54. 280 0
      Modules/ManageApi/Controllers/AuthController.php
  55. 142 0
      Modules/ManageApi/Controllers/BaseController.php
  56. 348 0
      Modules/ManageApi/Controllers/FinanceController.php
  57. 24 0
      Modules/ManageApi/Controllers/IndexController.php
  58. 486 0
      Modules/ManageApi/Controllers/ManagerController.php
  59. 435 0
      Modules/ManageApi/Controllers/PermissionController.php
  60. 230 0
      Modules/ManageApi/Controllers/PluploadController.php
  61. 188 0
      Modules/ManageApi/Controllers/SystemController.php
  62. 47 0
      Modules/ManageApi/Controllers/TotalController.php
  63. 416 0
      Modules/ManageApi/Controllers/UeditorController.php
  64. 1273 0
      Modules/ManageApi/Controllers/VideoController.php
  65. 291 0
      Modules/ManageApi/Controllers/shareUrlsController.php
  66. 123 0
      Modules/ManageApi/Middlewares/ManageAuthMiddleware.php
  67. 57 0
      Modules/ManageApi/Requests/BaseRequest.php
  68. 49 0
      Modules/ManageApi/Responses/AgentPagesResponse.php
  69. 43 0
      Modules/ManageApi/Responses/AnnouncePagesResponse.php
  70. 42 0
      Modules/ManageApi/Responses/AppDeductionRuleResponse.php
  71. 37 0
      Modules/ManageApi/Responses/AppPagesResponse.php
  72. 37 0
      Modules/ManageApi/Responses/CommonAllResponse.php
  73. 53 0
      Modules/ManageApi/Responses/CommonDetailResponse.php
  74. 42 0
      Modules/ManageApi/Responses/CommonPagesResponse.php
  75. 45 0
      Modules/ManageApi/Responses/CommonSuccessPkResponse.php
  76. 45 0
      Modules/ManageApi/Responses/CommonSuccessUidResponse.php
  77. 29 0
      Modules/ManageApi/Responses/ConfigSuccessResponse.php
  78. 46 0
      Modules/ManageApi/Responses/FinanceRechargePageResponse.php
  79. 46 0
      Modules/ManageApi/Responses/FinanceWithdrawResponse.php
  80. 51 0
      Modules/ManageApi/Responses/ManagerDetailResponse.php
  81. 48 0
      Modules/ManageApi/Responses/ManagerPagesResponse.php
  82. 48 0
      Modules/ManageApi/Responses/NotIncrementIdDetailResponse.php
  83. 48 0
      Modules/ManageApi/Responses/NotIncrementIdPagesResponse.php
  84. 51 0
      Modules/ManageApi/Responses/PermissionRoleDetailResponse.php
  85. 52 0
      Modules/ManageApi/Responses/PermissionRolePagesResponse.php
  86. 45 0
      Modules/ManageApi/Responses/SuccessIdResponse.php
  87. 48 0
      Modules/ManageApi/Responses/SystemLogPagesResponse.php
  88. 47 0
      Modules/ManageApi/Responses/VideoCategoryDetailResponse.php
  89. 48 0
      Modules/ManageApi/Responses/VideoCategoryPagesResponse.php
  90. 57 0
      Modules/ManageApi/Responses/VideoDetailResponse.php
  91. 58 0
      Modules/ManageApi/Responses/VideoPagesResponse.php
  92. 37 0
      Modules/ManageApi/Responses/shareUrlsPagesResponse.php
  93. 12 0
      Modules/ManageApi/Services/DemoService.php
  94. 112 0
      Modules/ManageApi/Services/ManageAuthService.php
  95. 78 0
      Modules/ManageApi/Services/VideoTagService.php
  96. 17 0
      Modules/Web/Controllers/BaseController.php
  97. 57 0
      Modules/Web/Requests/BaseRequest.php
  98. 39 0
      Modules/Web/Responses/DemoResponse.php
  99. 12 0
      Modules/Web/Services/DemoService.php
  100. 150 2
      README.md

+ 331 - 0
Modules/AgentApi/Controllers/AgentController.php

@@ -0,0 +1,331 @@
+<?php
+
+namespace Modules\AgentApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Models\Mysql\AgentModel;
+use App\Repositories\AgentRepository;
+use App\Repositories\ChannelRepository;
+use App\Repositories\ConfigRepoistory;
+use App\Repositories\Criterias\AgentPagesCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Repositories\ManagerRepository;
+use Illuminate\Support\Facades\Validator;
+use Modules\AgentApi\Responses\AgentDetailResponse;
+use Modules\AgentApi\Responses\AgentPagesResponse;
+use Modules\AgentApi\Responses\CommonSuccessUidResponse;
+
+class AgentController extends BaseController
+{
+    /**
+     * 代理列表
+     *
+     * @return \Illuminate\Http\JsonResponse|AgentPagesResponse
+     */
+    public function lists()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'parent_uid' => 'nullable|string',
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'status' => 'nullable|integer'
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $accountInfo = $this->getCurrentAgent();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $orders = [
+            ['id', 'DESC']
+        ];
+
+        if (!isset($inputData['page_size']) || $inputData['page_size'] > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        } else {
+            $pageSize = $inputData['page_size'];
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        //默认上级代理
+        if (empty($inputData['parent_uid'])) {
+            $inputData['parent_uid'] = $accountInfo['uid'];
+        } else {
+            // 查询的判断搜否是自己代理下的
+            $agentModel = new AgentModel();
+            $agentUids = $agentModel->findAllChildUids($accountInfo['uid']);
+            $agentUids = array_merge($agentUids, [$accountInfo['uid']]);
+            if (!in_array($inputData['parent_uid'], $agentUids)) {
+                return json_fail('无数据');
+            }
+        }
+        /**
+         * @var AgentRepository $agentRepo
+         */
+        $agentRepo = app(AgentRepository::class);
+
+        $field = [
+            'uid',
+            'nick',
+            'username',
+            'parent_uid',
+            'commission_ratio',
+            'status',
+            'created_at',
+            'agent_level',
+            'deduction_switch',
+            'own_app_uids'
+        ];
+        $res = $agentRepo->pushCriteria(new AgentPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize,
+            $field, $page);
+        return new AgentPagesResponse($res);
+    }
+
+    /**
+     * 创建渠道
+     *
+     * @param AgentRepository $objRepo
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \App\Exceptions\ApiException
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function create(AgentRepository $objRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'username' => 'unique:agent,username|required|max:16',
+            'password' => 'required|min:6|max:32',
+            'two_password' => 'required|string',
+            'commission_ratio' => 'required|integer',
+            'own_app_uids' => 'required|string',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        if ($inputData['password'] != $inputData['two_password']) {
+            return json_fail('确认密码错误,请重新输入!');
+        }
+
+        $accountInfo = $this->getCurrentAgent();
+
+        /**
+         * @var ConfigRepoistory $confRepo
+         */
+        $confRepo = app(ConfigRepoistory::class);
+
+        if (empty($accountInfo['parent_uids'])) {
+            $parentUids = $accountInfo['uid'];
+        } else {
+            $parentUids = $accountInfo['parent_uids'] . ',' . $accountInfo['uid'];
+        }
+
+        $setData = [
+            'username' => $inputData['username'],
+            'nick' => $inputData['username'],
+            'password' => $inputData['password'],
+            'status' => 1,
+            'parent_uid' => $accountInfo['uid'],
+            'parent_uids' => $parentUids,
+            'withdraw_pwd' => $confRepo->get('withdraw_pwd', 'sys'),
+            'own_app_uids' => $inputData['own_app_uids'],
+            'commission_ratio' => $inputData['commission_ratio'],
+            'deduction_switch' => 0,
+            'jdata' => json_encode([])
+        ];
+
+        $agentObj = $objRepo->createOne($setData, $accountInfo['uid']);
+        $respData = [
+            'uid' => $agentObj->uid,
+        ];
+
+        if (empty($accountInfo['child_uids'])) {
+            $childUids = $agentObj->uid;
+        } else {
+            $childUids = $accountInfo['child_uids'] . ',' . $agentObj->uid;
+        }
+        //新增子集集合
+        $objRepo->update(['child_uids' => $childUids], $accountInfo['uid'], 'uid');
+
+        //创建渠道
+        $chanRepo = app(ChannelRepository::class);
+        $chanRepo->createAgentChannel($agentObj->uid);
+
+        return new CommonSuccessUidResponse($respData);
+    }
+
+    /**
+     * 账户信息修改
+     *
+     * @param AgentRepository $agentRepo
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \App\Exceptions\ApiException
+     */
+    public function modify(AgentRepository $agentRepo)
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'nick' => 'nullable|max:16',
+            'old_password' => 'nullable|string',
+            'password' => 'nullable|min:6|max:32',
+            'withdraw_pwd' => 'nullable|min:6|max:32',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $accountInfo = $this->getCurrentAgent();
+
+        if (!empty($inputData['password'])) {
+            /**
+             * @var ManagerRepository $managerRepo
+             */
+            $managerRepo = app(ManagerRepository::class);
+            $md5password = $managerRepo->generatePassword($inputData['old_password'], $accountInfo['salt']);
+            if ($md5password != $accountInfo['password']) {
+                return json_fail('历史密码错误,请确认后再进行修改');
+            }
+        }
+
+        $setData = [];
+        if (!empty($inputData['nick'])) {
+            $setData['nick'] = $inputData['nick'];
+        }
+
+        if (!empty($inputData['password'])) {
+            $setData['password'] = $inputData['password'];
+        }
+
+        if (!empty($inputData['withdraw_pwd'])) {
+            $setData['withdraw_pwd'] = $inputData['withdraw_pwd'];
+        }
+
+        $saveResult = $agentRepo->agentEdit($setData, $accountInfo['uid']);
+        if (!$saveResult) {
+            return json_fail('修改失败');
+        }
+
+        return new CommonSuccessUidResponse($accountInfo['uid']);
+    }
+
+    /**
+     * 匹配用户名
+     *
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function checkUsername()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'username' => 'unique:agent,username|required|max:16',
+        ];
+        $message = [
+            'username.unique' => '账号已存在!',
+            'username.required' => '用户名不能为空!',
+            'username.max' => '用户名不能大于16字节!'
+        ];
+        $validator = Validator::make($inputData, $rules, $message);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        return json_fail('用户名可用!', 200, []);
+    }
+
+    /**
+     * 我的详情.
+     * @return mixed|AgentDetailResponse
+     */
+    public function detailMe()
+    {
+        $accountInfo = $this->getCurrentAgent();
+        if (empty($accountInfo)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+
+        return new AgentDetailResponse($accountInfo);
+    }
+
+    public function editLevelAgent()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'uid' => 'required',
+            'own_app_uids' => 'required',
+            'status' => 'required',
+            'commission_ratio' => 'required'
+        ];
+        $message = [
+            'uid.required' => 'UID出错!',
+            'own_app_uids.required' => '应用不能为空!',
+            'status.required' => '状态不能为空!',
+            'commission_ratio.required' => '分成比例不能为空!'
+        ];
+        $validator = Validator::make($inputData, $rules, $message);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $accountInfo = $this->getCurrentAgent();
+        $agentModel = new AgentModel();
+        // 查询修改的用户下级最高的分成比例
+        $commission_ratio_min = $agentModel->where('parent_uid', $inputData['uid'])
+            ->orderByDesc('commission_ratio')
+            ->pluck('commission_ratio')->first();
+
+        // 修改的不能小于他下级的
+        if ($commission_ratio_min) {
+            if ($inputData['commission_ratio'] < $commission_ratio_min) {
+                return json_fail('比例不能小于他下级的!');
+            }
+        }
+
+        if ($inputData['commission_ratio'] > $accountInfo->commission_ratio) {
+            return json_fail('比例不能高出自己的!');
+        }
+
+        // 查询是否是自己
+        $agent = $agentModel->where('uid', $inputData['uid'])->first();
+
+        if ($agent->parent_uid !== $accountInfo->uid) {
+            return json_fail('不属于一级代理!');
+        }
+        if (!empty($inputData['own_app_uids'])) {
+            $agent->own_app_uids = $inputData['own_app_uids'];
+        }
+
+        if (!empty($inputData['status'])) {
+            $agent->status = $inputData['status'];
+        }
+
+        if (!empty($inputData['commission_ratio'])) {
+            $agent->commission_ratio = $inputData['commission_ratio'];
+        }
+        if (!$agent->save()) {
+            return json_fail('更新失败!');
+        }
+        return json_success('更新成功!');
+    }
+
+}

+ 81 - 0
Modules/AgentApi/Controllers/AppController.php

@@ -0,0 +1,81 @@
+<?php
+
+namespace Modules\AgentApi\Controllers;
+
+use App\Jobs\AppPackJob;
+use App\Models\Mysql\PackModel;
+use App\Repositories\AgentRepository;
+use App\Repositories\AppRepository;
+use App\Repositories\ChannelRepository;
+use Illuminate\Support\Facades\Validator;
+
+class AppController extends BaseController
+{
+    /**
+     * app列表
+     *
+     * @param AppRepository $appRepo
+     * @return mixed
+     */
+    public function lists(AppRepository $appRepo, AgentRepository $agentRepo)
+    {
+        $accountInfo = $this->getCurrentAgent();
+        //获取代理应用uid
+        $appUidData = $agentRepo->findBy('uid', $accountInfo['uid'], ['own_app_uids']);
+        $appUids = explode(',', $appUidData->own_app_uids);
+
+        $appData = $appRepo->getAgentApp($appUids);
+        return json_success($appData);
+    }
+
+    public function pack()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'uid' => 'required|string',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        // 查找渠道开始打包
+        /**
+         * 获取渠道app
+         * @var ChannelRepository $channRepo
+         */
+        $channelRepo = app(ChannelRepository::class);
+        $channelData = $channelRepo->getAppChannel($inputData['uid']);
+        $packArray =[
+            'agent_uid'=>$channelData->agent_uid,
+            'channel_code'=>$channelData->uid,
+            'mom_app_name'=>$channelData->app->name,
+            'mom_app_uid'=>$channelData->app->uid,
+            'mom_app_id'=>$channelData->app->app_id,
+            'android_or_ios'=>1,
+            'mom_app_download'=>$channelData->app->android_url,
+            'mom_app_version'=>$channelData->app->android_version,
+        ];
+
+        $packCreateData = $packArray;
+        // 数据库格式
+        unset($packCreateData['mom_app_download']);
+        $packCreateData['app_download'] = '';
+        $packCreateData['status'] = 3;// 打包中
+        // 写入数据库
+        $packModel = (new PackModel())->where('channel_code',$inputData['uid'])->count();
+        if ($packModel){
+            if (!PackModel::query()->where('channel_code',$inputData['uid'])->update($packCreateData)){
+                return json_success([],'打包失败请重试');
+            }
+        }else{
+            if (!PackModel::query()->create($packCreateData)){
+                return json_success([],'打包失败请重试');
+            }
+        }
+        // 打包
+        dispatch(new AppPackJob($packArray))->onQueue('app_pack');
+        return json_success([],'开始打包,稍等1-2分钟');
+    }
+}

+ 291 - 0
Modules/AgentApi/Controllers/AuthController.php

@@ -0,0 +1,291 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-05-18 20:38:13
+ */
+
+
+namespace Modules\AgentApi\Controllers;
+
+
+use App\Repositories\AgentLogRepository;
+use App\Repositories\AgentRepository;
+use App\Repositories\Utils\JwtRepository;
+use Modules\AgentApi\Services\AgentAuthService;
+use Validator;
+use Lcobucci\JWT\Token;
+use Otp\Otp;
+use Otp\GoogleAuthenticator;
+use ParagonIE\ConstantTime\Encoding;
+
+class AuthController extends BaseController
+{
+    /**
+     * Jwt受众.
+     *
+     * @var string
+     */
+    private $jwtAud = 'agent-users';
+
+    /**
+     * 属性描述.
+     *
+     * @var AgentLogRepository
+     */
+    protected $logRepo = null;
+
+    public function login()
+    {
+        $inputData = $this->getJsonData();
+
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var AgentRepository $agentRepo
+         */
+        $agentRepo = app(AgentRepository::class);
+
+        /**
+         * @var AgentAuthService $authService
+         */
+        $authService = app(AgentAuthService::class);
+
+        $rules = [
+            'username' => 'required|max:64',
+            'password' => 'required|max:64',
+//            'verify' => 'required|max:12',
+//            'captcha_key' => 'required',
+            'ggcode' => 'nullable|max:10',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+//        if (!captcha_api_check($inputData['verify'], $inputData['captcha_key'], 'sanguo')) {
+//            return json_fail('验证码有误.');
+//        }
+
+
+        // 查询字段.
+        $columns = [
+            'id', 'uid', 'username', 'nick', 'password', 'salt', 'last_login_at', 'otp_secret', 'status',
+        ];
+        // 条件.
+        $conditions = ['username' => $inputData['username']];
+        $agents = $agentRepo->findWhere($conditions, $columns);
+
+        // 先判断是否存在用户.
+        if ($agents->count() == 0) {
+            $logSubject = '代理登陆获取token失败;原因:无此账号;入参:account['. $inputData['username'] .'];密码:['. pass_encode($inputData['password']) .']';
+            $logSvalue = '';
+            $logDbData = [];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            // 给一个不容易分析的错误信息给前台.
+            return json_fail('代理账号或者密码错误.');
+        }
+        // 取第一个用户.
+        $agent = $agents->first();
+        $md5password = $agentRepo->generatePassword($inputData['password'], $agent->salt);
+        if ($agent->password !== $md5password) {
+            $logSubject = '代理登陆获取token失败;原因:密码错误;入参:account['. $inputData['username'] .'];密码:['. pass_encode($inputData['password']) .']';
+            $logSvalue = '';
+            $logDbData = [];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return json_fail('账号或者密码错误.');
+        }
+        $ggcode = isset($inputData['ggcode']) ? $inputData['ggcode'] : '';
+        $isOpenOtp =  boolval(env('OTP_OPEN', true)); //是否开启谷歌身份码验证功能
+        if ($isOpenOtp) {
+            if (empty($ggcode)) {
+                return json_fail('请填写身份码.');
+            }
+            $otp = new Otp();
+            $otpSecret = !empty($agent->otp_secret) ? $agent->otp_secret : GoogleAuthenticator::generateRandom();
+            $otpCheckResult = $otp->checkTotp(Encoding::base32DecodeUpper($otpSecret), $ggcode);
+            if (!$otpCheckResult) {
+                $logSubject = '代理登陆获取token失败;原因:OTP错误;入参:用户名['. $inputData['username'] .'];密码:['. pass_encode($inputData['password']) .'];otp:['. pass_encode($ggcode) .']';
+                $logSvalue = '';
+                $logDbData = [];
+                $this->log($logSubject, $logSvalue, $logDbData);
+
+                return json_fail('身份码不正确');
+            }
+        }
+        if ($agent->status == 2) {
+            $logSubject = '代理登陆获取token失败;原因:被冻结;入参:username['. $inputData['username'] .'];密码:['. pass_encode($inputData['password']) .']';
+            $logSvalue = '';
+            $logDbData = [];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return json_fail('您已经被冻结,无法登陆.');
+        }
+        $logSubject = '登录成功';
+        $logSvalue = $agent->uid;
+        list($module, $action) = $this->getModuleAndAction();
+        $inputs = [
+            'agent_uid' => $agent->uid,
+            'username' => $agent->username,
+            'nick' => $agent->nick,
+            'subject' => mb_strcut($logSubject, 0, 700),
+            'module' => $module,
+            'action' => $action,
+            'svalue' => !empty($logSvalue) ? $logSvalue : '',
+            'db_data' => [],
+        ];
+
+        $this->getLogRepo()->createOne($inputs);
+
+        $tokenInfo = $authService->createSingleJwt($agent->uid);
+
+        app(AgentRepository::class)->update(['last_login_ip' => request()->ip(), 'last_login_at' => date('Y-m-d H:i:s')], $agent->uid, 'uid');
+
+        $data = [
+            'token' => $tokenInfo['token'],
+            'expired_at' => $tokenInfo['expired_at'],
+            'agent_uid' => $agent->uid,
+        ];
+
+        return json_success($data);
+    }
+
+    public function logout()
+    {
+    }
+
+    /**
+     * 刷新Token并返回token.
+     * @param  mixed  $validatorFactory
+     * @param  JwtRepository  $jwtRepository
+     * @param  AgentAuthService  $authService
+     * @return mixed
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function refreshToken() {
+
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var AgentRepository $agentRepo
+         */
+        $agentRepo = app(AgentRepository::class);
+
+        /**
+         * @var AgentAuthService $authService
+         */
+        $authService = app(AgentAuthService::class);
+        /**
+         * @var JwtRepository $jwtRepository
+         */
+        $jwtRepository = app(JwtRepository::class);
+
+        $inputData = $this->getJsonData();
+        $rules = [
+            'agent_uid' => 'required',
+            'old_token' => 'required',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $validateResult = $jwtRepository->validateToken($inputData['old_token'], $this->jwtAud);
+        if ($validateResult['is_illegal']) {
+            return json_fail('无效数据传入.');
+        }
+        if (!$validateResult['is_valid']) {
+            return json_fail('旧Token无效.');
+        }
+        /*if (!$validateResult['is_expired']) {
+            // 旧token没过期
+            return json_fail('旧token怎么了');
+        }*/
+        /**
+         * @var Token $tokenObj
+         */
+        $tokenObj = $validateResult['token_obj'];
+        $claims = $tokenObj->claims();
+        $agentUid = $claims->get('agent_uid');
+        // 检查入参设备信息是否有效.
+        if ($agentUid != $inputData['agent_uid']) {
+            $logSubject = '代理登陆刷新token失败;原因:入参agent_uid和token中的agent_uid不一致;入参:agent_uid['. $inputData['agent_uid'] .']';
+            $logSvalue = '';
+            $logDbData = [];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return json_fail('无效数据匹配.');
+        }
+        $tokenInfo = $authService->createSingleJwt($agentUid);
+
+        $data = [
+            'token' => $tokenInfo['token'],
+            'expired_at' => $tokenInfo['expired_at'],
+        ];
+
+        return json_success($data);
+    }
+
+    /**
+     * php ping
+     * @return string
+     */
+    public function ping()
+    {
+        return 'pong';
+    }
+
+    public function captchaImage()
+    {
+        return captcha('sanguo');
+    }
+
+    /**
+     * 记录操作日志.
+     * @param string $subject 主题.
+     * @param  string  $svalue 关联数据.
+     * @param  array  $dbData 此数组数据结构一定要是["after_items" => [0, 1, 2], "before_items" => [0, 1, 2]], after_items,before_items 是一个二维码数组.
+     * @return bool
+     */
+    protected function log(string $subject, $svalue = '', $dbData = [])
+    {
+        list($module, $action) = $this->getModuleAndAction();
+        $inputs = [
+            'agent_uid' => '',
+            'username' => '',
+            'nick' => '匿名',
+            'subject' => mb_strcut((string)$subject, 0, 700),
+            'module' => $module,
+            'action' => $action,
+            'svalue' => !empty($svalue) ? mb_strcut((string)$svalue, 0, 700) : '',
+            'db_data' => $dbData,
+        ];
+
+        $this->getLogRepo()->createOne($inputs);
+
+        return true;
+    }
+
+    /**
+     * 获取日志仓库.
+     * @return AgentLogRepository|null
+     */
+    protected function getLogRepo()
+    {
+        if (empty($this->logRepo)) {
+            $this->logRepo = make(AgentLogRepository::class);
+        }
+
+        return $this->logRepo;
+    }
+}

+ 141 - 0
Modules/AgentApi/Controllers/BaseController.php

@@ -0,0 +1,141 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:24
+ */
+namespace Modules\AgentApi\Controllers;
+
+use App\Models\Mysql\AgentModel;
+use App\Repositories\AgentLogRepository;
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Routing\Controller as RoutingController;
+use Modules\AgentApi\Services\AgentAuthService;
+use Route;
+
+class BaseController extends RoutingController
+{
+    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+
+    /**
+     * 当前代理UID.
+     *
+     * @var string
+     */
+    protected $ctAgentUid = '';
+
+    /**
+     * 当前管理员信息.
+     *
+     * @var string
+     */
+    protected $ctAgentInfo = [];
+
+    /**
+     * 登陆账户信息
+     * @var array
+     */
+    public $accountInfo = [];
+
+    /**
+     * 获取
+     * @return array|mixed
+     */
+    protected function getJsonData()
+    {
+        $arrJson = request()->all();
+        return $arrJson['data'] ?? [];
+    }
+
+    /**
+     * 获取日志需要的当前模块名和动作名.
+     * @return array
+     */
+    protected function getModuleAndAction()
+    {
+        $routeAction = $this->getCurrentRouteHandler();
+        $controller_suffix = 'Controller';
+        preg_match_all('/([a-z0-9A-Z]+\\\)?(\w+)@(\w+)/', $routeAction, $matchs);
+        $controller_full = !empty($matchs[2][0]) ? $matchs[2][0] : '';  //类名.
+        $controller = str_replace($controller_suffix, '', $controller_full);    // 控制器名.
+        $action = !empty($matchs[3][0]) ? $matchs[3][0] : '';    //方法名.
+
+        return [$controller, $action];
+    }
+
+    /**
+     * 获取当前路由话柄.
+     * @return string
+     */
+    protected function getCurrentRouteHandler()
+    {
+        return Route::currentRouteAction();
+    }
+
+    /**
+     * 获取当前代理对象信息.
+     * @return mixed
+     */
+    protected function getCurrentAgent()
+    {
+        $agentInfo = $this->getCurrentAgentInfo();
+
+        return AgentModel::where('uid', $agentInfo['agent_uid'])->first();
+    }
+
+    /**
+     * 获取当前代理信息.
+     * @return array
+     */
+    protected function getCurrentAgentInfo()
+    {
+        if (empty($this->ctAgentInfo)) {
+            $authService = new AgentAuthService();
+            $this->ctAgentInfo = $authService->getAgentInfo();
+            $this->ctAgentUid = $this->ctAgentInfo['agent_uid'];
+        }
+
+        return $this->ctAgentInfo;
+    }
+    /**
+     * 获取日志仓库.
+     * @return AgentLogRepository
+     */
+    protected function getLogRepo()
+    {
+        return make(AgentLogRepository::class);
+    }
+
+    /**
+     * 记录操作日志.
+     * @param string $subject 主题.
+     * @param  string  $svalue 关联数据.
+     * @param  array  $dbData 此数组数据结构一定要是["after_items" => [0, 1, 2], "before_items" => [0, 1, 2]], after_items,before_items 是一个二维码数组.
+     * @return bool
+     */
+    protected function log(string $subject, $svalue = '', $dbData = [])
+    {
+        $agent = $this->getCurrentAgent();
+        list($module, $action) = $this->getModuleAndAction();
+        $inputs = [
+            'agent_uid' => !empty($agent->uid) ? (string)$agent->uid : '',
+            'username' => '',
+            'nick' => '',
+            'subject' => mb_strcut((string)$subject, 0, 700),
+            'module' => $module,
+            'action' => $action,
+            'svalue' => !empty($svalue) ? mb_strcut((string)$svalue, 0, 700) : '',
+            'db_data' => $dbData,
+        ];
+        if (!empty($agent)) {
+            $inputs['username'] = $agent->username;
+            $inputs['nick'] = $agent->nick;
+        }
+
+        $this->getLogRepo()->createOne($inputs);
+
+        return true;
+    }
+}

+ 65 - 0
Modules/AgentApi/Controllers/ShareUrlController.php

@@ -0,0 +1,65 @@
+<?php
+
+namespace Modules\AgentApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Repositories\AgentRepository;
+use App\Repositories\ChannelRepository;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Repositories\Criterias\ShareUrlsPagesCriteria;
+use App\Repositories\ShareUrlsRepository;
+use Modules\AgentApi\Responses\ShareUrlsResponse;
+
+class ShareUrlController extends BaseController
+{
+    /**
+     * 分享列表
+     *
+     * @param AgentRepository $agentRepo
+     * @return ShareUrlsResponse
+     */
+    public function lists(AgentRepository $agentRepo)
+    {
+        $accountInfo = $this->getCurrentAgent();
+        /**
+         * 获取代理应用uid
+         * @var AgentRepository $agentRepo
+         */
+        $agentRepo = app(AgentRepository::class);
+        $appUidData = $agentRepo->findBy('uid', $accountInfo['uid'], ['own_app_uids']);
+        $appUids = explode(',', $appUidData->own_app_uids);
+
+        /**
+         * 获取渠道app
+         * @var ChannelRepository $channRepo
+         */
+        $channRepo = app(ChannelRepository::class);
+        $channData = $channRepo->agentChannel($accountInfo['uid'], $appUids);
+
+        /**
+         * 获取获取分享url
+         * @var ShareUrlsRepository $sharRepo
+         */
+        $sharRepo = app(ShareUrlsRepository::class);
+        $sharData = $sharRepo->findAllBy('status', 1, ['mom_app_uid', 'title', 'url', 'cover','sort'])->toArray();
+
+        $i = 0;
+        $res = [];
+        foreach ($appUids as  $key => $uid) {
+            foreach ($sharData as $sharVal) {
+                if ($sharVal['mom_app_uid'] == $uid && isset($channData[$uid]['uid'])) {
+                    $res[$key][$i]['mom_app_uid'] = $uid;
+                    $res[$key][$i]['url'] = $sharVal['url'];
+                    $res[$key][$i]['cover'] = $sharVal['cover'];
+                    $res[$key][$i]['title'] = $sharVal['title'];
+                    $res[$key][$i]['sort'] = $sharVal['sort'];
+                    $res[$key][$i]['channel_uid'] = $channData[$uid]['uid'];
+                    $i++;
+                }
+            }
+            $i = 0;
+        }
+
+        return new ShareUrlsResponse(array_values($res));
+    }
+}

+ 336 - 0
Modules/AgentApi/Controllers/TotalController.php

@@ -0,0 +1,336 @@
+<?php
+
+namespace Modules\AgentApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Models\Mongo\AgentAttrsTodayModel;
+use App\Models\Mongo\UsersModel;
+use App\Models\Mysql\AgentAttrsDayModel;
+use App\Models\Mysql\AgentModel;
+use App\Models\Mysql\AppModel;
+use App\Models\Mysql\CommissionLogModel;
+use App\Models\Mysql\PackModel;
+use App\Repositories\AgentAttrsDayRepository;
+use App\Repositories\AgentAttrsRepository;
+use App\Repositories\AgentRepository;
+use App\Repositories\AnnounceRepository;
+use App\Repositories\ChannelRepository;
+use App\Repositories\Criterias\AgentAttrsDayPagesCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Repositories\ShareUrlsRepository;
+use Carbon\Carbon;
+use Illuminate\Support\Facades\Validator;
+use Modules\AgentApi\Responses\TotalPagesResponse;
+
+class TotalController extends BaseController
+{
+    /*public function __construct()
+    {
+        $this->accountInfo = $this->getCurrentAgent();
+    }*/
+
+    public function home(AgentRepository $agentRepo, AgentAttrsRepository $attrsRepo)
+    {
+        $this->accountInfo = $this->getCurrentAgent();
+        $accountInfo = $agentRepo->findBy('uid', $this->accountInfo['uid']);
+
+        //获取统计数据
+        $res = $attrsRepo->getAgentSumTotal($accountInfo);
+
+        /**
+         * @var AnnounceRepository $annRespo
+         */
+        $annRespo = app(AnnounceRepository::class);
+        $annData = $annRespo->findWhere(['status' => 1, 'type' => 1], ['content', 'title']);
+        $res['announce'] = $annData;
+
+        // 获取落地页信息
+        /**
+         * 获取代理应用uid
+         * @var AgentRepository $agentRepo
+         */
+        $agentRepo = app(AgentRepository::class);
+        $appUidData = $agentRepo->findBy('uid', $accountInfo['uid'], ['own_app_uids']);
+        $appUids = explode(',', $appUidData->own_app_uids);
+        // 昨日新增
+
+        $yesterday = (new AgentAttrsDayModel())
+            ->where([
+                'agent_uid' => $accountInfo['uid'],
+                'td_date' => Carbon::now()->subDay()->toDateString(),
+            ])
+            ->first(['td_users_total', 'td_users_deduction']);
+//            ->selectRaw('sum(td_users_total) as td_users_total')
+//            ->selectRaw('sum(td_users_deduction) as td_users_deduction')
+//            ->get();
+        $res['yesterday_users_total'] = ($yesterday->td_users_total ?? 0) - ($yesterday->td_users_deduction ?? 0);
+
+
+        // 可提现余额
+        $res['cps_withdraw'] = (int)$accountInfo['balance'];
+        $commissionLogModel = new CommissionLogModel();
+        // 累计收益
+        $res['all_income'] = (int)$commissionLogModel->where('agent_uid', $accountInfo['uid'])->sum('commission_money');
+        //总收益
+        /**
+         * 获取渠道app
+         * @var ChannelRepository $channRepo
+         */
+        $channRepo = app(ChannelRepository::class);
+        $channData = $channRepo->agentChannel($accountInfo['uid'], $appUids);
+
+        $appPack = [];
+        // 渠道ID
+        foreach ($channData as $item) {
+            array_push($appPack, $item->uid);
+        }
+        // 查询打包渠道
+        $appPackModel = (new PackModel())->whereIn('channel_code', $appPack)->get();
+        $appPackInfo = [];
+        foreach ($appPackModel as $item) {
+            $appPackInfo[$item->channel_code] = $item->toArray();
+        }
+        // 查询app
+        $appModel = (new AppModel())->get();
+        $appInfo = [];
+        foreach ($appModel as $item) {
+            $appInfo[$item->uid] = $item->toArray();
+        }
+        /**
+         * 获取获取分享url
+         * @var ShareUrlsRepository $sharRepo
+         */
+        $sharRepo = app(ShareUrlsRepository::class);
+        $sharData = $sharRepo->findAllBy('status', 1,
+            ['mom_app_uid', 'title', 'url', 'cover', 'sort', 'note'])->toArray();
+        $appModel = (new AppModel())->pluck('name', 'uid')->toArray();
+        $shar = [];
+        foreach ($sharData as $key => $sharVal) {
+            if (isset($channData[$sharVal['mom_app_uid']])) {
+                if (isset($appPackInfo[$channData[$sharVal['mom_app_uid']]['uid']])) {
+                    $shar[$key]['pack'] = $appPackInfo[$channData[$sharVal['mom_app_uid']]['uid']];
+                }
+                // 以推广的落地页
+                $shar[$key]['status'] = 1;
+                $shar[$key]['users'] = UsersModel::where([
+                    'channel_uid' => $channData[$sharVal['mom_app_uid']]['uid'],
+                    'mom_app_uid' => $sharVal['mom_app_uid'],
+                    'is_deduction' => 0
+                ])->distinct('user_uid')->count();
+                $shar[$key]['channel_uid'] = $channData[$sharVal['mom_app_uid']]['uid'];
+            } else {
+                $shar[$key]['status'] = 0;
+                $shar[$key]['users'] = 0;
+                $shar[$key]['channel_uid'] = '';
+            }
+            $shar[$key]['app_name'] = $appModel[$sharVal['mom_app_uid']] . '短视频';
+            $shar[$key]['mom_app_uid'] = $sharVal['mom_app_uid'];
+            $shar[$key]['app'] = $appInfo[$sharVal['mom_app_uid']];
+            $shar[$key]['url'] = $sharVal['url'];
+            $shar[$key]['cover'] = $sharVal['cover'];
+            $shar[$key]['title'] = $sharVal['title'];
+            $shar[$key]['sort'] = $sharVal['sort'];
+            $shar[$key]['note'] = $sharVal['note'];
+
+        }
+        $res['links'] = array_values($shar);
+        return json_success($res);
+    }
+
+    /**
+     * 统计列表
+     *
+     * @return \Illuminate\Http\JsonResponse|TotalPagesResponse
+     */
+    public function lists()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'start_date' => 'nullable|string',
+            'end_date' => 'nullable|string',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $this->accountInfo = $this->getCurrentAgent();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $orders = [
+            ['td_date', 'DESC']
+        ];
+
+        $inputData['agent_uid'] = $this->accountInfo['uid'];
+
+        if (!isset($inputData['page_size']) || $inputData['page_size'] > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        } else {
+            $pageSize = $inputData['page_size'];
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        /**
+         * @var AgentAttrsDayRepository $attrsDayRepo
+         */
+        $attrsDayRepo = app(AgentAttrsDayRepository::class);
+        $field = [
+            'td_commission_money',
+            'td_date',
+            'td_deduction_success_money as user_commission_money',
+            'td_users_deduction',
+            'td_users_total'
+        ];
+        $res = $attrsDayRepo->pushCriteria(new AgentAttrsDayPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize,
+            $field, $page);
+
+        foreach ($res->items() as &$item) {
+            $startDate = Carbon::parse($item['td_date'])->startOfDay()->toDateTimeString();
+            $endDate = Carbon::parse($item['td_date'])->endOfDay()->toDateTimeString();
+            //用户抽成
+            $item['user_commission_money'] = $item['td_commission_money'];
+
+            $totalRake = AgentAttrsTodayModel::where('agent_uid', $this->accountInfo['uid'])->whereBetween('date',
+                [$startDate, $endDate])->sum('commission_money');
+            //下级抽成
+            $item['child_commission_money'] = ($totalRake - $item['user_commission_money']) < 0 ? 0 : $totalRake - $item['user_commission_money'];
+
+            //合计抽成
+            $item['total_commission_money'] = $item['user_commission_money'] + $item['child_commission_money'];
+
+            //今日下载
+            if ($this->accountInfo['uid'] == 'qkx07G' && date('Y-m-d') == '2022-07-30') {
+                $item['users_deduction'] = 2235;
+            } else {
+                $item['users_deduction'] = $item['td_users_total'] - $item['td_users_deduction'];
+            }
+        }
+
+        return new TotalPagesResponse($res);
+    }
+
+
+    /**
+     * cpa 的统计
+     */
+    public function cpaLists()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'start_date' => 'nullable|string',
+            'end_date' => 'nullable|string',
+            'mom_app_uid' => 'nullable|string',
+            'agent_uid' => 'nullable|string',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $accountInfo = $this->getCurrentAgent();
+        // 获取我下面的代理
+        $childUids = (new AgentModel())
+            ->where(['parent_uid' => $accountInfo->uid, 'status' => 1])
+            ->pluck('uid')->toArray();
+        $childUids = array_merge([$accountInfo->uid], $childUids);
+        if (!empty($inputData['app_uid'])) {
+            if (!in_array($inputData['app_uid'], $childUids)) {
+                return json_fail('无数据!');
+            }
+        }
+        $startTime = Carbon::now()->subDays(1)->startOfDay()->toDateTime();
+        $endTime = Carbon::now()->endOfDay()->endOfDay()->toDateTime();
+        if ($inputData['start_date'] && $inputData['end_date']) {
+            if (Carbon::parse($inputData['start_date'])->diffInDays(Carbon::parse($inputData['end_date'])) > 30) {
+                return json_fail('只能查询30天以内的数据!');
+            }
+            $startTime = Carbon::parse($inputData['start_date'])->startOfDay()->toDateTime();
+            $endTime = Carbon::parse($inputData['end_date'])->endOfDay()->toDateTime();
+        }
+        //查询代理名称
+        $agent = (new AgentModel())->whereIn('uid', $childUids)
+            ->select('uid', 'nick', 'agent_level')->get();
+        $agentKeyValue = $agent->pluck('nick', 'uid')->toArray();
+        $agentKeyLevel = $agent->pluck('agent_level', 'uid')->toArray();
+
+        //查询app
+        $app = (new AppModel())->select('name', 'uid')->get();
+        $appKeyValue = $app->pluck('name', 'uid')->toArray();
+        // 查询下载量一个月以内的
+        $users = (new UsersModel())
+            ->where('is_deduction', 0)
+            ->where(function ($q) use ($inputData, $childUids) {
+                if (!empty($inputData['agent_uid'])) {
+                    $q->where('agent_uid', $inputData['agent_uid']);
+                } else {
+                    $q->whereIn('agent_uid', $childUids);
+                }
+                if (!empty($inputData['mom_app_uid'])) {
+                    $q->where('mom_app_uid', $inputData['mom_app_uid']);
+                }
+            })
+            ->whereBetween('created_at', [
+                $startTime,
+                $endTime,
+            ])
+            ->orderByDesc('created_at')
+            ->get()->toArray();
+        $total = [];
+        foreach ($users as $user) {
+            $upData = explode(' ', $user['created_at'])[0];
+            $newKey = $upData . '_' . $user['agent_uid'] . '_' . $user['mom_app_uid'];
+            if (isset($total[$newKey])) {
+                // 存在说明渠道相同
+                $total[$newKey]['total'] += 1;
+            } else {
+                // 不存在新渠道
+                $total[$newKey] = [
+                    'agent_nick' => $agentKeyValue[$user['agent_uid']],
+                    'agent_uid' => $user['agent_uid'],
+                    'app_name' => $appKeyValue[$user['mom_app_uid']],
+                    'agent_level' => $agentKeyLevel[$user['agent_uid']],
+                    'created_at' => $upData,
+                    'total' => 1,
+                ];
+            }
+        }
+        $agentArray = $agent->toArray();
+        // 删除自己的放在第一位
+        unset($agentArray[array_search($accountInfo->uid, array_column($agentArray, 'uid'))]);
+        $agentArray = array_merge([
+            [
+                'uid' => $accountInfo->uid,
+                'nick' => $accountInfo->nick,
+                'agent_level' => $accountInfo->agent_level,
+            ]
+        ], $agentArray);
+
+        foreach ($total as &$totalV) {
+            if ($totalV['agent_uid'] == 'qkx07G' && $totalV['created_at'] == '2022-07-30') {
+                $totalV['total'] = 966;
+            }
+        }
+
+        $data = [
+            'data_list' => array_values($total),
+            'app_list' => $app->toArray(),
+            'agent_list' => $agentArray,
+        ];
+        return json_success($data);
+    }
+}

+ 413 - 0
Modules/AgentApi/Controllers/WithdrawController.php

@@ -0,0 +1,413 @@
+<?php
+
+namespace Modules\AgentApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Jobs\SendTgWithdrawMegJob;
+use App\Repositories\AgentBankInfoRepository;
+use App\Repositories\AgentRepository;
+use App\Repositories\AnnounceRepository;
+use App\Repositories\BalanceLogRepository;
+use App\Repositories\ConfigRepoistory;
+use App\Repositories\Criterias\FinanceWithdrawCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Repositories\WithDrawalLogRepository;
+use Carbon\Carbon;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Validator;
+use Modules\AgentApi\Responses\ConfigSuccessResponse;
+use Modules\AgentApi\Responses\WithdrawBankInfoResponse;
+use Modules\AgentApi\Responses\WithdrawLogResponse;
+
+class WithdrawController extends BaseController
+{
+    /*public function __construct()
+    {
+        $this->accountInfo = $this->getCurrentAgent();
+    }*/
+
+    /**
+     * 代理提现方式列表
+     *
+     * @param AgentBankInfoRepository $bankRepo
+     * @return WithdrawBankInfoResponse
+     */
+    public function bankInfo(AgentBankInfoRepository $bankRepo)
+    {
+
+        $this->accountInfo = $this->getCurrentAgent();
+        $bankInfo = $bankRepo->agentBankList($this->accountInfo['uid']);
+
+        /**
+         * @var ConfigRepoistory $confRepo
+         */
+        $confRepo = app(ConfigRepoistory::class);
+        $serviceLink = $confRepo->get('servicese_link', 'sys');
+
+        return new WithdrawBankInfoResponse($bankInfo, $serviceLink);
+    }
+
+    /**
+     * 新增提现方式
+     *
+     * @return \Illuminate\Http\JsonResponse|ConfigSuccessResponse
+     */
+    public function bankCreate()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'bank_card' => 'nullable|string',
+            'bank_account' => 'nullable|string',
+            'bank_mark' => 'nullable|string',
+            'type' => 'required|in:1,2,3',
+            'ustd_network' => 'nullable|string',
+            'ustd_account' => 'nullable|string',
+            'alipay_account' => 'nullable|string',
+            'alipay_username' => 'nullable|string',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $this->accountInfo = $this->getCurrentAgent();
+
+        /**
+         * @var AgentBankInfoRepository $bankRepo
+         */
+        $bankRepo = app(AgentBankInfoRepository::class);
+
+        $findBank = $bankRepo->findWhere(['type' => $inputData['type'], 'agent_uid' => $this->accountInfo['uid']],
+            ['id']);
+        if ($findBank->isNotEmpty()) {
+            return json_fail('请勿重复添加同一种提现方式!');
+        }
+
+        $setarr['type'] = $inputData['type'];
+        $setarr['agent_uid'] = $this->accountInfo['uid'];
+
+        if (!empty($inputData['alipay_account'])) {
+            $setarr['alipay_account'] = $inputData['alipay_account'];
+        }
+
+        if (!empty($inputData['alipay_username'])) {
+            $setarr['alipay_username'] = $inputData['alipay_username'];
+        }
+
+        if (!empty($inputData['bank_card'])) {
+            $setarr['bank_card'] = $inputData['bank_card'];
+        }
+
+        if (!empty($inputData['bank_account'])) {
+            $setarr['bank_account'] = $inputData['bank_account'];
+        }
+
+        if (!empty($inputData['bank_mark'])) {
+            $setarr['bank_mark'] = $inputData['bank_mark'];
+        }
+
+        if (!empty($inputData['ustd_network'])) {
+            $setarr['ustd_network'] = $inputData['ustd_network'];
+        }
+
+        if (!empty($inputData['ustd_account'])) {
+            $setarr['ustd_account'] = $inputData['ustd_account'];
+        }
+
+        $result = $bankRepo->create($setarr);
+
+        if (!$result) {
+            return json_fail('新增提现方式失败');
+        } else {
+            $logSubject = '新增一个提现银行卡,:' . $result->uid;
+            $logSvalue = $result->uid;
+            $logDbData = [
+                'after_items' => [
+                    $setarr,
+                ],
+                'before_items' => [
+
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+            return new ConfigSuccessResponse();
+        }
+    }
+
+    /**
+     * 提现列表
+     *
+     * @return \Illuminate\Http\JsonResponse|WithdrawLogResponse
+     */
+    public function lists()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'start_date' => 'nullable|string',
+            'end_date' => 'nullable|string',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $this->accountInfo = $this->getCurrentAgent();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $orders = [
+            ['created_at', 'DESC']
+        ];
+
+        if (!isset($inputData['page_size']) || $inputData['page_size'] > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        } else {
+            $pageSize = $inputData['page_size'];
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        /**
+         * @var WithDrawalLogRepository $witRepo
+         */
+
+        $witRepo = app(WithDrawalLogRepository::class);
+        $inputData['agent_uid'] = $this->accountInfo['uid'];
+        $field = ['created_at', 'paid_at', 'status', 'uid', 'type', 'amount', 'usdt_rate'];
+        $res = $witRepo->pushCriteria(new FinanceWithdrawCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize,
+            $field, $page);
+
+        /**
+         * @var AgentRepository $agentRepo
+         */
+        $agentRepo = app(AgentRepository::class);
+        $accountInfo = $agentRepo->findBy('uid', $this->accountInfo['uid'], ['frozen', 'balance']);
+
+        /**
+         * @var AnnounceRepository $annRespo
+         */
+        $annRespo = app(AnnounceRepository::class);
+        $annData = $annRespo->findWhere(['status' => 1, 'type' => 2], ['content', 'title']);
+
+        return new WithdrawLogResponse($res, $accountInfo, $annData);
+    }
+
+    /**
+     * 提现账户详情
+     *
+     * @return mixed
+     */
+    public function accountInfo()
+    {
+        $this->accountInfo = $this->getCurrentAgent();
+
+        /**
+         * @var AgentRepository $agentRepo
+         */
+        $agentRepo = app(AgentRepository::class);
+        $accountInfo = $agentRepo->findBy('uid', $this->accountInfo['uid'], ['balance']);
+        /**
+         * @var AgentBankInfoRepository $bankList
+         */
+        $bankRepo = app(AgentBankInfoRepository::class);
+        $bankList = $bankRepo->agentBankList($this->accountInfo['uid'],
+            ['type', 'bank_account', 'ustd_account', 'alipay_account', 'uid']);
+
+        /**
+         * @var ConfigRepoistory $confRepo
+         */
+        $confRepo = app(ConfigRepoistory::class);
+        $usdtRate = $confRepo->get('usdt_rate', 'sys');
+
+        if ($this->accountInfo['withdraw_usdt']) {
+            $minUsdt = $this->accountInfo['withdraw_usdt'];
+        } else {
+            $minUsdt = $confRepo->get('withdraw_min_usdt', 'sys');
+        }
+        if ($this->accountInfo['withdraw_amount']) {
+            $minMoney = $this->accountInfo['withdraw_amount'];
+        } else {
+            $minMoney = $confRepo->get('withdraw_min_money', 'sys');
+        }
+
+
+        $res['balance'] = $accountInfo->balance;
+        $res['usdt_rate'] = $usdtRate;
+        $res['bank_list'] = $bankList;
+        $res['withdraw_min_usdt'] = $minUsdt;
+        $res['withdraw_min_money'] = $minMoney;
+
+        return json_success($res);
+    }
+
+    /**
+     * 提现申请
+     *
+     * @return \Illuminate\Http\JsonResponse|ConfigSuccessResponse
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function apply()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'bank_uid' => 'required|string',
+            'type' => 'required|in:1,2,3',
+            'amount' => 'required|integer',
+            'withdraw_pwd' => 'required',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $this->accountInfo = $this->getCurrentAgent();
+
+        /**
+         * @var ConfigRepoistory $confRepo
+         */
+        $confRepo = app(ConfigRepoistory::class);
+
+        $rate = 0;
+        if ($inputData['type'] == 2) {
+            $rate = $confRepo->get('usdt_rate', 'sys');
+            $amount = intval($inputData['amount'] * $rate);
+        } else {
+            $amount = $inputData['amount'];
+        }
+
+        /**
+         * @var AgentRepository $agentRepo
+         */
+        $agentRepo = app(AgentRepository::class);
+        $accountInfo = $agentRepo->findBy('uid', $this->accountInfo['uid']);
+        $oldAccountInfo = clone $accountInfo;
+
+        //最低提现金额
+        if ($inputData['type'] == 2) {
+            if ($accountInfo['withdraw_usdt']) {
+                $minUsdt = $accountInfo['withdraw_usdt'];
+            } else {
+                $minUsdt = $confRepo->get('withdraw_min_usdt', 'sys');
+            }
+            if ($minUsdt > $amount) {
+                return json_fail('最低提现为' . $minUsdt . 'U');
+            }
+        } else {
+            if ($accountInfo['withdraw_amount']) {
+                $minMoney = $accountInfo['withdraw_amount'];
+            } else {
+                $minMoney = $confRepo->get('withdraw_min_money', 'sys');
+            }
+            if ($minMoney > $amount) {
+                return json_fail('最低提现金额为' . $minMoney . '元');
+            }
+        }
+
+
+        if ($accountInfo->balance < $amount) {
+            return json_fail('提现余额不足,请重新申请!');
+        }
+
+        if ($accountInfo->withdraw_pwd != $inputData['withdraw_pwd']) {
+            return json_fail('提现密码错误!');
+        }
+
+        /**
+         * @var BalanceLogRepository $bLogRepo
+         */
+        $bLogRepo = app(BalanceLogRepository::class);
+
+        /**
+         * @var WithDrawalLogRepository $witRepo
+         */
+        $witRepo = app(WithDrawalLogRepository::class);
+
+        /**
+         * @var AgentBankInfoRepository $bankList
+         */
+        $bankRepo = app(AgentBankInfoRepository::class);
+        $bankInfo = $bankRepo->findByWhere(['uid' => $inputData['bank_uid']]);
+        if (!$bankInfo) {
+            return json_fail('提现账户异常,请联系客服!');
+        }
+
+        DB::beginTransaction();
+
+        try {
+            //帐变操作
+            $accountInfo->balance -= $amount;
+            $accountInfo->frozen += $amount;
+            $res1 = $accountInfo->save();
+            if (!$res1) {
+                DB::rollBack();
+                return json_fail('提现申请失败!');
+            }
+
+            //写入提现记录
+            $instData = [
+                'status' => 2,
+                'agent_uid' => $this->accountInfo['uid'],
+                'amount' => $amount,
+                'type' => $inputData['type'],
+                'before_amount' => $oldAccountInfo->balance,
+                'after_amount' => $oldAccountInfo->balance - $amount,
+                'bank_account' => $bankInfo->bank_account,
+                'bank_card' => $bankInfo->bank_card,
+                'bank_mark' => $bankInfo->bank_mark,
+                'ustd_network' => $bankInfo->ustd_network,
+                'ustd_account' => $bankInfo->ustd_account,
+                'alipay_account' => $bankInfo->alipay_account,
+                'alipay_username' => $bankInfo->alipay_username,
+                'usdt_rate' => $rate,
+            ];
+            $res2 = $witRepo->create($instData);
+            if (!$res2) {
+                DB::rollBack();
+                return json_fail('提现异常!');
+            }
+
+            $res3 = $bLogRepo->record($accountInfo, $oldAccountInfo);
+            if (!$res3) {
+                DB::rollBack();
+                return json_fail('提现失败!');
+            }
+
+            DB::commit();
+        } catch (\Throwable $e) {
+            var_dump($e->getMessage());
+            DB::rollBack();
+            return json_fail('提现异常,请联系客服!');
+        }
+        $this->dispatch(new SendTgWithdrawMegJob(
+            $this->accountInfo['uid'],
+            $this->accountInfo['username'],
+            $inputData['type'],
+            $inputData['amount'],
+            Carbon::now()->toDateTimeString()
+        ));
+        return new ConfigSuccessResponse();
+    }
+
+    public function usdtNetWork()
+    {
+        $this->accountInfo = $this->getCurrentAgent();
+        $confRepo = app(ConfigRepoistory::class);
+        $usdtAddress = $confRepo->get('usdt_address', 'sys');
+        return json_success(explode(',', $usdtAddress));
+    }
+}

+ 110 - 0
Modules/AgentApi/Middlewares/AgentAuthMiddleware.php

@@ -0,0 +1,110 @@
+<?php
+/**
+ * 后台认证中间件.
+ *
+ * @author nj
+ * @date   2019-11-03 14:07:35
+ */
+
+namespace Modules\AgentApi\Middlewares;
+
+use App\Constants\ErrorCodeConstant;
+use Closure;
+use App\Repositories\Utils\AccessControllerListsRepository;
+use App\Repositories\Utils\JwtRepository;
+use Modules\AgentApi\Services\AgentAuthService;
+use Cache;
+
+class AgentAuthMiddleware
+{
+    protected $except = [];   //URI里排除的操作
+
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        if ($this->shouldPassThrough($request)) {
+            return $next($request);
+        }
+        /**
+         * @var AgentAuthService $authService
+         */
+        $authService = app(AgentAuthService::class);
+        $token = $authService->getHeaderToken();
+        if (empty($token)) {
+            return json_fail(trans('common.err_failed_authorization'), 403);
+        }
+        /**
+         * @var JwtRepository $jwtRepo
+         */
+        $jwtRepo = make(JwtRepository::class);
+        $aud = 'agent-users';
+        $validateResult = $jwtRepo->validateToken($token, $aud);
+        if (!$validateResult['is_valid']) {
+            return json_fail(trans('common.err_failed_authorization'), 403);
+        } else {
+            if ($validateResult['is_expired']) {
+                return json_fail('认证过期', 401);
+            }
+            // 验证单点.
+            $claim = $validateResult['token_obj']->claims();
+            $tokenAgentUid = $claim->get('agent_uid');
+            $tokenIp = $claim->get('ip');
+            $clientIp = $jwtRepo->getClientIp();
+            $cacheKey = $authService->getAgentCacheKey($tokenAgentUid);
+            /**
+             * @var Cache $cache
+             */
+            $cache = make('cache');
+            $cacheToken = $cache->has($cacheKey) ? $cache->get($cacheKey) : '';
+            if (empty($cacheToken) || $cacheToken !== $token) {
+                return json_fail(trans('common.err_failed_authorization'), 403);
+            }
+            if (empty($tokenIp) || empty($clientIp) || $tokenIp !== $clientIp) {
+                return json_fail(trans('common.err_failed_authorization'), 403);
+            }
+        }
+
+        return $next($request);
+    }
+
+    private function getExcepts()
+    {
+        $uriPrefix = env('AGENT_API_URI_PREFIX', 'agent_api');
+        $this->except = [
+            $uriPrefix . '/ping',
+            $uriPrefix .'/auth/login',
+            $uriPrefix . '/auth/refresh/token',
+            $uriPrefix . '/auth/logout',
+            $uriPrefix . '/chunqiu/captcha',
+        ];
+
+        return $this->except;
+    }
+
+    /**
+     * Determine if the request has a URI that should pass through CSRF verification.
+     *
+     * @param  \Illuminate\Http\Request $request
+     * @return bool
+     */
+    protected function shouldPassThrough($request)
+    {
+        foreach ($this->getExcepts() as $except) {
+            if ($except !== '/') {
+                $except = trim($except, '/');
+            }
+
+            if ($request->is($except)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 57 - 0
Modules/AgentApi/Requests/BaseRequest.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:25
+ */
+namespace Modules\AgentApi\Requests;
+
+use App\Exceptions\ApiException;
+use Illuminate\Foundation\Http\FormRequest;
+use \Illuminate\Contracts\Validation\Validator;
+
+class BaseRequest extends FormRequest
+{
+
+    /**
+     * Determine if the user is authorized to make this request.
+     *
+     * @return bool
+     */
+    public function authorize()
+    {
+        //默认放行auth校验
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        return [
+            //
+        ];
+    }
+
+    /**
+     * 重定义校验失败
+     * @param Validator $validator
+     * @throws ApiException
+     */
+    protected function failedValidation(Validator $validator)
+    {
+        throw new ApiException($validator->messages()->first(), 500);
+    }
+
+    /**
+     * 重定义认证失败
+     * @throws ApiException
+     */
+    protected function failedAuthorization()
+    {
+        throw new ApiException('认证错误', 403);
+    }
+}

+ 51 - 0
Modules/AgentApi/Responses/AgentDetailResponse.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * 管理员详情返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 17:36:29
+ */
+
+
+namespace Modules\AgentApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class AgentDetailResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        if (empty($this->data)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $result = $this->data->toArray();
+        if (isset($result['role']['permissions']) && is_string($result['role']['permissions'])) {
+            $arrPermissions = !empty($result['role']['permissions']) ? explode(',', $result['role']['permissions']) : [];
+            $result['role']['permissions'] = $arrPermissions;
+        }
+        unset($result['id']);
+
+        return json_success($result);
+    }
+}

+ 65 - 0
Modules/AgentApi/Responses/AgentPagesResponse.php

@@ -0,0 +1,65 @@
+<?php
+/**
+ * 代理分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 16:19:51
+ */
+
+namespace Modules\AgentApi\Responses;
+
+use App\Models\Mongo\UsersModel;
+use App\Models\Mysql\CommissionLogModel;
+use Carbon\Carbon;
+use Illuminate\Contracts\Support\Responsable;
+
+class AgentPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data;
+        $startDate = Carbon::parse()->startOfDay()->toDateTimeString();
+        $endDate = Carbon::parse()->endOfDay()->toDateTimeString();
+
+        $userModel = new UsersModel();
+        $commissionLogModel = new CommissionLogModel();
+        if ($this->data->isNotEmpty()) {
+            foreach ($result->items() as &$v) {
+                //今日抽成
+                $v->td_commission_money = $commissionLogModel::whereBetween('date', [$startDate, $endDate])->where('agent_uid', $v['uid'])->sum('commission_money');
+
+                //今日注册(扣量后)
+                $v->td_users_total = $userModel::whereBetween('created_at', [toMongoIsoDate($startDate), toMongoIsoDate($endDate)])->where(['is_deduction' => 0, 'agent_uid' => $v['uid']])->count();
+
+                //总抽成
+                $v->commission_money = $commissionLogModel::where('agent_uid', $v['uid'])->sum('commission_money');
+
+                //总注册
+                $v->users_total = $userModel::where(['is_deduction' => 0, 'agent_uid' => $v['uid']])->count();
+            }
+        }
+
+        return json_success($result);
+    }
+}

+ 37 - 0
Modules/AgentApi/Responses/CommonSuccessUidResponse.php

@@ -0,0 +1,37 @@
+<?php
+/**
+ * 通用返回成功.
+ *
+ * @author nj
+ * @date   2021-01-24 01:11:06
+ */
+
+namespace Modules\AgentApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class CommonSuccessUidResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $uid = null;
+
+    public function __construct($uid = null)
+    {
+        $this->uid = $uid;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = [
+            'result' => 'success',
+            'uid' => $this->uid,
+        ];
+
+        return json_success($result);
+    }
+}

+ 29 - 0
Modules/AgentApi/Responses/ConfigSuccessResponse.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace Modules\AgentApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class ConfigSuccessResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $uid = null;
+
+    public function __construct($skey = null)
+    {
+        $this->skey = $skey;
+    }
+
+    public function toResponse($request = null)
+    {
+        $result = [
+            'result' => 'success',
+        ];
+
+        return json_success($result);
+    }
+}

+ 47 - 0
Modules/AgentApi/Responses/NotIncrementIdDetailResponse.php

@@ -0,0 +1,47 @@
+<?php
+/**
+ * 管理员分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 16:19:51
+ */
+
+namespace Modules\AgentApi\Responses;
+
+use Illuminate\Contracts\Support\Arrayable;
+use Illuminate\Contracts\Support\Responsable;
+
+class NotIncrementIdDetailResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        if ($this->data instanceof Arrayable) {
+            $result = $this->data->toArray();
+        } else {
+            $result = $this->data;
+        }
+
+        return json_success($result);
+    }
+}

+ 30 - 0
Modules/AgentApi/Responses/ShareUrlsResponse.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Modules\AgentApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+use KZ2\Laravel\Repositories\Contracts\RepositoryInterface as Repository;
+use KZ2\Laravel\Repositories\Criteria\Criteria;
+
+class ShareUrlsResponse implements Responsable
+{
+    private $data = [];
+
+    /**
+     * 代理渠道
+     *
+     * @var array
+     */
+    private $channData = [];
+
+    public function __construct($params = [], $channData = [])
+    {
+        $this->data = $params;
+
+    }
+
+    public function toResponse($request)
+    {
+        return json_success($this->data);
+    }
+}

+ 45 - 0
Modules/AgentApi/Responses/SuccessIdResponse.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:57
+ */
+namespace Modules\AgentApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class SuccessIdResponse implements Responsable
+{
+    protected $data = null;
+
+    private $params = [];
+
+    /**
+     * 构造
+     * GetTokenResponse constructor.
+     * @param $data
+     * @param $params
+     */
+    public function __construct($data, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    /**
+     * 响应结果
+     *
+     * @param \Illuminate\Http\Request $request
+     * @return array
+     */
+    public function toResponse($request)
+    {
+        $result = [];
+        $data = is_object($this->data) ? $this->data->toArray() : $this->data;
+        if (!empty($data['id'])) {
+            $result['id'] = $data['id'];
+        }
+
+        return json_success($result);
+    }
+}

+ 27 - 0
Modules/AgentApi/Responses/TotalPagesResponse.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Modules\AgentApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class TotalPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    public function __construct($data = null)
+    {
+        $this->data = $data;
+    }
+
+    public function toResponse($request)
+    {
+        $result = $this->data->toArray();
+
+        return json_success($result);
+    }
+}

+ 46 - 0
Modules/AgentApi/Responses/WithdrawBankInfoResponse.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace Modules\AgentApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class WithdrawBankInfoResponse implements Responsable
+{
+    protected $data = null;
+
+    /**
+     * 客服链接
+     * @var string
+     */
+    private $serviceLink = '';
+
+
+    /**
+     * 构造
+     * GetTokenResponse constructor.
+     * @param $data
+     * @param $params
+     */
+    public function __construct($data, $serviceLink)
+    {
+        $this->data = $data;
+
+        $this->serviceLink = $serviceLink;
+    }
+
+    public function toResponse($request)
+    {
+
+        $result['data'] = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                unset($v['id']);
+            }
+            unset($v['id']);
+        }
+
+        $result['service_link'] = $this->serviceLink;
+
+        return json_success($result);
+    }
+}

+ 52 - 0
Modules/AgentApi/Responses/WithdrawLogResponse.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace Modules\AgentApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class WithdrawLogResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 账号信息
+     *
+     * @var array|mixed
+     */
+    private $accountInfo = [];
+
+    /**
+     * 公告信息
+     *
+     * @var array
+     */
+    protected $annData = [];
+
+    public function __construct($data = null, $accountInfo = [], $annData = [])
+    {
+        $this->data = $data;
+
+        $this->accountInfo = $accountInfo;
+
+        $this->annData = $annData;
+    }
+
+    public function toResponse($request)
+    {
+        $result = $this->data->toArray();
+
+        //账户余额
+        $result['account_info']['balance'] = $this->accountInfo['balance'] + $this->accountInfo['frozen'];
+        //可提现金额
+        $result['account_info']['withdraw_balance'] = $this->accountInfo['balance'];
+        //公告信息
+        $result['announce'] = $this->annData;
+
+        return json_success($result);
+    }
+}

+ 112 - 0
Modules/AgentApi/Services/AgentAuthService.php

@@ -0,0 +1,112 @@
+<?php
+/**
+ * 认证服务.
+ *
+ * @author nj
+ * @date   2021-01-22 11:36:11
+ */
+
+namespace Modules\AgentApi\Services;
+
+use App\Constants\AgentConstant;
+use App\Constants\CommonConstant;
+use App\Constants\ManageConstant;
+use App\Repositories\Utils\JwtRepository;
+use Psr\SimpleCache\CacheInterface;
+use Cache;
+use Str;
+
+class AgentAuthService
+{
+    /**
+     * Jwt受众.
+     *
+     * @var string
+     */
+    private $jwtAud = 'agent-users';
+
+    /**
+     * 获取存储用户设备Token的缓存Key.
+     * @param string $agentUid
+     * @return string
+     */
+    public function getAgentCacheKey($agentUid)
+    {
+        $cacheKey = 'agent_auth_'. $agentUid;
+
+        return $cacheKey;
+    }
+
+    /**
+     * 创建单点Jwt.
+     * @param string $agentUid 代理UID
+     * @param int $roleId 角色ID
+     * @return array
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function createSingleJwt($agentUid)
+    {
+        $agent = new \stdClass();
+        $agent->uid = $agentUid;
+        /**
+         * @var JwtRepository $jwtRepository
+         */
+        $jwtRepository = make(JwtRepository::class);
+        $tokenInfo = $jwtRepository->createAgentJwt($agent);
+        $token = $tokenInfo['token'];
+
+        $cacheKey = $this->getAgentCacheKey($agentUid);
+        /**
+         * @var Cache $cache
+         */
+        $cache = make('cache');
+        $cacheToken = $cache->has($cacheKey) ? $cache->get($cacheKey) : '';
+        if (!empty($cacheToken)) {
+            $cache->delete($cacheKey);
+        }
+
+        $cache->set($cacheKey, $token, config('jwt.ttl') * 60);
+
+        return $tokenInfo;
+    }
+
+    /**
+     * 获取头部Token.
+     * @return mixed
+     */
+    public function getHeaderToken()
+    {
+        $token = request()->header(AgentConstant::AG_TOKEN_NAME, null);
+        if (empty($token)) {
+            // 支持GET/PUT获取token
+            $token = request()->input(AgentConstant::AG_TOKEN_NAME, null);
+        }
+
+        return $token;
+    }
+
+    /**
+     * 获取Token中的AgentInfo
+     * @return array  返回agent_uid
+     */
+    public function getAgentInfo()
+    {
+        $managerInfo = [];
+        $token = $this->getHeaderToken();
+        if (!empty($token)) {
+            /**
+             * @var JwtRepository $jwtRepository
+             */
+            $jwtRepository = make(JwtRepository::class);
+            $validateResult = $jwtRepository->validateToken($token, $this->jwtAud);
+            if ($validateResult['is_valid']) {
+                $claim = $validateResult['token_obj']->claims();
+                $managerInfo = [
+                    'agent_uid' => !empty($claim->get('agent_uid')) ? $claim->get('agent_uid') : '',
+                ];
+            }
+        }
+
+        return $managerInfo;
+    }
+}

+ 17 - 0
Modules/Api/Controllers/BaseController.php

@@ -0,0 +1,17 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:24
+ */
+namespace Modules\Api\Controllers;
+
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Routing\Controller as RoutingController;
+
+class BaseController extends RoutingController
+{
+    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+}

+ 57 - 0
Modules/Api/Requests/BaseRequest.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:25
+ */
+namespace Modules\Api\Requests;
+
+use App\Exceptions\ApiException;
+use Illuminate\Foundation\Http\FormRequest;
+use \Illuminate\Contracts\Validation\Validator;
+
+class BaseRequest extends FormRequest
+{
+
+    /**
+     * Determine if the user is authorized to make this request.
+     *
+     * @return bool
+     */
+    public function authorize()
+    {
+        //默认放行auth校验
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        return [
+            //
+        ];
+    }
+
+    /**
+     * 重定义校验失败
+     * @param Validator $validator
+     * @throws ApiException
+     */
+    protected function failedValidation(Validator $validator)
+    {
+        throw new ApiException($validator->messages()->first(), 500);
+    }
+
+    /**
+     * 重定义认证失败
+     * @throws ApiException
+     */
+    protected function failedAuthorization()
+    {
+        throw new ApiException('认证错误', 403);
+    }
+}

+ 45 - 0
Modules/Api/Responses/SuccessIdResponse.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:57
+ */
+namespace Modules\Api\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class SuccessIdResponse implements Responsable
+{
+    protected $data = null;
+
+    private $params = [];
+
+    /**
+     * 构造
+     * GetTokenResponse constructor.
+     * @param $data
+     * @param $params
+     */
+    public function __construct($data, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    /**
+     * 响应结果
+     *
+     * @param \Illuminate\Http\Request $request
+     * @return array
+     */
+    public function toResponse($request)
+    {
+        $result = [];
+        $data = is_object($this->data) ? $this->data->toArray() : $this->data;
+        if (!empty($data['id'])) {
+            $result['id'] = $data['id'];
+        }
+
+        return json_success($result);
+    }
+}

+ 12 - 0
Modules/Api/Services/DemoService.php

@@ -0,0 +1,12 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:59
+ */
+namespace Modules\Api\Services;
+
+class DemoService
+{
+
+}

+ 26 - 0
Modules/InnerApi/Controllers/BaseController.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:24
+ */
+namespace Modules\InnerApi\Controllers;
+
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Routing\Controller as RoutingController;
+
+class BaseController extends RoutingController
+{
+    use DispatchesJobs, ValidatesRequests;
+
+    protected function getBodyRaw()
+    {
+        return request()->getContent();
+    }
+
+    protected function getJsonData()
+    {
+        return request()->json()->all();
+    }
+}

+ 44 - 0
Modules/InnerApi/Controllers/ChannelsController.php

@@ -0,0 +1,44 @@
+<?php
+namespace Modules\InnerApi\Controllers;
+
+use App\Models\Mysql\PackModel;
+use Illuminate\Support\Facades\Validator;
+
+class ChannelsController extends BaseController
+{
+    public function PackInfo(){
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'agent_uid' => 'required',
+            'channel_code' => 'required',
+            'mom_app_uid' => 'required',
+            'android_or_ios' => 'required',
+            'app_download' => 'required',
+            'mom_app_version' => 'required',
+        ];
+        $validator = Validator::make($inputData, $rules);
+
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $packModel = (new PackModel())->where([
+            'channel_code'=>$inputData['channel_code'],
+            'mom_app_uid'=>$inputData['mom_app_uid'],
+            'android_or_ios'=>$inputData['android_or_ios'],
+            'mom_app_version'=>$inputData['mom_app_version'],
+        ])->first();
+        if ($packModel){
+            $packModel->status = 1;
+            $packModel->app_download = $inputData['app_download'];
+            if (!$packModel->save()){
+                logger('储存失败',$inputData);
+                return json_fail('储存失败!');
+            }
+            return json_success([],'OK');
+        }
+        logger('此渠道未进行打处理!',$inputData);
+        return json_success([],'此渠道未进行打处理!');
+    }
+}

+ 51 - 0
Modules/InnerApi/Controllers/PayController.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * 支付.
+ *
+ * @author nj
+ * @date   2021-07-21 15:19
+ */
+
+namespace Modules\InnerApi\Controllers;
+
+use App\Models\Mongo\PayLogsModel;
+use App\Models\Mysql\ChannelModel;
+use Modules\InnerApi\Requests\PayIncRequest;
+use Modules\InnerApi\Responses\SuccessCommonResponse;
+use Modules\InnerApi\Services\PayService;
+
+class PayController extends BaseController
+{
+    public function inc(PayIncRequest $request, PayService $service)
+    {
+        $arrJson = $this->getJsonData();
+        if (!empty($arrJson)) {
+            foreach ($arrJson as $k => &$item) {
+                $item['agent_uid'] = '';
+                $item['is_deduction'] = 0;
+                if (!empty($item['share_code']) && empty($item['invite_uid'])) {
+                    // 兼容一下,置顶定义混乱了, share_code, invite_uid.
+                    $item['invite_uid'] = $item['share_code'];
+                }
+                if (!empty($item['channel_uid'])) {
+                    $channelInfo = ChannelModel::where(['uid' => $item['channel_uid'], 'mom_app_uid' => $item['mom_app_uid']])->first(['agent_uid']);
+                    if (empty($channelInfo)) {
+                        logger('新增充值查不到渠道的报错日志:' . json_encode($item, true));
+                        continue;
+                    }
+                    $item['agent_uid'] = $channelInfo->agent_uid;
+                    $orderInt = PayLogsModel::query()->where(['order_sn' => $item['order_sn'], 'pay_status' => 1, 'order_status' => 1])->count();
+                    if (empty($orderInt)) {
+                        // 扣量计算
+                        $item = $service->oneRandDeduction($item);
+                    }
+                }
+
+                $arrJson[$k] = $item;
+            }
+            $service->inc($arrJson);
+        }
+
+        return new SuccessCommonResponse();
+    }
+}

+ 98 - 0
Modules/InnerApi/Controllers/UsersController.php

@@ -0,0 +1,98 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-07-10 16:41
+ */
+
+namespace Modules\InnerApi\Controllers;
+
+use App\Models\Mongo\UsersModel;
+use App\Models\Mysql\ChannelModel;
+use Modules\InnerApi\Requests\UserIncRequest;
+use Modules\InnerApi\Requests\UserLoginRequest;
+use Modules\InnerApi\Requests\UserUpRequest;
+use Modules\InnerApi\Responses\SuccessCommonResponse;
+use Modules\InnerApi\Services\UserService;
+
+class UsersController extends BaseController
+{
+    /**
+     * 新用户.
+     * @param UserIncRequest $request
+     * @param UserService $service
+     * @return SuccessCommonResponse
+     */
+    public function inc(UserIncRequest $request, UserService $service)
+    {
+
+        $arrJson = $this->getJsonData();
+        if (!empty($arrJson)) {
+            foreach ($arrJson as $k => $item) {
+                $item['agent_uid'] = '';
+                if (!empty($item['share_code']) && empty($item['invite_uid'])) {
+                    // 兼容一下,置顶定义混乱了, share_code, invite_uid.
+                    $item['invite_uid'] = $item['share_code'];
+                }
+                    // 渠道是空字符串,代表官方包.
+                    $item['is_deduction'] = 0;
+                } else {
+                    $channelInfo = ChannelModel::where(['uid' => $item['channel_uid'], 'mom_app_uid' => $item['mom_app_uid']])->first(['agent_uid']);
+                    if (!empty($channelInfo)) {
+                        $item['agent_uid'] = $channelInfo->agent_uid;
+                        // 扣量.
+                        $item = $service->oneRandDeduction($item);
+                    }
+                }
+                $arrJson[$k] = $item;
+            }
+            $users = $service->inc($arrJson);
+        }
+
+        return new SuccessCommonResponse();
+    }
+
+    /**
+     * 登录.
+     * @param UserLoginRequest $request
+     * @param UserService $service
+     * @return SuccessCommonResponse
+     */
+    public function login(UserLoginRequest $request, UserService $service)
+    {
+        $arrJson = $this->getJsonData();
+        if (!empty($arrJson)) {
+            foreach ($arrJson as &$item) {
+                $item['agent_uid'] = '';
+                if (!empty($item['channel_uid'])) {
+                    $channelInfo = ChannelModel::where(['uid' => $item['channel_uid'], 'mom_app_uid' => $item['mom_app_uid']])->first(['agent_uid']);
+                    if (!empty($channelInfo)) {
+                        $item['agent_uid'] = $channelInfo->agent_uid;
+                    }
+                }
+            }
+            $service->login($arrJson);
+        }
+
+        return new SuccessCommonResponse();
+    }
+
+    /**
+     * 用户信息修改
+     * @param UserUpRequest $request
+     * @param UserService $service
+     * @return SuccessCommonResponse
+     */
+    public function up(UserUpRequest $request, UserService $service)
+    {
+        $arrJson = $this->getJsonData();
+        if (!empty($arrJson)) {
+            foreach ($arrJson as $item) {
+                UsersModel::query()->where(['uid' => $item['uid'], 'user_id' => $item['user_id'], 'device_no' => $item['device_no']])->update(['mobile' => $item['mobile']]);
+            }
+        }
+
+        return new SuccessCommonResponse();
+    }
+}

+ 68 - 0
Modules/InnerApi/Middlewares/InnerApiAuthMiddleware.php

@@ -0,0 +1,68 @@
+<?php
+/**
+ * 内部API AUTH中间件.
+ */
+namespace Modules\InnerApi\Middlewares;
+
+use App\Exceptions\InnerApiException;
+use Closure;
+use Illuminate\Support\Str;
+
+class InnerApiAuthMiddleware
+{
+    /**
+     * 排除认证的路由.
+     * @var string[]
+     */
+    protected $except = [
+    ];
+
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        if (!$this->shouldPassThrough($request)) {
+            $xToken = $request->header('X-TOKEN', '');
+            $tokenSecret = env('INNERT_API_TOKEN_SECRET', Str::random(6));
+            if ($xToken !== $tokenSecret) {
+                throw new InnerApiException('认证错误', 403);
+            }
+        }
+        return $next($request);
+    }
+
+    /**
+     * 获取排除项.
+     * @return array
+     */
+    public function getExcept()
+    {
+        return $this->except;
+    }
+
+    /**
+     * Determine if the request has a URI that should pass through CSRF verification.
+     *
+     * @param  \Illuminate\Http\Request $request
+     * @return bool
+     */
+    protected function shouldPassThrough($request)
+    {
+        foreach ($this->getExcept() as $except) {
+            if ($except !== '/') {
+                $except = trim($except, '/');
+            }
+
+            if ($request->is($except)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 57 - 0
Modules/InnerApi/Requests/BaseRequest.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:25
+ */
+namespace Modules\InnerApi\Requests;
+
+use App\Exceptions\InnerApiException;
+use Illuminate\Foundation\Http\FormRequest;
+use \Illuminate\Contracts\Validation\Validator;
+
+class BaseRequest extends FormRequest
+{
+
+    /**
+     * Determine if the user is authorized to make this request.
+     *
+     * @return bool
+     */
+    public function authorize()
+    {
+        //默认放行auth校验
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        return [
+            //
+        ];
+    }
+
+    /**
+     * 重定义校验失败
+     * @param Validator $validator
+     * @throws InnerApiException
+     */
+    protected function failedValidation(Validator $validator)
+    {
+        throw new InnerApiException($validator->messages()->first(), 500);
+    }
+
+    /**
+     * 重定义认证失败
+     * @throws InnerApiException
+     */
+    protected function failedAuthorization()
+    {
+        throw new InnerApiException('认证错误', 403);
+    }
+}

+ 19 - 0
Modules/InnerApi/Requests/ChannelPackageNotifyRequest.php

@@ -0,0 +1,19 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-05-26 16:24
+ */
+
+namespace Modules\InnerApi\Requests;
+
+class ChannelPackageNotifyRequest extends BaseRequest
+{
+    public function rules()
+    {
+        return [
+            '*.channel_apk_url' => 'nullable',
+        ];
+    }
+}

+ 15 - 0
Modules/InnerApi/Requests/CommonAppDeploy.php

@@ -0,0 +1,15 @@
+<?php
+
+
+namespace Modules\InnerApi\Requests;
+
+
+class CommonAppDeploy extends BaseRequest
+{
+    public function rules()
+    {
+        return [
+            '*.channel_uid' => 'required',
+        ];
+    }
+}

+ 17 - 0
Modules/InnerApi/Requests/CommonGetAdListRequest.php

@@ -0,0 +1,17 @@
+<?php
+
+
+namespace Modules\InnerApi\Requests;
+
+
+class CommonGetAdListRequest extends BaseRequest
+{
+    public function rules()
+    {
+        return [
+            '*.channel_uid' => 'required',
+            '*.tag_str' => 'required',
+            '*.mom_app_uid' => 'required'
+        ];
+    }
+}

+ 35 - 0
Modules/InnerApi/Requests/PayIncRequest.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-07-10 16:43
+ */
+
+namespace Modules\InnerApi\Requests;
+
+class PayIncRequest extends BaseRequest
+{
+    public function rules()
+    {
+        return [
+            '*.created_at' => 'required|date',
+            '*.updated_at' => 'required|date',
+            '*.paid_at' => 'nullable|date',
+            '*.channel_uid' => 'nullable',
+            '*.user_uid' => 'nullable',
+            '*.invite_uid' => 'nullable',
+            '*.device_type' => 'required|in:A,I,H,P',
+            '*.order_sn' => 'required',
+            '*.order_amount' => 'required|numeric',
+            '*.real_amount' => 'required|numeric',
+            '*.transaction_no' => 'required',
+            '*.order_type' => 'required|integer',
+            '*.buy_gold_num' => 'required|integer',
+            '*.package_info' => 'required',
+            '*.pay_status' => 'required|integer',
+            '*.order_status' => 'required|integer',
+            '*.mom_app_uid' => 'required',
+        ];
+    }
+}

+ 18 - 0
Modules/InnerApi/Requests/PayUpStatusRequest.php

@@ -0,0 +1,18 @@
+<?php
+
+
+namespace Modules\InnerApi\Requests;
+
+
+class PayUpStatusRequest extends BaseRequest
+{
+    public function rules()
+    {
+        return [
+            '*.uid' => 'required',
+            '*.order_sn' => 'required',
+            '*.pay_status' => 'required|integer',
+            '*.order_status' => 'required|integer',
+        ];
+    }
+}

+ 30 - 0
Modules/InnerApi/Requests/UserIncRequest.php

@@ -0,0 +1,30 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-07-10 16:43
+ */
+
+namespace Modules\InnerApi\Requests;
+
+class UserIncRequest extends BaseRequest
+{
+    public function rules()
+    {
+        return [
+            '*.ip' => 'required|ip',
+            '*.uid' => 'required',
+            '*.user_uid' => 'required',
+            '*.created_at' => 'required',
+            '*.channel_uid' => 'nullable',
+            '*.app_version' => 'required',
+            '*.mobile' => 'nullable',
+            '*.device_type' => 'required|in:A,I,H,P',
+            '*.device_no' => 'required',
+            '*.invite_uid' => 'nullable',
+            '*.nick' => 'required',
+            '*.mom_app_uid' => 'required',
+        ];
+    }
+}

+ 25 - 0
Modules/InnerApi/Requests/UserLoginRequest.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-07-10 16:43
+ */
+
+namespace Modules\InnerApi\Requests;
+
+class UserLoginRequest extends BaseRequest
+{
+    public function rules()
+    {
+        return [
+            '*.user_uid' => 'required',
+            '*.created_at' => 'required',
+            '*.channel_uid' => 'nullable',
+            '*.app_version' => 'required',
+            '*.device_type' => 'required|in:A,I,H,P',
+            '*.device_no' => 'required',
+            '*.mom_app_uid' => 'required|string',
+        ];
+    }
+}

+ 18 - 0
Modules/InnerApi/Requests/UserUpRequest.php

@@ -0,0 +1,18 @@
+<?php
+
+
+namespace Modules\InnerApi\Requests;
+
+
+class UserUpRequest extends BaseRequest
+{
+    public function rules()
+    {
+        return [
+            '*.mobile' => 'nullable',
+            '*.uid' => 'required',
+            '*.user_id' => 'required',
+            '*.device_no' => 'required',
+        ];
+    }
+}

+ 31 - 0
Modules/InnerApi/Responses/SuccessAdsResponse.php

@@ -0,0 +1,31 @@
+<?php
+
+
+namespace Modules\InnerApi\Responses;
+
+
+use Illuminate\Contracts\Support\Responsable;
+
+class SuccessAdsResponse implements Responsable
+{
+    protected $data = null;
+
+    private $params = [];
+
+    /**
+     * 构造
+     * GetTokenResponse constructor.
+     * @param $data
+     * @param $params
+     */
+    public function __construct($data, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse($request)
+    {
+        return json_success($this->data);
+    }
+}

+ 37 - 0
Modules/InnerApi/Responses/SuccessCommonResponse.php

@@ -0,0 +1,37 @@
+<?php
+/**
+ * 通用成功返回(无ID主键).
+ *
+ * @author nj
+ * @date   2021-05-26 16:37
+ */
+
+namespace Modules\InnerApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class SuccessCommonResponse implements Responsable
+{
+    /**
+     * 构造
+     * GetTokenResponse constructor.
+     */
+    public function __construct()
+    {
+    }
+
+    /**
+     * 响应结果
+     *
+     * @param \Illuminate\Http\Request $request
+     * @return array
+     */
+    public function toResponse($request)
+    {
+        $result = [
+            'success',
+        ];
+
+        return json_success($result);
+    }
+}

+ 45 - 0
Modules/InnerApi/Responses/SuccessIdResponse.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:57
+ */
+namespace Modules\InnerApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class SuccessIdResponse implements Responsable
+{
+    protected $data = null;
+
+    private $params = [];
+
+    /**
+     * 构造
+     * GetTokenResponse constructor.
+     * @param $data
+     * @param $params
+     */
+    public function __construct($data, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    /**
+     * 响应结果
+     *
+     * @param \Illuminate\Http\Request $request
+     * @return array
+     */
+    public function toResponse($request)
+    {
+        $result = [];
+        $data = is_object($this->data) ? $this->data->toArray() : $this->data;
+        if (!empty($data['id'])) {
+            $result['id'] = $data['id'];
+        }
+
+        return json_success($result);
+    }
+}

+ 179 - 0
Modules/InnerApi/Services/ChannelPackageService.php

@@ -0,0 +1,179 @@
+<?php
+/**
+ * 渠道包服务层.
+ *
+ * @author nj
+ * @date   2021-05-26 16:26
+ */
+
+namespace Modules\InnerApi\Services;
+
+use Hashids\Hashids;
+use Modules\Admin\Models\ChannelPackageModel;
+
+class ChannelPackageService
+{
+    /**
+     * 更新渠道包的下载链接(Todo mock).
+     * @param $items
+     * @return array
+     */
+    public function updateUrls($items)
+    {
+        $result = [
+            'total' => 0,
+            'success_ids' => [],
+            'fail_ids' => [],
+        ];
+
+        if (!empty($items)) {
+            foreach ($items as $item) {
+                //打包失败情况
+                $model = new ChannelPackageModel();
+                $channelConfigurationModel = $model::all()
+                    ->where('is_private_update', '=', $item['is_private_update'])
+                    ->where('channel_uid', '=', $item['channel_id'])
+                    ->where('user_uid', '=', $item['user_id'])
+                    ->where('app_version', '=', $item['app_version'])
+                    ->first();
+                if ($channelConfigurationModel) {
+                    //失败情况
+                    if ($item['code'] !== 200) {
+                        $data['path'] = " ";
+                        $data['status'] = 2;
+                        $res = $channelConfigurationModel->update($data);
+                        if ($res) {
+                            $result['success_ids']++;
+                        } else {
+                            $result['fail_ids']++;
+                        }
+                        return $result;
+                    }
+                    //存在在覆盖更新包
+                    $data = [];
+                    if ($item['mom_platform'] === 'I') {
+                        //'I'为苹果包
+                        $data['type'] = 2;
+                    } else {
+                        //'A'为安卓包
+                        $data['type'] = 1;
+                    }
+                    $data['path'] = $item['channel_apk_url'];
+                    $data ['status'] = 1;
+                    $res = $channelConfigurationModel->update($data);
+                    if ($res) {
+                        $result['success_ids']++;
+                    } else {
+                        $result['fail_ids']++;
+                    }
+                }
+            }
+        }
+        return $result;
+    }
+
+    /**
+     * 根据渠道一键打包,渠道分私有控制版本和使用全局母包版本2种情况(Todo mock).
+     * Note: 这个方法是给代理系统后台调用的服务方法.
+     * @param $channelUid
+     * @return array|false
+     */
+    public function packByChannelId($channelUid)
+    {
+        $res = [];
+        $channelModel = new \stdClass();
+        $channelColumns = [
+            'id',
+            'user_uid',
+            'name',
+            'uid',
+            'mom_app_uid',
+            'is_private_update'
+        ];
+        $channelObj = $channelModel->where(
+            'uid',
+            $channelUid
+        )->first($channelColumns);
+        if (empty($channelObj)) {
+            return false;
+        }
+        $momAppUid = $channelObj->mom_app_uid;  // 母包应用唯一自定义ID,非自增ID.
+        $momModel = new \stdClass();
+        $momColumns = ['id', 'uid', 'name'];
+        $momObj = $momModel->where(
+            'uid',
+            $channelObj->mom_app_uid
+        )->first($momColumns);
+        if (empty($momObj)) {
+            return false;
+        }
+        $recentLimit = 10;  // 取最近的n个.
+        $versionColumns = [
+            'id',
+            'app_uid',
+            'platform',
+            'version',
+            'apk_url',
+            'ipk_url',
+            'icon_url',
+            'office_url'
+        ];
+        if (boolval($channelObj->is_private_update) === true) {
+            $channelPrivateModel = new \stdClass();
+            $items = $channelPrivateModel->where(
+                'channel_uid',
+                $channelUid
+            )->orderBy(
+                'version',
+                'DESC'
+            )->take($recentLimit)->get($versionColumns);
+            if (!empty($items)) {
+                foreach ($items as $item) {
+                    $res[] = [
+                        'id' => $item->id,
+                        'is_private_update' => 1,
+                        'user_uid' => $channelObj->uid,
+                        'mom_app_uid' => $channelObj->mom_app_uid,
+                        'mom_app_name' => $momObj->name,
+                        'channel_id' => $channelUid,
+                        'channel_name' => $channelObj->name,
+                        'mom_platform' => $item->platform,
+                        'mom_app_version' => $item->version,
+                        'mom_apk_url' => $item->apk_url,
+                        'mom_ipa_url' => $item->ipa_url,
+                        'app_icon_url' => $item->icon_url,
+                    ];
+                }
+            }
+        } else {
+            $momAppVersionModel = new \stdClass();
+            $items = $momAppVersionModel->where(
+                'app_uid',
+                $momAppUid
+            )->orderBy(
+                'version',
+                'DESC'
+            )->take($recentLimit)->get($versionColumns);
+            if (!empty($items)) {
+                foreach ($items as $item) {
+                    $res[] = [
+                        'id' => $item->id,
+                        'is_private_update' => 0,
+                        'user_uid' => $channelObj->uid,
+                        'mom_app_uid' => $channelObj->mom_app_uid,
+                        'mom_app_name' => $momObj->name,
+                        'channel_id' => $channelUid,
+                        'channel_name' => $channelObj->name,
+                        'mom_platform' => $item->platform,
+                        'mom_app_version' => $item->version,
+                        'mom_apk_url' => $item->apk_url,
+                        'mom_ipa_url' => $item->ipa_url,
+                        'app_icon_url' => $item->icon_url,
+                    ];
+                }
+            }
+        }
+
+        return $res;
+    }
+}

+ 47 - 0
Modules/InnerApi/Services/CommonService.php

@@ -0,0 +1,47 @@
+<?php
+
+
+namespace Modules\InnerApi\Services;
+
+
+use App\Constants\AdsConstant;
+use Modules\Admin\Models\AdminUserModel;
+use Modules\Admin\Models\AdsModel;
+
+class CommonService
+{
+    /**
+     * 渠道广告列表
+     * @param array $item
+     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
+     */
+    public function adList(array $item)
+    {
+        $tagStr = explode(',', $item['tag_str']);
+        //获取广告
+        $date = date('Y-m-d H:i:s');
+        $adList = AdsModel::query()
+            ->select([
+                'title',
+                'link_url',
+                'image_url',
+                'jump_type',
+                'day_status',
+                'button_content',
+                'ag_ads_content',
+                'id',
+            ])
+            ->whereIn('alias', $tagStr)
+            ->where([
+                'channel_uid' => $item['channel_uid'],
+                'mom_app_uid' => $item['mom_app_uid'],
+                'status' => AdsConstant::STATUS_ON,
+            ])->where('start_at', '<=', $date)->where('end_at', '>', $date)->orderBy('squad', 'desc')->get();
+
+        foreach ($adList as &$ad) {
+            $ad['image_url'] = getOss() . $ad['image_url'];
+        }
+
+        return $adList;
+    }
+}

+ 44 - 0
Modules/InnerApi/Services/DeductionService.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Modules\InnerApi\Services;
+
+
+use App\Constants\RedisKeysConstant;
+use App\Models\Mysql\AgentModel;
+use App\Models\Mysql\ConfigModel;
+use App\Models\Mysql\UsersRulesModel;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\Cache;
+
+class DeductionService
+{
+    /**
+     * 缓存时间
+     * @var int
+     */
+    private $cacheTiem = 300;
+
+    /**
+     * 获取扣量配置
+     * @param $agentUserId
+     * @return mixed
+     * @directions
+     *  思路:1.如果渠道app是代理开出来的,那么就需要再查推广的规则配置
+     *       2.如果渠道app是推广开出来的,那就只有一级直接把规则返回
+     */
+    public function rulesInfo($agentUserId)
+    {
+        $res = Cache::remember(
+            RedisKeysConstant::CANNEL_USER_RULES.$agentUserId,
+            $this->cacheTiem,
+            function () {
+                $data = ConfigModel::where(['sgroup' => 'sys', 'skey' => 'global_deduction_rules'])->first();
+                $arrData = !empty($data->svalue) ? json_decode($data->svalue, true) : [];
+                return ['deductionRule' => $arrData];
+            }
+        );
+
+        return $res;
+    }
+}

+ 12 - 0
Modules/InnerApi/Services/DemoService.php

@@ -0,0 +1,12 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:59
+ */
+namespace Modules\InnerApi\Services;
+
+class DemoService
+{
+
+}

+ 223 - 0
Modules/InnerApi/Services/PayService.php

@@ -0,0 +1,223 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-07-10 16:47
+ */
+
+namespace Modules\InnerApi\Services;
+
+use App\Constants\RedisKeysConstant;
+use App\Events\InnerUserOrderIncEvent;
+use App\Models\Mongo\PayLogsModel;
+use App\Models\Mongo\UsersModel;
+use App\Repositories\AgentRepository;
+use App\Repositories\AppDeductionRuleRepository;
+use App\Repositories\ConfigRepoistory;
+use App\Repositories\PayLogRepository;
+use Carbon\Carbon;
+use Illuminate\Support\Facades\Redis;
+use LuckyBox\Card\IdCard;
+use LuckyBox\LuckyBox;
+
+class PayService
+{
+    /**
+     * 增量写入或者修改支付订单.
+     * @param array $data
+     * @return mixed
+     */
+    public function inc(array $data)
+    {
+        $eventParams = [];
+        foreach ($data as $item) {
+            $item['created_at'] = Carbon::parse($item['created_at']);
+            $item['updated_at'] = !empty($item['updated_at']) ? Carbon::parse($item['updated_at'])
+                : Carbon::now();
+            $item['paid_at'] = !empty($item['paid_at']) ? Carbon::parse($item['paid_at']) : null;
+            $item['invite_uid'] = !empty($item['invite_uid']) ? $item['invite_uid'] : '';
+            $item['order_amount'] = floatval(number_format($item['order_amount'] ?? 0, 2));
+            $item['real_amount'] = floatval(number_format($item['real_amount'] ?? 0, 2));
+            $item['pay_status'] = isset($item['pay_status']) ? intval($item['pay_status']) : 3;
+            $item['order_status'] = isset($item['order_status']) ? intval($item['order_status']) : 3;
+            $item['buy_type'] = isset($item['buy_type']) ? intval($item['buy_type']) : 0;
+            $item['buy_gold_num'] = isset($item['buy_gold_num']) ? intval($item['buy_gold_num']) : 0;
+            $item['user_uid'] = !empty($item['user_uid']) ? $item['user_uid']: '';
+            if (!empty($item['invite_uid'])) {
+                if ($item['invite_uid'] == $item['user_uid']) {
+                    // 不能等于自己.
+                    $item['invite_uid'] = '';
+                }
+            }
+            $oldLogObj = null;
+            $isOldLog = false;
+            $logObj = PayLogsModel::where(['mom_app_uid' => $item['mom_app_uid']])->where(['order_sn' => $item['order_sn']])->first();
+            if (!empty($logObj)) {
+                // 存在.
+                $isOldLog = true;
+                $oldLogObj = clone $logObj; // 克隆老订单.
+
+                $logObj->updated_at = $item['updated_at'];
+                $logObj->paid_at = $item['paid_at'];
+                $logObj->invite_uid = $item['invite_uid'];
+                $logObj->transaction_no = $item['transaction_no'];
+                $logObj->pay_status = $item['pay_status'];
+                $logObj->order_status = $item['order_status'];
+                $logObj->is_deduction = $item['is_deduction'];
+                $logObj->save();
+            } else {
+                // 不存在,新建.
+                $logObj = PayLogsModel::create($item);
+            }
+            if (!empty($logObj->_id)) {
+                $eventParams[] = [
+                    'log_id' => $logObj->_id,
+                    'old_log' => [
+                        'pay_status' => $isOldLog ? $oldLogObj->pay_status : 0,
+                        'order_status' => $isOldLog ? $oldLogObj->order_status : 0,
+                    ]
+                ];
+            }
+        }
+
+        // 触发事件.
+        event(new InnerUserOrderIncEvent($eventParams));
+
+        return true;
+    }
+
+    /**
+     * 随机扣量标记.
+     * @param array $item
+     * @return array
+     */
+    public function oneRandDeduction(array $item)
+    {
+
+        //订单成功才参与扣量
+        if ($item['pay_status'] != 1 && $item['order_status'] != 1) {
+            return $item;
+        }
+
+        $deductionConfig = app(ConfigRepoistory::class)->pickByKeys(['preferential_order', 'user_deduction_switch'], 'sys');
+        // 判断总扣规则是否开启
+        if ($deductionConfig['user_deduction_switch']['svalue'] == 0) {
+            return $item;
+        }
+        $iTodayPayOrder = PayLogsModel::query()->where([
+            'channel_uid' => $item['channel_uid'],
+            'pay_status' => 1,
+            'order_status' => 1,
+        ])->count();
+        //每日前多少单不扣
+        if ($iTodayPayOrder < $deductionConfig['preferential_order']['svalue']) {
+            return $item;
+        }
+
+        //代理扣量验证
+        $agentInfo = app(AgentRepository::class)->findBy('uid', $item['agent_uid'], ['deduction_switch', 'jdata', 'preferential_order']);
+
+        if ($agentInfo->deduction_switch == 1) {
+            if ($iTodayPayOrder >= $agentInfo->preferential_order) {
+                $aData = json_decode($agentInfo->jdata, true);
+                $item['is_deduction'] = $this->isDeduction($item, $aData);
+            }
+        } else if ($agentInfo->deduction_switch == 0) {
+            // 总扣量规则
+            $ruleData = app(AppDeductionRuleRepository::class)->findWhere(['app_uid' => $item['mom_app_uid'], 'deduction_type' => 'cps', 'deduction_rule' => 2])->keyBy('deduction_method');
+            $userSuccessOrder = PayLogsModel::query()->where(['user_uid' => $item['user_uid'], 'pay_status' => 1, 'order_status' => 1])->count();
+            if ($userSuccessOrder > 0) {
+                if (isset($ruleData[2])) {
+                    $aData = json_decode($ruleData[2]->jdata, true);
+                    $item['is_deduction'] = $this->isDeduction($item, $aData, 1);
+                }
+            } else {
+                //首充扣量
+                if (isset($ruleData[1])) {
+                    $aData = json_decode($ruleData[1]->jdata, true);
+                    $item['is_deduction'] = $this->isDeduction($item, $aData, 2);
+                }
+            }
+        } else {
+            $item['is_deduction'] = 0;
+        }
+        return $item;
+    }
+
+    /**
+     * 判断是否扣量
+     *
+     * @param array $item
+     * @param array $aData
+     * @param string $method
+     * @return int
+     */
+    private function isDeduction(array $item,array $aData, $method = '')
+    {
+
+        $isDeduction = 0;
+        $scope = [];
+        $deduction = [];
+        $deductionPrice = $aData['deduction_price'];
+        $deductionNumber = $aData['deduction_number'];
+        foreach ($deductionPrice as $key => $val) {
+            if ($item['real_amount'] >= $val['min_price'] && $item['real_amount'] <= $val['max_price']) {
+                $scope = $val;
+                $deduction = $deductionNumber[$key];
+            }
+        }
+
+        //如果没有符合的金额规则,就返回
+        if (empty($scope) || $deduction['deduction'] == 0) {
+            return $isDeduction;
+        }
+
+        $hashKey = RedisKeysConstant::DEDUCTION_RULES_HASH . $item['mom_app_uid'] . $item['channel_uid'] . $method;
+        //获取扣量标记
+        $areaKey = $scope['min_price'] . '-' . $scope['max_price'];
+        $deductionMark = Redis::hget($hashKey, $areaKey);
+
+        $mark = [0,0];
+        if ($deductionMark) {
+            $mark = explode('-', $deductionMark);
+            //如果一轮规则达到 且扣量完成 则删除并初始化标记
+            if ($mark[0] == $deduction['number'] && $mark[1] == $deduction['deduction']) {
+                Redis::hDel($hashKey, $areaKey);
+                $mark = [0,0];
+            }
+            //如果扣量达到,但是一轮还没结束则直接返回
+            if ($mark[1] == $deduction['deduction']) {
+                // $mark[0] + 1  参与订单总数加1
+                Redis::hset($hashKey, $areaKey, $mark[0] + 1 . '-' . $mark[1]);
+                return $isDeduction;
+            }
+        }
+
+        //必扣计算
+        if (($deduction['number'] - $mark[0]) <= ($deduction['deduction'] - $mark[1])) {
+            $isDeduction = 1;
+        } else {
+            $boxItems = [
+                1 => ['name' => 0, 'rate' => 80],
+                2 => ['name' => 1, 'rate' => 20]
+            ];
+            $luckyBox = new LuckyBox();
+            foreach ($boxItems as $id => $items) {
+                $card = new IdCard();
+                $card->setId($id)->setRate($items['rate']);
+                $luckyBox->add($card);
+            }
+            $boxValue = $boxItems[$luckyBox->draw()->getId()];
+            $isDeduction = $boxValue['name'];
+        }
+
+        if ($isDeduction == 1) {
+            $mark[1] += 1;
+        }
+
+        $mark[0] += 1;
+        Redis::hset($hashKey, $areaKey, $mark[0] . '-' . $mark[1]);
+        return $isDeduction;
+    }
+}

+ 191 - 0
Modules/InnerApi/Services/UserService.php

@@ -0,0 +1,191 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-07-10 16:47
+ */
+
+namespace Modules\InnerApi\Services;
+
+use App\Constants\RedisKeysConstant;
+use App\Events\InnerUserNewIncEvent;
+use App\Jobs\UserHyperLogLogJob;
+use App\Models\Mongo\UsersModel;
+use App\Repositories\AgentRepository;
+use App\Repositories\AppDeductionRuleRepository;
+use App\Repositories\ConfigRepoistory;
+use Carbon\Carbon;
+use Illuminate\Support\Facades\Redis;
+use Zhuzhichao\IpLocationZh\Ip;
+
+class UserService
+{
+    /**
+     * 缓存时间
+     * @var int
+     */
+    private $cacheTime = 300;
+
+    /**
+     * 增量写入新用户.
+     * @param array $data
+     * @return mixed
+     */
+    public function inc(array $data)
+    {
+        $eventParams = [];
+        foreach ($data as $item) {
+            $item['created_at'] = Carbon::parse($item['created_at']);
+            $item['invite_uid'] = !empty($item['invite_uid']) ? $item['invite_uid'] : '';
+            if ($item['invite_uid'] == $item['user_uid']) {
+                // 不能等于自己.
+                $item['invite_uid'] = '';
+            }
+            $ipInfo = Ip::find($item['ip']);
+            $item['ip_address'] = $ipInfo[0] . ' ' . $ipInfo[1] . ' ' . $ipInfo[2];
+            $logObj = UsersModel::create($item);
+            if (!empty($logObj->_id)) {
+                $eventParams[] = [
+                    'log_id' => $logObj->_id,
+                ];
+            }
+        }
+
+        // 新用户注册时,也算一次登录.
+        self::login($data);
+
+        // 触发事件.
+        event(new InnerUserNewIncEvent($eventParams));
+
+        return true;
+    }
+
+    /**
+     * 登录
+     * @param array $data
+     * @return bool
+     */
+    public function login(array $data)
+    {
+        // 丢job构造函数时,必须精简数据.
+        $jobData = [];
+        if (!empty($data)) {
+            foreach ($data as $item) {
+                $jobData[] = [
+                    'agent_uid' => $item['agent_uid'],
+                    'channel_uid' => $item['channel_uid'],
+                    'created_at' => $item['created_at'],
+                    'device_type' => $item['device_type'],
+                    'mom_app_uid' => $item['mom_app_uid'],
+                    'user_uid' => $item['user_uid'],
+                ];
+            }
+        }
+        if (!empty($jobData)) {
+            dispatch(new UserHyperLogLogJob($jobData));
+        }
+
+        return true;
+    }
+
+    /**
+     * 随机扣量标记. cpa扣量
+     * @param array $item
+     * @return array
+     */
+    public function oneRandDeduction(array $item)
+    {
+        $item['is_deduction'] = 0;
+        //获取代理扣量配置
+        $deductionConfig = app(ConfigRepoistory::class)->pickByKeys(['preferential_user', 'cpa_deduction_switch'], 'sys');
+        if ($deductionConfig['cpa_deduction_switch']['svalue'] == 0) {
+            return $item;
+        }
+
+        $iTodayRegUser = UsersModel::where('channel_uid', $item['channel_uid'])->count();
+        //每日前几单不扣
+        if ($iTodayRegUser < $deductionConfig['preferential_user']['svalue']) {
+            return $item;
+        }
+
+        $agentInfo = app(AgentRepository::class)->findBy('uid', $item['agent_uid'], ['deduction_switch', 'cpa_deduction_rule']);
+        if ($agentInfo->deduction_switch == 2 && !empty($agentInfo->cpa_deduction_rule)) {
+            $item['is_deduction'] = $this->isDeduction($item, $agentInfo->cpa_deduction_rule);
+        } else if ($agentInfo->deduction_switch == 0 || $agentInfo->deduction_switch == 3) {
+            //获取扣量规则
+            $rulesInfo = app(AppDeductionRuleRepository::class)->findByWhere(['app_uid' => $item['mom_app_uid'], 'deduction_type' => 'cpa'], ['deduction_pro']);
+            if (empty($rulesInfo)) {
+                return $item;
+            }
+
+            if ($agentInfo->deduction_switch == 3) {
+                $method = 3;
+            } else {
+                $method = 1;
+            }
+            //超过每日设定的默认单就开始扣量
+            $item['is_deduction'] = $this->isDeduction($item, $rulesInfo->deduction_pro, $method);
+        } else {
+            $item['is_deduction'] = 0;
+        }
+        return $item;
+    }
+
+    /**
+     * 扣量
+     *
+     * @param $item
+     * @param $deductionPro
+     * @param $method
+     * @return int|mixed
+     */
+    private function isDeduction($item, $deductionPro, $method = '')
+    {
+        $isDeduction = 0;
+        //设置扣量参数
+        $ratio = $deductionPro;
+        $ratio = explode('-',$ratio);
+
+        if ($ratio[1] == 0) {
+            return $isDeduction;
+        }
+
+        $hashKey = RedisKeysConstant::DEDUCTION_RULES_HASH_CPA . $item['mom_app_uid'] . $item['channel_uid'] . $method;
+        //扣量标记
+        $areaKey = $item['channel_uid'];
+        $deductionMark = Redis::hget($hashKey, $areaKey);
+        $mark = [0,0];
+        if ($deductionMark) {
+            $mark = explode('-', $deductionMark);
+
+            if ($mark[0] == $ratio[0] && $mark[1] == $ratio[1]) {
+                Redis::hDel($hashKey, $areaKey);
+                $mark = [0,0];
+            }
+
+            //如果扣量达到,但是一轮还没结束则直接返回
+            if ($mark[1] == $ratio[1]) {
+                // $mark[0] + 1  参与订单总数加1
+                Redis::hset($hashKey, $areaKey, $mark[0] + 1 . '-' . $mark[1]);
+                return $isDeduction;
+            }
+        }
+
+        //必扣计算
+        if (($ratio[0] - $mark[0]) <= ($ratio[1] - $mark[1])) {
+            $isDeduction = 1;
+        } else {
+            $isDeduction = mt_rand(0, 1);
+        }
+
+        if ($isDeduction == 1) {
+            $mark[1] += 1;
+        }
+
+        $mark[0] += 1;
+        Redis::hset($hashKey, $areaKey, $mark[0] . '-' . $mark[1]);
+
+        return $isDeduction;
+    }
+}

+ 70 - 0
Modules/ManageApi/Commands/PermissionResourceCommand.php

@@ -0,0 +1,70 @@
+<?php
+declare(strict_types=1);
+
+namespace Modules\ManageApi\Commands;
+
+use Illuminate\Console\Command;
+use App\Repositories\Utils\AccessControllerListsRepository;
+
+/**
+ * @Command
+ */
+class PermissionResourceCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'permission:resource';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '更新后台权限资源表';
+
+    private $aclRepo = null;
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $authConfig = config('manage_acl');
+        $this->getAclRepo()->getController(
+            $authConfig,
+            base_path(). '/Modules/ManageApi/Controllers',
+            'Controller',
+            'Modules\ManageApi\Controllers'
+        );
+        $this->line('更新权限资源完毕,可以去表[permission_resource]查看确认', 'info');
+    }
+
+    /**
+     * 获取ACL仓库
+     * @return AccessControllerListsRepository|null
+     */
+    private function getAclRepo()
+    {
+        if (empty($this->aclRepo)) {
+            $this->aclRepo = make(AccessControllerListsRepository::class);
+        }
+
+        return $this->aclRepo;
+    }
+
+}

+ 537 - 0
Modules/ManageApi/Controllers/AgentController.php

@@ -0,0 +1,537 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\AgentConstant;
+use App\Constants\CommonConstant;
+use App\Jobs\AppPackJob;
+use App\Models\Mongo\PayLogsModel;
+use App\Models\Mongo\UsersModel;
+use App\Models\Mysql\AgentAttrsModel;
+use App\Models\Mysql\AgentModel;
+use App\Models\Mysql\PackModel;
+use App\Models\Mysql\WithDrawalLogModel;
+use App\Repositories\AgentBankInfoRepository;
+use App\Repositories\AgentRepository;
+use App\Repositories\AppRepository;
+use App\Repositories\ChannelRepository;
+use App\Repositories\ConfigRepoistory;
+use App\Repositories\Criterias\AgentPagesCriteria;
+use App\Repositories\Criterias\ChannelPagesCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Repositories\ShareUrlsRepository;
+use Carbon\Carbon;
+use Grpc\Channel;
+use Illuminate\Support\Facades\Validator;
+use Modules\ManageApi\Responses\AgentPagesResponse;
+use Modules\ManageApi\Responses\CommonAllResponse;
+use Modules\ManageApi\Responses\CommonSuccessUidResponse;
+use Modules\ManageApi\Responses\NotIncrementIdDetailResponse;
+
+class AgentController extends BaseController
+{
+    public function lists(AgentRepository $agenRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'deduction_switch' => 'nullable|integer',
+            'parent_uid' => 'nullable',
+            'uid' => 'nullable',
+            'username' => 'nullable',
+            'nick' => 'nullable',
+            'end_date' => 'nullable|date',
+            'start_date' => 'nullable|date'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $inputData['today_date'] = date('Y-m-d');
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+        $orders = [['id', 'desc']];
+        $res = $agenRepo->pushCriteria(new AgentPagesCriteria($inputData))
+            ->pushCriteria(new OrderLimitCriteria($orders))
+            ->paginate($pageSize, ['*'], $page);
+
+        if (!empty($inputData['start_date']) && !empty($inputData['end_date'])) {
+            $startDate = toMongoIsoDate(Carbon::parse($inputData['start_date'])->startOfDay()->toDateTimeString());
+            $endDate = toMongoIsoDate(Carbon::parse($inputData['end_date'])->endOfDay()->toDateTimeString());
+        }
+
+        $withdrawalModel = new WithDrawalLogModel();
+        foreach ($res->items() as &$val) {
+            // 今日充值
+            $val->td_order_success_money = $val->attrsDay->td_order_success_money ?? 0;
+            // 今日扣量金额
+            $val->td_deduction_money = $val->attrsDay->td_deduction_success_money ?? 0;
+            // 今日注册
+            $val->td_users_total = $val->attrsDay->td_users_total ?? 0;
+            // 今日注册扣量
+            $val->td_users_deduction = $val->attrsDay->td_users_deduction ?? 0;
+
+            // 时间范围搜索数据
+            if (empty($inputData['start_date'])) {
+                $val->all_users_total = 0;
+                $val->add_users_deduction = 0;
+                $val->add_deduction_success_money = 0;
+                $val->all_success_money = 0;
+            } else {
+                $val->all_users_total = UsersModel::where( 'agent_uid', $val->uid)->whereBetween('created_at', [$startDate, $endDate])->count();
+                $val->add_users_deduction = UsersModel::where(['is_deduction' => 1, 'agent_uid' => $val->uid])->whereBetween('created_at', [$startDate, $endDate])->count();
+                $val->all_success_money = PayLogsModel::where( ['agent_uid' => $val->uid, 'pay_status' => 1])->whereBetween('created_at', [$startDate, $endDate])->sum('real_amount');
+                $val->add_deduction_success_money =  PayLogsModel::where( ['agent_uid' => $val->uid, 'pay_status' => 1, 'is_deduction' => 1])->whereBetween('created_at', [$startDate, $endDate])->sum('real_amount');
+            }
+
+            // 今日提现
+            $val->td_withdraw = $withdrawalModel->where([
+                'status' => 3,
+                'agent_uid' => $val['uid']
+            ])->whereBetween('paid_at', [
+                Carbon::parse()->startOfDay()->toDateTimeString(),
+                Carbon::parse()->endOfDay()->toDateTimeString()
+            ])->sum('amount');
+            // 总充值
+            $val->order_success_money = $val->attrs->order_success_money ?? 0;
+            // 总充值扣量
+            $val->deduction_success_money = $val->attrs->deduction_success_money ?? 0;
+//            // 总注册
+//            $val->users_total = UsersModel::where('agent_uid', $val->uid)->groupBy('user_uid')->count();
+//            // 总注册扣量
+//            $val->users_deduction = UsersModel::where(['agent_uid' => $val->uid, 'is_deduction' => 1])->groupBy('user_uid')->count();
+            // 总注册
+            $val->users_total = UsersModel::where('agent_uid', $val->uid)->groupBy('user_uid')->count();
+            // 总注册扣量
+            $val->users_deduction = UsersModel::where(['agent_uid' => $val->uid, 'is_deduction' => 1])->groupBy('user_uid')->count();
+            // 累计提现
+            $val->withdraw_money = $withdrawalModel->where(['status' => 3, 'agent_uid' => $val['uid']])->sum('amount');
+            // cpa支出金额
+            $val->cps_money = ($val->users_total - $val->users_deduction) * $val->cpa_price;
+
+            unset($val->attrs, $val->attrsDay, $val->everyDay);
+        }
+        return new AgentPagesResponse($res);
+    }
+
+    /**
+     * 用户详情
+     */
+    public function detail()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $agentRepo = app(AgentRepository::class);
+        $data = $agentRepo->findBy('uid', $inputData['uid']);
+        $jdata = json_decode($data['jdata'], true);
+        $data['deduction_price'] = $jdata['deduction_price'] ?? 0;
+        $data['deduction_number'] = $jdata['deduction_number'] ?? 0;
+
+        //获取代理提现银行卡
+        $bankRepo = app(AgentBankInfoRepository::class);
+        $data['bank_info'] = $bankRepo->findWhere(['agent_uid' => $inputData['uid']]);
+
+        return new NotIncrementIdDetailResponse($data);
+    }
+
+    /**
+     * 创建一个代理.
+     * @param Validator $validatorFactory
+     * @param AgentRepository $objRepo
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function create(Validator $validatorFactory, AgentRepository $objRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'parent_uid' => 'nullable|max:16',
+            'username' => 'unique:agent,username|required|max:16',
+            'nick' => 'nullable|max:64',
+            'status' => 'in:1,2',
+            'password' => 'required|min:6|max:32',
+            'otp_screct' => 'nullable|max:32',
+            'own_app_uids' => 'required|string',
+            'withdraw_pwd' => 'nullable|max:64',
+            'commission_ratio' => 'required|integer|between:0,100',
+            'deduction_switch' => 'in:0,1,2,3',
+            'deduction_price' => 'nullable|string',
+            'deduction_number' => 'nullable|string',
+            'withdraw_amount' => 'nullable|integer',
+            'withdraw_usdt' => 'nullable|integer'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        if ($inputData['commission_ratio'] < AgentConstant::MIN_COMMISSION_RATIO || $inputData['commission_ratio'] > AgentConstant::MAX_COMMISSION_RATIO) {
+            return json_fail('提成设置错误');
+        }
+
+        if (empty($inputData['withdraw_pwd'])) {
+            //如果为空就取默认值
+            $appRepo = app(ConfigRepoistory::class);
+            $withdrawPwd = $appRepo->get('withdraw_pwd', 'sys');
+            $inputData['withdraw_pwd'] = $withdrawPwd;
+        }
+
+        if (empty($inputData['withdraw_pwd'])) {
+            //如果为空就取默认值
+            $appRepo = app(ConfigRepoistory::class);
+            $withdrawPwd = $appRepo->get('withdraw_pwd', 'sys');
+            $inputData['withdraw_pwd'] = $withdrawPwd;
+        }
+
+        if (empty($inputData['withdraw_amount'])) {
+            $inputData['withdraw_amount'] = 0;
+        }
+
+        if (empty($inputData['withdraw_usdt'])) {
+            $inputData['withdraw_usdt'] = 0;
+        }
+
+        $item = [
+            'username' => $inputData['username'],
+            'nick' => $inputData['nick'] ?? '',
+            'password' => $inputData['password'],
+            'status' => $inputData['status'],
+            'otp_screct' => $inputData['otp_screct'] ?? '',
+            'parent_uid' => $inputData['parent_uid'] ?? '',
+            'withdraw_pwd' => $inputData['withdraw_pwd'],
+            'own_app_uids' => $inputData['own_app_uids'],
+            'commission_ratio' => $inputData['commission_ratio'],
+            'deduction_switch' => $inputData['deduction_switch'],
+            'preferential_order' => $inputData['preferential_order'] ?: 0,
+            'jdata' => $inputData['jdata'],
+            'cpa_deduction_rule' => $inputData['cpa_deduction_rule'],
+            'cpa_price' => $inputData['cpa_price'] ?: 0,
+            'withdraw_amount' => $inputData['withdraw_amount'],
+            'withdraw_usdt' => $inputData['withdraw_usdt'],
+        ];
+        $agentObj = $objRepo->createOne($item);
+        $respData = [
+            'uid' => $agentObj->uid,
+        ];
+
+        //创建渠道
+        $chanRepo = app(ChannelRepository::class);
+        $chanRepo->createAgentChannel($agentObj->uid);
+
+        return new CommonSuccessUidResponse($respData);
+    }
+
+    public function checkUsername()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'username' => 'unique:agent,username|required|max:16',
+        ];
+        $message = [
+            'username.unique' => '账号已存在!',
+            'username.required' => '用户名不能为空!',
+            'username.max' => '用户名不能大于16字节!'
+        ];
+        $validator = Validator::make($inputData, $rules, $message);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        return json_fail('用户名可用!', 200, []);
+    }
+
+    /**
+     * 修改代理.
+     * @param Validator $validatorFactory
+     * @param AgentRepository $objRepo
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \App\Exceptions\ApiException
+     */
+    public function modify(Validator $validatorFactory, AgentRepository $objRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required',
+            'nick' => 'required|max:64',
+            'password' => 'nullable|min:6|max:32',
+            'withdraw_pwd' => 'nullable|max:64',
+            'own_app_uids' => 'required|string',
+            'status' => 'in:1,2',
+            'commission_ratio' => 'required|integer|between:0,100',
+            'deduction_switch' => 'in:0,1,2,3',
+            'deduction_price' => 'nullable|string',
+            'deduction_number' => 'nullable|string',
+            'withdraw_amount' => 'nullable|integer',
+            'withdraw_usdt' => 'nullable|integer'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        if ($inputData['commission_ratio'] < AgentConstant::MIN_COMMISSION_RATIO || $inputData['commission_ratio'] > AgentConstant::MAX_COMMISSION_RATIO) {
+            return json_fail('提成设置错误');
+        }
+        if (empty($inputData['withdraw_amount'])) {
+            $inputData['withdraw_amount'] = 0;
+        }
+        if (empty($inputData['withdraw_usdt'])) {
+            $inputData['withdraw_usdt'] = 0;
+        }
+        $item = [
+            'nick' => $inputData['nick'] ?? '',
+            'status' => $inputData['status'],
+            'password' => $inputData['password'] ?? null,
+            'withdraw_pwd' => $inputData['withdraw_pwd'] ?? null,
+            'parent_uid' => $inputData['parent_uid'] ?? '',
+            'own_app_uids' => $inputData['own_app_uids'],
+            'commission_ratio' => $inputData['commission_ratio'],
+            'deduction_switch' => $inputData['deduction_switch'],
+            'preferential_order' => $inputData['preferential_order'] ?: 0,
+            'cpa_deduction_rule' => $inputData['cpa_deduction_rule'],
+            'cpa_price' => $inputData['cpa_price'],
+            'withdraw_amount' => $inputData['withdraw_amount'],
+            'withdraw_usdt' => $inputData['withdraw_usdt'],
+            'jdata' => $inputData['jdata']
+        ];
+
+        $saveResult = $objRepo->updateOne($item, $inputData['uid']);
+        if (!$saveResult) {
+            return json_fail('修改失败');
+        }
+
+        $objRepo->delAgentRuleCache($inputData['uid']);
+        return new CommonSuccessUidResponse($inputData['uid']);
+    }
+
+
+    public function editStatus(Validator $validatorFactory, AgentRepository $objRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $user = $objRepo->findBy('uid', $inputData['uid']);
+        if (empty($user)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldUser = clone $user;
+        $user->status = $user->status === 1 ? 2 : 1;
+        $result = $user->save();
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '编辑一个代理状态, UID:' . $user->uid;
+            $logSvalue = $user->uid;
+            $logDbData = [
+                'after_items' => [
+                    $user->toArray(),
+                ],
+                'before_items' => [
+                    $oldUser->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($user->uid);
+        }
+    }
+
+    /**
+     * 修改代理提现账户
+     *
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function editAgentBank()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'type' => 'required|integer',
+            'agent_uid' => 'required|string',
+            'bank_card' => 'required|string',
+            'bank_account' => 'required|string',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        /**
+         * @var AgentBankInfoRepository
+         */
+        $agentBankRepo = app(AgentBankInfoRepository::class);
+
+        $bankInfo = $agentBankRepo->findBy('agent_uid', $inputData['agent_uid']);
+        if (empty($bankInfo)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldbankInfo = clone $bankInfo;
+
+        if (!empty($inputData['type'])) {
+            $bankInfo->type = trim($inputData['type']);
+        }
+
+        if (!empty($inputData['bank_card'])) {
+            $bankInfo->bank_card = trim($inputData['bank_card']);
+        }
+
+        if (!empty($inputData['bank_account'])) {
+            $bankInfo->bank_account = trim($inputData['bank_account']);
+        }
+
+        $result = $bankInfo->save();
+
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '修改一个代理的提现方式,开户方式:' . $bankInfo->type;
+            $logSvalue = $bankInfo->uid;
+            $logDbData = [
+                'after_items' => [
+                    $bankInfo->toArray(),
+                ],
+                'before_items' => [
+                    $oldbankInfo->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($bankInfo->uid);
+        }
+    }
+
+    public function channelList()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'nullable|string',
+            'agent_uid' => 'nullable|string',
+            'mom_app_uid' => 'nullable|string',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orders = [
+            ['id', 'DESC'],
+        ];
+        // 注意,单例分页一定要克隆一个(保证前面没用使用到paginate);
+        $page = request()->has('page') ? request()->input('page', 1) : null;
+        $pageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        /**
+         * @var ChannelRepository $videoRepo
+         */
+        $channelRepo = app(ChannelRepository::class);
+        $res = $channelRepo->pushCriteria(new ChannelPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))
+            ->paginate($pageSize,
+                ['*'], $page);
+
+        $shareUrlData = app(ShareUrlsRepository::class)->findWhere(['status' => 1], ['mom_app_uid', 'url'])->toArray();
+        $shareUrlData = array_column($shareUrlData, null, 'mom_app_uid');
+
+        foreach ($res->items() as &$val) {
+            if (isset($shareUrlData[$val->mom_app_uid])) {
+                $val->share_url = $shareUrlData[$val->mom_app_uid]['url'] . '?channel_code=' . $val->uid;
+            } else {
+                $val->share_url = '';
+            }
+        }
+
+        return new CommonAllResponse($res);
+    }
+
+    /**
+     * 打包
+     * @return \Illuminate\Http\JsonResponse|void
+     */
+    public function channelPack()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        // 查找渠道开始打包
+        /**
+         * 获取渠道app
+         * @var ChannelRepository $channRepo
+         */
+        $channelRepo = app(ChannelRepository::class);
+        $channelData = $channelRepo->getAppChannel($inputData['uid']);
+        $packArray =[
+            'agent_uid'=>$channelData->agent_uid,
+            'channel_code'=>$channelData->uid,
+            'mom_app_name'=>$channelData->app->name,
+            'mom_app_uid'=>$channelData->app->uid,
+            'mom_app_id'=>$channelData->app->app_id,
+            'android_or_ios'=>1,
+            'mom_app_download'=>$channelData->app->android_url,
+            'mom_app_version'=>$channelData->app->android_version,
+        ];
+
+        $packCreateData = $packArray;
+        // 数据库格式
+        unset($packCreateData['mom_app_download']);
+        $packCreateData['app_download'] = '';
+        $packCreateData['status'] = 3;// 打包中
+        // 写入数据库
+        $packModel = (new PackModel())->where('channel_code',$inputData['uid'])->count();
+        if ($packModel){
+            if (!PackModel::query()->where('channel_code',$inputData['uid'])->update($packCreateData)){
+                return json_success([],'打包失败请重试');
+            }
+        }else{
+            if (!PackModel::query()->create($packCreateData)){
+                return json_success([],'打包失败请重试');
+            }
+        }
+        // 打包
+        dispatch(new AppPackJob($packArray))->onQueue('app_pack');
+        return json_success([],'开始打包,稍等1-2分钟');
+    }
+
+}

+ 316 - 0
Modules/ManageApi/Controllers/AnnounceController.php

@@ -0,0 +1,316 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Repositories\AnnounceRepository;
+use App\Repositories\Criterias\AnnouncePagesCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use Illuminate\Support\Facades\Validator;
+use Modules\ManageApi\Responses\AnnouncePagesResponse;
+use Modules\ManageApi\Responses\CommonSuccessUidResponse;
+use Modules\ManageApi\Responses\NotIncrementIdDetailResponse;
+
+class AnnounceController extends BaseController
+{
+    /**
+     * 公告列表
+     * @return \Illuminate\Http\JsonResponse|AnnouncePagesResponse
+     */
+    public function lists(Validator $validatorFactory, AnnounceRepository $repo)
+    {
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'type' => 'nullable|integer',
+            'title' => 'nullable',
+            'uid' => 'nullable',
+        ];
+
+        $validator = Validator::make($inputData,$rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $items = $repo->pushCriteria(new AnnouncePagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+
+        return new AnnouncePagesResponse($items);
+    }
+
+    /**
+     * 应用详情
+     * @return \Illuminate\Http\JsonResponse|NotIncrementIdDetailResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function detail(Validator $validatorFactory, AnnounceRepository $repo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string',
+        ];
+        $validator = Validator::make($inputData,$rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $video = $repo->findBy('uid', $inputData['uid']);
+
+        return new NotIncrementIdDetailResponse($video);
+    }
+
+    /**
+     * 添加应用
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function create(Validator $validatorFactory, AnnounceRepository $repo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'title' => 'required|max:255',
+            'type' => 'required|integer|max:6',
+            'content' => 'required|string',
+            'status' => 'nullable|integer',
+        ];
+        $validator = Validator::make($inputData,$rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $setarr = [
+            'title' => $inputData['title'],
+            'content' => $inputData['content'],
+            'type' => $inputData['type'],
+            'status' => $inputData['status'] ?? 1,
+        ];
+
+        $result = $repo->create($setarr);
+        if (!$result) {
+            return json_fail('创建失败');
+        } else {
+            $logSubject = '添加一个公告, 名称:' . $result->title;
+            $logSvalue = $result->uid;
+            $logDbData = [
+                'after_items' => [
+                    $result->toArray(),
+                ],
+                'before_items' => [
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($result->uid);
+        }
+    }
+
+    public function editStatus(Validator $validatorFactory, AnnounceRepository $repo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string'
+        ];
+        $validator = Validator::make($inputData,$rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $repo->findBy('uid', $inputData['uid']);
+        if (empty($app)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldApp = clone $app;
+        $app->status = $app->status === 1 ? 2 : 1;
+        $result = $app->save();
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '编辑一个公告, 名称:' . $app->title;
+            $logSvalue = $app->uid;
+            $logDbData = [
+                'after_items' => [
+                    $app->toArray(),
+                ],
+                'before_items' => [
+                    $oldApp->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($app->uid);
+        }
+    }
+
+
+    /**
+     * 编辑应用
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function modify(Validator $validatorFactory, AnnounceRepository $repo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'title' => 'required|max:255',
+            'type' => 'required|integer|max:6',
+            'content' => 'required|string',
+            'status' => 'nullable|integer',
+        ];
+        $validator = Validator::make($inputData,$rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $item = $repo->findBy('uid', $inputData['uid']);
+        if (empty($item)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldItem = clone $item;
+
+        if (isset($inputData['title'])) {
+            $item->title = trim($inputData['title']);
+        }
+        if (isset($inputData['content'])) {
+            $item->content = rtrim($inputData['content']);
+        }
+        if (isset($inputData['type'])) {
+            $item->type = intval($inputData['type']);
+        }
+        $item->status = $inputData['status'];
+        $result = $item->save();
+
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '编辑一个公告, 名称:' . $item->title;
+            $logSvalue = $item->uid;
+            $logDbData = [
+                'after_items' => [
+                    $item->toArray(),
+                ],
+                'before_items' => [
+                    $oldItem->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($item->uid);
+        }
+    }
+
+    /**
+     * 应用删除
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function delete(Validator $validatorFactory, AnnounceRepository $repo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required',
+        ];
+        $validator = Validator::make($inputData,$rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $item = $repo->findBy('uid', $inputData['uid']);
+        if (empty($item)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+
+        $oldItem = clone $item;
+        $result = $item->delete();
+
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个应用,名称:' . $item->title;
+            $logSvalue = $item->uid;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldItem->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($item->uid);
+        }
+    }
+
+    /**
+     * 批量操作应用
+     * @return \Illuminate\Http\JsonResponse|mixed
+     */
+    public function batch(Validator $validatorFactory, AnnounceRepository $repo)
+    {
+        $inputData = $this->getJsonData();
+        $operateName = $inputData['operate_name'] ?? '';
+
+        switch ($operateName) {
+            case 'delete':
+                $itemPks = $inputData['item_pks'] ?? [];
+                if (!empty($itemPks)) {
+                    $resultItems = $repo->makeModel()->query()->whereIn('uid', $itemPks)->delete();
+                    if ($resultItems) {
+                        $strPks = implode(',', $itemPks);
+                        $logSubject = '批量删除多个公告';
+                        $logSvalue = $strPks;
+                        $logDbData = [
+                            'after_items' => [
+                            ],
+                            'before_items' => [
+                            ],
+                        ];
+                        $this->log($logSubject, $logSvalue, $logDbData);
+                    }
+                }
+
+                $data = [
+                    'result' => 'success',
+                    'pks' => $itemPks,
+                ];
+                return json_success($data);
+                break;
+            default:
+                break;
+        }
+
+        return json_fail('未知操作');
+    }
+}

+ 552 - 0
Modules/ManageApi/Controllers/AppController.php

@@ -0,0 +1,552 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Jobs\InitAppPackJob;
+use App\Models\Mysql\AppModel;
+use App\Repositories\AgentRepository;
+use App\Repositories\AppRepository;
+use App\Repositories\ChannelRepository;
+use App\Repositories\Criterias\AppPagesCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use Illuminate\Support\Facades\Validator;
+use Modules\ManageApi\Responses\AppPagesResponse;
+use Modules\ManageApi\Responses\CommonAllResponse;
+use Modules\ManageApi\Responses\CommonDetailResponse;
+use Modules\ManageApi\Responses\CommonSuccessUidResponse;
+use Modules\ManageApi\Responses\NotIncrementIdDetailResponse;
+
+class AppController extends BaseController
+{
+    /**
+     * 应用列表
+     * @return \Illuminate\Http\JsonResponse|AppPagesResponse
+     */
+    public function lists(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'name' => 'nullable',
+            'uid' => 'nullable',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $items = $appRepo->pushCriteria(new AppPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize,
+            ['*'], $page);
+
+        return new AppPagesResponse($items);
+    }
+
+    /**
+     * 应用详情
+     * @return \Illuminate\Http\JsonResponse|NotIncrementIdDetailResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function detail(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $appRepo->findBy('uid', $inputData['uid']);
+
+        return new NotIncrementIdDetailResponse($app);
+    }
+
+    /**
+     * 添加应用
+     * @return \Illuminate\Http\JsonResponse|mixed
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function create(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'name' => 'required|string|max:120',
+            'android_url' => 'nullable|string|max:700',
+            'ios_url' => 'nullable|string|max:700',
+            'android_version' => 'required|max:16',
+            'app_id' => 'required',
+            'ios_version' => 'required|max:700',
+            'android_enable' => 'required|integer|max:1',
+            'ios_enable' => 'required|integer|max:1',
+            'icon_url' => 'nullable|string|max:700',
+            'status' => 'nullable|integer',
+            'squad' => 'nullable|integer'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $setarr = [
+            'name' => $inputData['name'],
+            'android_url' => $inputData['android_url'],
+            'ios_url' => $inputData['ios_url'],
+            'android_version' => $inputData['android_version'],
+            'ios_version' => $inputData['ios_version'],
+            'android_enable' => $inputData['android_enable'],
+            'ios_enable' => $inputData['ios_enable'],
+            'icon_url' => $inputData['icon_url'],
+            'status' => $inputData['status'] ?? 1,
+            'squad' => $inputData['squad'] ?? 1000,
+            'app_id' => $inputData['app_id'],
+        ];
+
+        $result = $appRepo->create($setarr);
+
+        if (!$result) {
+            return json_fail('创建失败');
+        } else {
+            // 母包初始化打包 //目前简单判断
+            if ($result->android_url){
+                $initMomPack = [
+                    'mom_app_name' => $result->name,
+                    'mom_app_uid' => $result->uid,
+                    'mom_app_id' => $result->app_id,
+                    'android_or_ios' => 1,
+                    'mom_app_download' => $result->android_url,
+                    'mom_app_version' => $result->android_version,
+                ];
+                $this->dispatch((new InitAppPackJob($initMomPack))->onQueue('app_pack'));
+            }
+
+            $logSubject = '添加一个App应用, 名称:' . $result->name;
+            $logSvalue = $result->uid;
+            $logDbData = [
+                'after_items' => [
+                    $result->toArray(),
+                ],
+                'before_items' => [
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            //创建渠道
+            /**
+             * @var AgentRepository $agentRepo
+             */
+            $agentRepo = app(AgentRepository::class);
+            $agentData = $agentRepo->all(['uid']);
+
+            /**
+             * @var ChannelRepository $chanRepo
+             */
+            $chanRepo = app(ChannelRepository::class);
+
+            $setData = [];
+            foreach ($agentData->toArray() as $key => $item) {
+                $setData['name'] = $result->uid . '_' . $key;
+                $setData['agent_uid'] = $item['uid'];
+                $setData['mom_app_uid'] = $result->uid;
+                $chanRepo->create($setData);
+            }
+
+            return new CommonSuccessUidResponse($result->uid);
+        }
+    }
+
+    /**
+     * 编辑应用
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function modify(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string',
+            'name' => 'required|string|max:120',
+            'android_url' => 'nullable|string|max:700',
+            'ios_url' => 'nullable|string|max:700',
+            'android_version' => 'required|max:16',
+            'ios_version' => 'required|max:700',
+            'android_enable' => 'required|integer|max:1',
+            'ios_enable' => 'required|integer|max:1',
+            'icon_url' => 'nullable|string|max:700',
+            'status' => 'nullable|integer',
+            'squad' => 'nullable|integer',
+            'app_id' => 'required',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $appRepo->findBy('uid', $inputData['uid']);
+        if (empty($app)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldApp = clone $app;
+
+        if (isset($inputData['name'])) {
+            $app->name = trim($inputData['name']);
+        }
+
+        if (isset($inputData['android_url'])) {
+            $app->android_url = trim($inputData['android_url']);
+        }
+
+        if (isset($inputData['ios_url'])) {
+            $app->ios_url = trim($inputData['ios_url']);
+        }
+
+        if (isset($inputData['android_version'])) {
+            $app->android_version = trim($inputData['android_version']);
+        }
+        if (isset($inputData['ios_version'])) {
+            $app->ios_version = trim($inputData['ios_version']);
+        }
+        if (isset($inputData['android_enable'])) {
+            $app->android_enable = trim($inputData['android_enable']);
+        }
+        if (isset($inputData['ios_enable'])) {
+            $app->ios_enable = trim($inputData['ios_enable']);
+        }
+        if (isset($inputData['icon_url'])) {
+            $app->icon_url = trim($inputData['icon_url']);
+        }
+        if (isset($inputData['status'])) {
+            $app->status = trim($inputData['status']);
+        }
+        if (isset($inputData['app_id'])) {
+            $app->app_id = trim($inputData['app_id']);
+        }
+        if (isset($inputData['squad'])) {
+            $app->squad = trim($inputData['squad']);
+        }
+
+        $result = $app->save();
+
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            // 母包初始化打包 //目前简单判断
+            if ($app->android_url){
+                $initMomPack = [
+                    'mom_app_name' => $app->name,
+                    'mom_app_uid' => $app->uid,
+                    'mom_app_id' => $app->app_id,
+                    'android_or_ios' => 1,
+                    'mom_app_download' => $app->android_url,
+                    'mom_app_version' => $app->android_version,
+                ];
+                $this->dispatch((new InitAppPackJob($initMomPack))->onQueue('app_pack'));
+            }
+            $logSubject = '编辑一个App应用, 名称:' . $app->name;
+            $logSvalue = $app->uid;
+            $logDbData = [
+                'after_items' => [
+                    $app->toArray(),
+                ],
+                'before_items' => [
+                    $oldApp->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($app->uid);
+        }
+    }
+
+    /**
+     * 应用删除
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function delete(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $appRepo->findBy('uid', $inputData['uid']);
+        if (empty($app)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+
+        $oldApp = clone $app;
+        $result = $app->delete();
+
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个应用,名称:' . $app->name;
+            $logSvalue = $app->uid;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldApp->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($app->uid);
+        }
+    }
+
+    /**
+     * 批量修改
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function batchDelete(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uids' => 'required',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $appUidToArray = explode(',', $inputData['uids']);
+        $appUIds = $appRepo->findArray('uid', $appUidToArray);
+        if (empty($appUIds)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $appUidToStr = implode(',', $appUIds);
+        if (!$appRepo->delectUids('uid', $appUIds)) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '批量删除应用UID:' . $appUidToStr;
+            $logSvalue = $appUidToStr;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $appUIds,
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($appUidToStr);
+        }
+    }
+
+    public function editStatus(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $appRepo->findBy('uid', $inputData['uid']);
+        if (empty($app)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldApp = clone $app;
+        $app->status = $app->status === 1 ? 2 : 1;
+        $result = $app->save();
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '编辑一个App应用, 名称:' . $app->name;
+            $logSvalue = $app->uid;
+            $logDbData = [
+                'after_items' => [
+                    $app->toArray(),
+                ],
+                'before_items' => [
+                    $oldApp->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($app->uid);
+        }
+    }
+
+    public function editAndroidEnable(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $appRepo->findBy('uid', $inputData['uid']);
+        if (empty($app)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldApp = clone $app;
+        $app->android_enable = $app->android_enable === 1 ? 0 : 1;
+        $result = $app->save();
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '编辑一个App应用android_enable状态, 名称:' . $app->name;
+            $logSvalue = $app->uid;
+            $logDbData = [
+                'after_items' => [
+                    $app->toArray(),
+                ],
+                'before_items' => [
+                    $oldApp->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($app->uid);
+        }
+    }
+
+    public function editIosEnable(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $appRepo->findBy('uid', $inputData['uid']);
+        if (empty($app)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldApp = clone $app;
+        $app->ios_enable = $app->ios_enable === 1 ? 0 : 1;
+        $result = $app->save();
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '编辑一个App应用ios_enable状态, 名称:' . $app->name;
+            $logSvalue = $app->uid;
+            $logDbData = [
+                'after_items' => [
+                    $app->toArray(),
+                ],
+                'before_items' => [
+                    $oldApp->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($app->uid);
+        }
+    }
+
+    /**
+     * 批量操作应用
+     * @return \Illuminate\Http\JsonResponse|mixed
+     */
+    public function batch(Validator $validatorFactory, AppRepository $appRepo)
+    {
+        $inputData = $this->getJsonData();
+        $operateName = $inputData['operate_name'] ?? '';
+
+        switch ($operateName) {
+            case 'delete':
+                $itemPks = $inputData['item_pks'] ?? [];
+                if (!empty($itemPks)) {
+                    $model = new AppModel();
+                    $resultItems = $model->whereIn('uid', $itemPks)->delete();
+                    if ($resultItems) {
+                        $strPks = implode(',', $itemPks);
+                        $logSubject = '批量删除多个app应用';
+                        $logSvalue = $strPks;
+                        $logDbData = [
+                            'after_items' => [
+                            ],
+                            'before_items' => [
+                            ],
+                        ];
+                        $this->log($logSubject, $logSvalue, $logDbData);
+                    }
+                }
+
+                $data = [
+                    'result' => 'success',
+                    'pks' => $itemPks,
+                ];
+                return json_success($data);
+                break;
+            default:
+                break;
+        }
+
+        return json_fail('未知操作');
+    }
+
+    /**
+     * 所用应用
+     * @param AppRepository $appRepo
+     * @return CommonAllResponse
+     */
+    public function all(AppRepository $appRepo)
+    {
+        $orders = [
+            ['name', 'ASC'],
+        ];
+
+        $items = $appRepo->pushCriteria(new OrderLimitCriteria($orders))->all();
+
+        return new CommonAllResponse($items);
+    }
+}

+ 289 - 0
Modules/ManageApi/Controllers/AppDeductionRuleController.php

@@ -0,0 +1,289 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Repositories\AppDeductionRuleRepository;
+use App\Repositories\Criterias\AppDeductionRuleCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use Illuminate\Support\Facades\Validator;
+use Modules\ManageApi\Responses\AppDeductionRuleResponse;
+use Modules\ManageApi\Responses\CommonSuccessUidResponse;
+use Modules\ManageApi\Responses\ConfigSuccessResponse;
+
+class AppDeductionRuleController extends BaseController
+{
+    /**
+     * 扣量规则列表
+     * @return mixed
+     */
+    public function lists()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var AppDeductionRuleRepository $configRepository
+         */
+        $DedRepo = make(AppDeductionRuleRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'deduction_type' => 'required|string',
+            'app_uid' => 'nullable|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+
+        if (!isset($inputData['page_size']) || $inputData['page_size'] > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        } else {
+            $pageSize = $inputData['page_size'];
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $res = $DedRepo->pushCriteria(new AppDeductionRuleCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+
+        return new AppDeductionRuleResponse($res);
+    }
+
+    /**
+     * 新增扣量规则
+     * @param Validator $validatorFactory
+     * @return \Illuminate\Http\JsonResponse|ConfigSuccessResponse
+     */
+    public function create()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'deduction_type' => 'required|string',
+            'deduction_rule' => 'nullable|integer',
+            'deduction_method' => 'nullable|integer',
+            'app_uid' => 'required|string',
+            'deduction_price' => 'nullable|string',
+            'deduction_pro' => 'nullable|string',
+            'jdata' => 'nullable|string',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        /**
+         * @var AppDeductionRuleRepository $configRepository
+         */
+        $DedRepo = make(AppDeductionRuleRepository::class);
+
+        $setarr = [
+            'deduction_type' => $inputData['deduction_type'],
+            'deduction_rule' => $inputData['deduction_rule'] ?? 0,
+            'app_uid' => $inputData['app_uid'],
+            'deduction_price' => $inputData['deduction_price'] ?? '',
+            'deduction_method' => $inputData['deduction_method'] ?? '',
+            'deduction_pro' => $inputData['deduction_pro'],
+            'jdata' => $inputData['jdata'],
+        ];
+
+        $result = $DedRepo->create($setarr);
+        if (!$result) {
+            return json_fail('新增失败');
+        } else {
+            $logSubject = '新增一个扣量规则,:' . $result->uid;
+            $logSvalue = $result->uid;
+            $logDbData = [
+                'after_items' => [
+                    $setarr,
+                ],
+                'before_items' => [
+
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+            return new ConfigSuccessResponse();
+        }
+    }
+
+    /**
+     * 修改规则
+     *
+     * @return \Illuminate\Http\JsonResponse|ConfigSuccessResponse
+     */
+    public function modify()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'deduction_rule' => 'nullable|integer',
+            'app_uid' => 'required|string',
+            'uid' => 'required|string',
+            'deduction_price' => 'nullable|string',
+            'deduction_pro' => 'nullable|string',
+            'deduction_method' => 'nullable|integer',
+            'jdata' => 'nullable|string',
+        ];
+
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        /**
+         * @var AppDeductionRuleRepository $managerRepo
+         */
+        $DedRepo = app(AppDeductionRuleRepository::class);
+
+        $deqInfo = $DedRepo->findBy('uid', $inputData['uid']);
+        if (empty($deqInfo)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+
+        $olddeqInfo = clone $deqInfo;
+
+        if (isset($inputData['deduction_type'])) {
+            $deqInfo->deduction_type = $inputData['deduction_type'];
+        }
+
+        if (isset($inputData['deduction_rule'])) {
+            $deqInfo->deduction_rule = $inputData['deduction_rule'];
+        }
+
+        if (isset($inputData['app_uid'])) {
+            $deqInfo->app_uid = $inputData['app_uid'];
+        }
+
+        if (isset($inputData['deduction_price'])) {
+            $deqInfo->deduction_price = $inputData['deduction_price'];
+        }
+
+        if (isset($inputData['deduction_pro'])) {
+            $deqInfo->deduction_pro = $inputData['deduction_pro'];
+        }
+
+        if (isset($inputData['deduction_method'])) {
+            $deqInfo->deduction_method = $inputData['deduction_method'];
+        }
+
+        if (isset($inputData['jdata'])) {
+            $deqInfo->jdata = $inputData['jdata'];
+        }
+
+        $result = $deqInfo->save();
+
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '修改一个扣量规则, uid是:' . $deqInfo->uid;
+            $logSvalue = $deqInfo->uid;
+            $logDbData = [
+                'after_items' => [
+                    $deqInfo->toArray(),
+                ],
+                'before_items' => [
+                    $olddeqInfo->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+            //删除缓存
+            $DedRepo->delRuleCache($deqInfo->deduction_type);
+            return new ConfigSuccessResponse();
+        }
+    }
+
+    public function delete()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        /**
+         * @var AppDeductionRuleRepository $managerRepo
+         */
+        $DedRepo = app(AppDeductionRuleRepository::class);
+
+        $deqInfo = $DedRepo->findBy('uid', $inputData['uid']);
+
+        $olddeqInfo = clone $deqInfo;
+        $result = $deqInfo->delete();
+
+        if (!$result) {
+            return json_fail('删除规则失败');
+        } else {
+            $logSubject = '删除一个扣量规则, uid是:' . $deqInfo->uid;
+            $logSvalue = $deqInfo->uid;
+            $logDbData = [
+                'after_items' => [
+                    $deqInfo->toArray(),
+                ],
+                'before_items' => [
+                    $olddeqInfo->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+            //删除缓存
+            $DedRepo->delRuleCache($deqInfo->deduction_type);
+            return new CommonSuccessUidResponse($deqInfo->uid);
+        }
+    }
+
+    /**
+     * 规则详情
+     *
+     * @param Validator $validatorFactory
+     * @return \Illuminate\Http\JsonResponse|AppDeductionRuleResponse
+     */
+    public function detail(Validator $validatorFactory)
+    {
+        $inputData = $this->getJsonData();
+
+        /**
+         * @var AppDeductionRuleRepository $managerRepo
+         */
+        $DedRepo = app(AppDeductionRuleRepository::class);
+
+        $rules = [
+            'uid' => 'required|string',
+        ];
+        $validator = Validator::make($inputData,$rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $res = $DedRepo->findBy('uid', $inputData['uid']);
+
+        return new AppDeductionRuleResponse($res);
+    }
+}

+ 280 - 0
Modules/ManageApi/Controllers/AuthController.php

@@ -0,0 +1,280 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-05-18 20:38:13
+ */
+
+
+namespace Modules\ManageApi\Controllers;
+
+
+use App\Repositories\ManagerRepository;
+use App\Repositories\Utils\JwtRepository;
+use Modules\ManageApi\Services\ManageAuthService;
+use Modules\ManageApi\Controllers\BaseController;
+use Validator;
+use App\Repositories\ManagerLogRepository;
+use Lcobucci\JWT\Token;
+use Otp\Otp;
+use Otp\GoogleAuthenticator;
+use ParagonIE\ConstantTime\Encoding;
+
+class AuthController extends BaseController
+{
+    /**
+     * Jwt受众.
+     *
+     * @var string
+     */
+    private $jwtAud = 'manage-users';
+
+    /**
+     * 属性描述.
+     *
+     * @var ManagerLogRepository
+     */
+    protected $logRepo = null;
+
+    public function login()
+    {
+        $inputData = $this->getJsonData();
+
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var ManagerRepository $managerRepo
+         */
+        $managerRepo = app(ManagerRepository::class);
+
+        /**
+         * @var ManageAuthService $authService
+         */
+        $authService = app(ManageAuthService::class);
+
+        $rules = [
+            'account' => 'required|max:120',
+            'password' => 'required|max:64',
+            'verify' => 'nullable|max:12',
+            'ggcode' => 'nullable|max:10',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        // 查询字段.
+        $columns = [
+            'id', 'manager_uid', 'username', 'nick', 'role_id', 'password', 'salt', 'locked_at', 'otp_secret',
+        ];
+        // 条件.
+        $conditions = ['username' => $inputData['account']];
+        $managers = $managerRepo->findWhere($conditions, $columns);
+        // 先判断是否存在用户.
+        if ($managers->count() == 0) {
+            $logSubject = '后台登陆获取token失败;原因:无此账号;入参:account['. $inputData['account'] .'];密码:['. pass_encode($inputData['password']) .']';
+            $logSvalue = '';
+            $logDbData = [];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            // 给一个不容易分析的错误信息给前台.
+            return json_fail('账号或者密码错误.');
+        }
+        // 取第一个用户.
+        $manager = $managers->first();
+        $md5password = $managerRepo->generatePassword($inputData['password'], $manager->salt);
+        if ($manager->password !== $md5password) {
+            $logSubject = '后台登陆获取token失败;原因:密码错误;入参:account['. $inputData['account'] .'];密码:['. pass_encode($inputData['password']) .']';
+            $logSvalue = '';
+            $logDbData = [];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return json_fail('账号或者密码错误.');
+        }
+        $ggcode = isset($inputData['ggcode']) ? $inputData['ggcode'] : '';
+        $isOpenOtp =  boolval(env('OTP_OPEN', true)); //是否开启谷歌身份码验证功能
+        if ($isOpenOtp) {
+            if (empty($ggcode)) {
+                return json_fail('请填写身份码.');
+            }
+            $otp = new Otp();
+            $otpSecret = !empty($manager->otp_secret) ? $manager->otp_secret : GoogleAuthenticator::generateRandom();
+            $otpCheckResult = $otp->checkTotp(Encoding::base32DecodeUpper($otpSecret), $ggcode);
+            if (!$otpCheckResult) {
+                $logSubject = '后台登陆获取token失败;原因:OTP错误;入参:account['. $inputData['account'] .'];密码:['. pass_encode($inputData['password']) .'];otp:['. pass_encode($ggcode) .']';
+                $logSvalue = '';
+                $logDbData = [];
+                $this->log($logSubject, $logSvalue, $logDbData);
+
+                return json_fail('身份码不正确');
+            }
+        }
+        if (!empty($manager->locked_at)) {
+            $logSubject = '后台登陆获取token失败;原因:被锁定;入参:account['. $inputData['account'] .'];密码:['. pass_encode($inputData['password']) .']';
+            $logSvalue = '';
+            $logDbData = [];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return json_fail('您已经被锁定,无法登陆.');
+        }
+        $logSubject = '后台登录成功';
+        $logSvalue = $manager->manager_uid;
+        list($module, $action) = $this->getModuleAndAction();
+        $inputs = [
+            'manager_uid' => $manager->manager_uid,
+            'username' => $manager->username,
+            'nick' => $manager->nick,
+            'subject' => mb_strcut($logSubject, 0, 700),
+            'module' => $module,
+            'action' => $action,
+            'svalue' => !empty($logSvalue) ? $logSvalue : '',
+            'db_data' => [],
+        ];
+
+        $this->getLogRepo()->createOne($inputs);
+
+        $tokenInfo = $authService->createSingleJwt($manager->manager_uid, $manager->role_id);
+
+        $data = [
+            'token' => $tokenInfo['token'],
+            'expired_at' => $tokenInfo['expired_at'],
+            'manager_uid' => $manager->manager_uid,
+        ];
+
+        return json_success($data);
+    }
+
+    public function logout()
+    {
+    }
+
+    /**
+     * 刷新Token并返回token.
+     * @param  mixed  $validatorFactory
+     * @param  JwtRepository  $jwtRepository
+     * @param  ManageAuthService  $authService
+     * @return mixed
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function refreshToken() {
+
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var ManagerRepository $managerRepo
+         */
+        $managerRepo = app(ManagerRepository::class);
+
+        /**
+         * @var ManageAuthService $authService
+         */
+        $authService = app(ManageAuthService::class);
+        /**
+         * @var JwtRepository $jwtRepository
+         */
+        $jwtRepository = app(JwtRepository::class);
+
+        $inputData = $this->getJsonData();
+        $rules = [
+            'manager_uid' => 'required',
+            'old_token' => 'required',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $validateResult = $jwtRepository->validateToken($inputData['old_token'], $this->jwtAud);
+        if ($validateResult['is_illegal']) {
+            return json_fail('无效数据传入.');
+        }
+        if (!$validateResult['is_valid']) {
+            return json_fail('旧Token无效.');
+        }
+        /*if (!$validateResult['is_expired']) {
+            // 旧token没过期
+            return json_fail('旧token怎么了');
+        }*/
+        /**
+         * @var Token $tokenObj
+         */
+        $tokenObj = $validateResult['token_obj'];
+        $claims = $tokenObj->claims();
+        $managerUid = $claims->get('manager_uid');
+        $roleId = $claims->get('role_id');
+        // 检查入参设备信息是否有效.
+        if ($managerUid != $inputData['manager_uid']) {
+            $logSubject = '后台登陆刷新token失败;原因:入参manager_uid和token中的manager_uid不一致;入参:manager_uid['. $inputData['manager_uid'] .']';
+            $logSvalue = '';
+            $logDbData = [];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return json_fail('无效数据匹配.');
+        }
+        $tokenInfo = $authService->createSingleJwt($managerUid, $roleId);
+
+        $data = [
+            'token' => $tokenInfo['token'],
+            'expired_at' => $tokenInfo['expired_at'],
+        ];
+
+        return json_success($data);
+    }
+
+    /**
+     * php ping
+     * @return string
+     */
+    public function ping()
+    {
+        return 'pong';
+    }
+
+    /**
+     * 记录操作日志.
+     * @param string $subject 主题.
+     * @param  string  $svalue 关联数据.
+     * @param  array  $dbData 此数组数据结构一定要是["after_items" => [0, 1, 2], "before_items" => [0, 1, 2]], after_items,before_items 是一个二维码数组.
+     * @return bool
+     */
+    protected function log(string $subject, $svalue = '', $dbData = [])
+    {
+        list($module, $action) = $this->getModuleAndAction();
+        $inputs = [
+            'manager_uid' => '',
+            'username' => '',
+            'nick' => '匿名',
+            'subject' => mb_strcut((string)$subject, 0, 700),
+            'module' => $module,
+            'action' => $action,
+            'svalue' => !empty($svalue) ? mb_strcut((string)$svalue, 0, 700) : '',
+            'db_data' => $dbData,
+        ];
+
+        $this->getLogRepo()->createOne($inputs);
+
+        return true;
+    }
+
+    /**
+     * 获取日志仓库.
+     * @return ManagerLogRepository|null
+     */
+    protected function getLogRepo()
+    {
+        if (empty($this->logRepo)) {
+            $this->logRepo = make(ManagerLogRepository::class);
+        }
+
+        return $this->logRepo;
+    }
+}

+ 142 - 0
Modules/ManageApi/Controllers/BaseController.php

@@ -0,0 +1,142 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:24
+ */
+namespace Modules\ManageApi\Controllers;
+
+use App\Models\Mysql\ManagerModel;
+use App\Repositories\ManagerLogRepository;
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Routing\Controller as RoutingController;
+use Modules\ManageApi\Services\ManageAuthService;
+use Route;
+
+class BaseController extends RoutingController
+{
+    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+
+    /**
+     * 当前管理员UID.
+     *
+     * @var string
+     */
+    protected $ctManagerUid = '';
+
+    /**
+     * 当前管理员信息.
+     *
+     * @var string
+     */
+    protected $ctManagerInfo = [];
+
+    /**
+     * 当前管理员角色.
+     *
+     * @var string
+     */
+    protected $ctRoleId = '';
+
+    /**
+     * 获取
+     * @return array|mixed
+     */
+    protected function getJsonData()
+    {
+        $arrJson = request()->all();
+        return $arrJson['data'] ?? [];
+    }
+
+    /**
+     * 获取日志需要的当前模块名和动作名.
+     * @return array
+     */
+    protected function getModuleAndAction()
+    {
+        $routeAction = $this->getCurrentRouteHandler();
+        $controller_suffix = 'Controller';
+        preg_match_all('/([a-z0-9A-Z]+\\\)?(\w+)@(\w+)/', $routeAction, $matchs);
+        $controller_full = !empty($matchs[2][0]) ? $matchs[2][0] : '';  //类名.
+        $controller = str_replace($controller_suffix, '', $controller_full);    // 控制器名.
+        $action = !empty($matchs[3][0]) ? $matchs[3][0] : '';    //方法名.
+
+        return [$controller, $action];
+    }
+
+    /**
+     * 获取当前路由话柄.
+     * @return string
+     */
+    protected function getCurrentRouteHandler()
+    {
+        return Route::currentRouteAction();
+    }
+
+    /**
+     * 获取当前管理员对象信息.
+     * @return mixed
+     */
+    protected function getCurrentManger()
+    {
+        $managerInfo = $this->getCurrentManagerInfo();
+
+        return ManagerModel::with(['role'])->where('manager_uid', $managerInfo['manager_uid'])->first();
+    }
+
+    /**
+     * 获取当前管理员信息.
+     * @return array
+     */
+    protected function getCurrentManagerInfo()
+    {
+        if (empty($this->ctManagerInfo)) {
+            $authService = new ManageAuthService();
+            $this->ctManagerInfo = $authService->getManagerInfo();
+            $this->ctManagerUid = $this->ctManagerInfo['manager_uid'];
+        }
+
+        return $this->ctManagerInfo;
+    }
+    /**
+     * 获取日志仓库.
+     * @return ManagerLogRepository
+     */
+    protected function getLogRepo()
+    {
+        return make(ManagerLogRepository::class);
+    }
+
+    /**
+     * 记录操作日志.
+     * @param string $subject 主题.
+     * @param  string  $svalue 关联数据.
+     * @param  array  $dbData 此数组数据结构一定要是["after_items" => [0, 1, 2], "before_items" => [0, 1, 2]], after_items,before_items 是一个二维码数组.
+     * @return bool
+     */
+    protected function log(string $subject, $svalue = '', $dbData = [])
+    {
+        $manager = $this->getCurrentManger();
+        list($module, $action) = $this->getModuleAndAction();
+        $inputs = [
+            'manager_uid' => !empty($manager->manager_uid) ? (string)$manager->manager_uid : '',
+            'username' => '',
+            'nick' => '',
+            'subject' => mb_strcut((string)$subject, 0, 700),
+            'module' => $module,
+            'action' => $action,
+            'svalue' => !empty($svalue) ? mb_strcut((string)$svalue, 0, 700) : '',
+            'db_data' => $dbData,
+        ];
+        if (!empty($manager)) {
+            $inputs['username'] = $manager->username;
+            $inputs['nick'] = $manager->nick;
+        }
+
+        $this->getLogRepo()->createOne($inputs);
+
+        return true;
+    }
+}

+ 348 - 0
Modules/ManageApi/Controllers/FinanceController.php

@@ -0,0 +1,348 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Models\Mongo\PayLogsModel;
+use App\Models\Mysql\WithDrawalLogModel;
+use App\Repositories\AgentBankInfoRepository;
+use App\Repositories\AgentRepository;
+use App\Repositories\BalanceLogRepository;
+use App\Repositories\Criterias\FinanceRechargeCriteria;
+use App\Repositories\Criterias\FinanceWithdrawCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Repositories\PayLogRepository;
+use App\Repositories\WithDrawalLogRepository;
+use Carbon\Carbon;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Validator;
+use Modules\ManageApi\Responses\CommonSuccessUidResponse;
+use Modules\ManageApi\Responses\ConfigSuccessResponse;
+use Modules\ManageApi\Responses\FinanceRechargePageResponse;
+use Modules\ManageApi\Responses\FinanceWithdrawResponse;
+
+class FinanceController extends BaseController
+{
+    /**
+     * 充值记录
+     *
+     * @return \Illuminate\Http\JsonResponse|FinanceRechargePageResponse
+     */
+    public function rechargeLists()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var PayLogRepository $payRepo
+         */
+        $payRepo = app(PayLogRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'user_uid' => 'nullable|string',
+            'user_name' => 'nullable|string',
+            'pay_type' => 'nullable|string',
+            'order_status' => 'nullable|integer',
+            'mom_app_uid' => 'nullable|string',
+            'order_sn' => 'nullable|string',
+            'start_date' => 'nullable|string',
+            'end_date' => 'nullable|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+
+        if (!isset($inputData['page_size']) || $inputData['page_size'] > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        } else {
+            $pageSize = $inputData['page_size'];
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $field = ['order_sn', 'user_uid', 'mom_app_uid', 'nick', 'agent_uid', 'real_amount', 'order_status', 'pay_channel', 'order_sn', 'created_at', 'paid_at', 'pay_type'];
+        $res = $payRepo->pushCriteria(new FinanceRechargeCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, $field, $page);
+
+        $recTotal = $this->rechargeTotal($inputData, $res);
+
+        return new FinanceRechargePageResponse($res, $recTotal);
+    }
+
+    /**
+     * 提现列表
+     *
+     * @return \Illuminate\Http\JsonResponse|FinanceWithdrawResponse
+     */
+    public function withdrawList()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var WithDrawalLogRepository $witRepo
+         */
+        $witRepo = app(WithDrawalLogRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'agent_username' => 'nullable|string',
+            'status' => 'nullable|integer',
+            'type' => 'nullable|integer',
+            'start_date' => 'nullable|string',
+            'end_date' => 'nullable|string',
+            'order' => 'nullable|string',
+
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        if (empty($inputData['order'])) {
+            $orders = [
+                ['id', 'DESC']
+            ];
+        } else {
+            $orderMap = explode('-', $inputData['order']);
+            $orders = [[$orderMap[0], $orderMap[1]]];
+        }
+
+        if (!isset($inputData['page_size']) || $inputData['page_size'] > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        } else {
+            $pageSize = $inputData['page_size'];
+        }
+        $page = format_page($inputData['page'] ?? 0);
+
+        $res = $witRepo->pushCriteria(new FinanceWithdrawCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+        $withdrawTotal = $this->withdrawTotal($inputData, $res);
+
+        return new FinanceWithdrawResponse($res, $withdrawTotal);
+    }
+
+    /**
+     * 提现操作
+     *
+     * @return \Illuminate\Http\JsonResponse|ConfigSuccessResponse
+     * @throws \Exception
+     */
+    public function applyWithdraw()
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'order_uid' => 'required|string',
+            'status' => 'required|integer|in:1,3'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        /**
+         * @var WithDrawalLogRepository $witRepo
+         */
+        $witRepo = app(WithDrawalLogRepository::class);
+
+        /**
+         * @var AgentRepository $witRepo
+         */
+        $agentRepo = app(AgentRepository::class);
+
+        /**
+         * @var BalanceLogRepository $bLogRepo
+         */
+        $bLogRepo = app(BalanceLogRepository::class);
+
+        $witOrder = $witRepo->getOrderFirst($inputData['order_uid']);
+        if (empty($witOrder)) {
+            return json_fail('订单状态异常');
+        }
+
+        $agent = $agentRepo->findBy('uid', $witOrder->agent_uid);
+        if (empty($agent)) {
+            return json_fail('代理账户不存在');
+        }
+
+        $oldAgent = clone $agent;
+
+        //开启事物
+        DB::beginTransaction();
+
+        if ($inputData['status'] == 1) { //拒绝
+            $agent->balance += $witOrder->amount;
+        }
+
+        $agent->frozen -= $witOrder->amount;
+        $res = $agent->save();
+
+        //修改订单状态
+        $res1 = $witRepo->update(['status' => $inputData['status'], 'paid_at' => date('Y-m-d H:i:s')], $inputData['order_uid'], 'uid');
+
+        if (!$res || !$res1) {
+            DB::rollBack();
+            return json_fail('操作失败');
+        }
+
+        $res2 = $bLogRepo->record($agent, $oldAgent);
+        if (!$res2) {
+            DB::rollBack();
+            return json_fail('帐变写入失败');
+        }
+
+        DB::commit();
+        return new ConfigSuccessResponse();
+    }
+
+    /**
+     * 删除提现银行卡
+     *
+     * @param AgentBankInfoRepository $bankRepo
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function withdrawBankDel(AgentBankInfoRepository $bankRepo)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'uid' => 'required|string',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $bankInfo = $bankRepo->findBy('uid', $inputData['uid']);
+        if (empty($bankInfo)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+
+        $oldBankInfo = clone $bankInfo;
+        $result = $bankInfo->delete();
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个提现银行卡,uid:' . $bankInfo->uid;
+            $logSvalue = $bankInfo->uid;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldBankInfo->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+            return new CommonSuccessUidResponse($bankInfo->uid);
+        }
+
+
+    }
+
+    /**
+     * 提现统计计算
+     *
+     * @param $inputData
+     * @param $res
+     * @return array
+     */
+    private function withdrawTotal($inputData, $res)
+    {
+        unset($inputData['page']);
+        unset($inputData['page_size']);
+
+        if (empty($inputData)) {
+            $witTotal['unpaid_amount'] = WithDrawalLogModel::where('status', '<>', 3)->sum('amount');
+        } else {
+            $data = $res->toArray()['data'];
+            $unpaidAmount = 0;
+            foreach ($data as $val) {
+                $unpaidAmount += $val['amount'];
+            }
+
+            $witTotal['today_amount'] = $unpaidAmount;
+        }
+        $witTotal['today_amount'] = WithDrawalLogModel::query()->where('status', 3)->whereBetween('created_at', [Carbon::parse(date('Y-m-d') . ' 00:00:00'), Carbon::parse(date('Y-m-d') . ' 23:59:59')])->sum('amount');
+
+        return $witTotal;
+    }
+
+    /**
+     * 充值统计计算(如果没有查询条件就统计当日的数据)
+     *
+     * @param $inputData
+     * @param $res
+     */
+    private function rechargeTotal($inputData, $res)
+    {
+        unset($inputData['page']);
+        unset($inputData['page_size']);
+        $data = $res->toArray()['data'];
+        if (empty($inputData)) {
+            $field = ['order_status', 'real_amount'];
+            $data = PayLogsModel::query()->whereBetween('created_at', [Carbon::parse(date('Y-m-d') . ' 00:00:00'), Carbon::parse(date('Y-m-d') . ' 23:59:59')])->get($field)->toArray();
+        }
+
+        $succInt = 0;
+        $orderInt = 0;
+        $recTotal['total_money'] = 0;
+        $recTotal['succ_money'] = 0;
+        foreach ($data as $v) {
+            if ($v['order_status'] == 1) {
+                $succInt++;
+                $recTotal['succ_money'] += $v['real_amount'];
+            }
+            $recTotal['total_money'] += $v['real_amount'];
+            $orderInt ++;
+        }
+        $recTotal['succ_int'] = $succInt;
+        $recTotal['order_int'] = $orderInt;
+        // 成功率 = 成功的订单 / 总订单
+        if ($succInt == 0) {
+            $recTotal['succ_rate'] = 0;
+        } else {
+            //精度问题 2 / 14 * 100
+            $recTotal['succ_rate'] = floor(round($succInt / $orderInt, 2) * 100);
+        }
+        return $recTotal;
+    }
+}

+ 24 - 0
Modules/ManageApi/Controllers/IndexController.php

@@ -0,0 +1,24 @@
+<?php
+/**
+ * 描述.
+ *
+ * @author nj
+ * @date   2021-10-04 10:07
+ */
+
+namespace Modules\ManageApi\Controllers;
+
+
+class IndexController
+{
+    public function index()
+    {
+        $arrJson = [
+            'code' => 200,
+            'message' => '',
+            'data' => '',
+            'handshake' => '',
+        ];
+        return json_encode($arrJson);
+    }
+}

+ 486 - 0
Modules/ManageApi/Controllers/ManagerController.php

@@ -0,0 +1,486 @@
+<?php
+/**
+ * 管理员控制器.
+ *
+ * @author nj
+ * @date   2021-01-23 00:08:32
+ */
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Repositories\Criterias\CommonWithCriteria;
+use App\Repositories\Criterias\ManagerPagesCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Models\Mysql\ManagerModel;
+use App\Repositories\ManagerRepository;
+use App\Repositories\PermissionRoleRepository;
+use Modules\ManageApi\Responses\CommonSuccessUidResponse;
+use Modules\ManageApi\Responses\ManagerDetailResponse;
+use Modules\ManageApi\Responses\ManagerPagesResponse;
+use Otp\GoogleAuthenticator;
+use Illuminate\Support\Facades\Validator;
+
+class ManagerController extends BaseController
+{
+    /**
+     * 分页列表.
+     * @return mixed
+     */
+    public function lists()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var ManagerRepository $managerRepo
+         */
+        $managerRepo = app(ManagerRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'manager_uid' => 'nullable',
+            'username' => 'nullable',
+            'nick' => 'nullable',
+            'is_locked' => 'nullable|integer',
+            'role_id' => 'nullable|integer',
+            'email' => 'nullable|email',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+        // 注意,单例分页一定要克隆一个(保证前面没用使用到paginate);
+        $managers = $managerRepo->pushCriteria(new ManagerPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+
+        return new ManagerPagesResponse($managers);
+    }
+
+    public function checkUsername()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'username' => 'unique:manager,username|required|max:16',
+        ];
+        $message = [
+            'username.unique' => '账号已存在!',
+            'username.required' => '用户名不能为空!',
+            'username.max' => '用户名不能大于16字节!'
+        ];
+        $validator = Validator::make($inputData,$rules,$message);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        return json_fail('用户名可用!',200,[]);
+    }
+
+    /**
+     * 详情
+     * @return mixed
+     */
+    public function detail()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+        /**
+         * @var ManagerRepository $managerRepo
+         */
+        $managerRepo = app(ManagerRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'manager_uid' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $manager = $managerRepo->pushCriteria(new CommonWithCriteria(['role']))->findBy('manager_uid', $inputData['manager_uid']);
+
+        return new ManagerDetailResponse($manager);
+    }
+
+    /**
+     * 添加.
+     * @return mixed|CommonSuccessUidResponse
+     */
+    public function add()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+        /**
+         * @var ManagerRepository $managerRepo
+         */
+        $managerRepo = app(ManagerRepository::class);
+
+        /**
+         * @var PermissionRoleRepository $permissionRoleRepository
+         */
+        $permissionRoleRepository = make(PermissionRoleRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'username' => 'required|min:2|max:60',
+            'nick' => 'required|max:60',
+            'role_id' => 'required|integer',
+            'password' => 'required|min:6|max:64',
+            'email' => 'nullable|max:120',
+            'otp_secret' => 'nullable|max:32',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $username = strtolower(trim($inputData['username']));
+        if ($managerRepo->isExistsUsername($username)) {
+            return json_fail('用户名已经存在');
+        }
+        $roleId = intval($inputData['role_id']);
+        $roleName = '';
+        $roleObj = $permissionRoleRepository->find($roleId);
+        if (!empty($roleObj)) {
+            $roleName = $roleObj->name;
+        }
+        $setarr = [
+            'username' => $username,
+            'nick' => trim($inputData['nick']),
+            'role_id' => intval($roleId),
+            'role_name' => $roleName,
+            'otp_secret' => isset($inputData['otp_secret']) ? trim($inputData['otp_secret']) : GoogleAuthenticator::generateRandom(),
+        ];
+        if (isset($inputData['email'])) {
+            $email = trim($inputData['email']);
+        } else {
+            $email = '';
+        }
+        $salt = random(6);
+        $password = $managerRepo->generatePassword($inputData['password'], $salt);
+        $setarr['salt'] = $salt;
+        $setarr['password'] = $password;
+        $setarr['email'] = $email;
+
+        $result = $managerRepo->create($setarr);
+        if (!$result) {
+            return json_fail('创建失败');
+        } else {
+            $logSubject = '添加一个管理员,账号:'. $username;
+            $logSvalue = $result->manager_uid;
+            $logDbData = [
+                'after_items' => [
+                    $result->toArray(),
+                ],
+                'before_items' => [],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($result->manager_uid);
+        }
+    }
+
+    /**
+     * 修改管理员.
+     * @return mixed
+     */
+    public function modify()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+        /**
+         * @var ManagerRepository $managerRepo
+         */
+        $managerRepo = app(ManagerRepository::class);
+
+        /**
+         * @var PermissionRoleRepository $permissionRoleRepository
+         */
+        $permissionRoleRepository = make(PermissionRoleRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'manager_uid' => 'required|string',
+            'nick' => 'required|max:60',
+            'role_id' => 'required|integer',
+            'password' => 'nullable|min:6|max:64',
+            'email' => 'nullable|max:120',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $manager = $managerRepo->findBy('manager_uid', $inputData['manager_uid']);
+        if (empty($manager)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldManager = clone $manager;
+        $manager->nick = $inputData['nick'];
+        $manager->role_id = intval($inputData['role_id']);
+        $roleName = '';
+        $roleObj = $permissionRoleRepository->find($manager->role_id);
+        if (!empty($roleObj)) {
+            $roleName = $roleObj->name;
+        }
+        $manager->role_name = $roleName;
+        if (isset($inputData['email'])) {
+            $email = trim($inputData['email']);
+            $manager->email = $email;
+        } else {
+            $manager->email = '';
+        }
+
+        if (!empty($inputData['password'])) {
+            $salt = $manager->salt;
+            if (empty($salt)) {
+                $salt = random(6);
+            }
+            $password = $managerRepo->generatePassword($inputData['password'], $manager->salt);
+            $manager->salt = $salt;
+            $manager->password = $password;
+        }
+        $result = $manager->save();
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '修改一个管理员,账号:'. $manager->username;
+            $logSvalue = $manager->manager_uid;
+            $logDbData = [
+                'after_items' => [
+                    $manager->toArray(),
+                ],
+                'before_items' => [
+                    $oldManager->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($manager->manager_uid);
+        }
+    }
+
+    /**
+     * 删除单个.
+     * @return mixed
+     */
+    public function delete()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+        /**
+         * @var ManagerRepository $managerRepo
+         */
+        $managerRepo = app(ManagerRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'manager_uid' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $manager = $managerRepo->findBy('manager_uid', $inputData['manager_uid']);
+        if ($manager->id === 1) {
+            return json_fail('此用户不允许删除!');
+        }
+        if (empty($manager)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldManager = clone $manager;
+        $result = $manager->delete();
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个管理员,账号:'. $oldManager->username;
+            $logSvalue = $oldManager->manager_uid;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldManager->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($inputData['manager_uid']);
+        }
+    }
+
+    /**
+     * 批量操作.
+     * @return mixed
+     */
+    public function batch()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+        /**
+         * @var ManagerRepository $managerRepo
+         */
+        $managerRepo = app(ManagerRepository::class);
+
+        $inputData = $this->getJsonData();
+        $operateName = $inputData['operate_name'] ?? '';
+
+        switch ($operateName) {
+            case 'delete':
+                $itemPks = $inputData['item_pks'] ?? [];
+                if (!empty($itemPks)) {
+                    $model = new ManagerModel();
+                    $resultItems = $model->whereIn('manager_uid', $itemPks)->delete();
+                    if ($resultItems) {
+                        $strPks = implode(',', $itemPks);
+                        $logSubject = '批量删除多个管理员';
+                        $logSvalue = $strPks;
+                        $logDbData = [
+                            'after_items' => [
+                            ],
+                            'before_items' => [],
+                        ];
+                        $this->log($logSubject, $logSvalue, $logDbData);
+                    }
+                }
+
+                $data = [
+                    'result' => 'success',
+                    'pks' => $itemPks,
+                ];
+                return json_success($data);
+                break;
+            default:
+                break;
+        }
+
+        return json_fail('未知操作');
+    }
+
+    /**
+     * 我的详情.
+     * @return mixed|ManagerDetailResponse
+     */
+    public function detailMe()
+    {
+        $manager = $this->getCurrentManger();
+        if (empty($manager)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+
+        return new ManagerDetailResponse($manager);
+    }
+
+    /**
+     * 修改自己信息.
+     * @return mixed|CommonSuccessUidResponse
+     */
+    public function modifyMe()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+        /**
+         * @var ManagerRepository $managerRepo
+         */
+        $managerRepo = app(ManagerRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'nick' => 'required|max:60',
+            'password' => 'nullable|min:6|max:64',
+            'email' => 'nullable|max:120',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $manager = $this->getCurrentManger();
+        if (empty($manager)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldManager = clone $manager;
+        $manager->nick = $inputData['nick'];
+        if (isset($inputData['email'])) {
+            $email = trim($inputData['email']);
+            $manager->email = $email;
+        } else {
+            $manager->email = '';
+        }
+
+        if (!empty($inputData['password'])) {
+            $salt = $manager->salt;
+            if (empty($salt)) {
+                $salt = random(6);
+            }
+            $password = $managerRepo->generatePassword($inputData['password'], $manager->salt);
+            $manager->salt = $salt;
+            $manager->password = $password;
+        }
+        $result = $manager->save();
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '修改自己管理员信息,账号:'. $manager->username;
+            $logSvalue = $manager->manager_uid;
+            $logDbData = [
+                'after_items' => [
+                    $manager->toArray(),
+                ],
+                'before_items' => [
+                    $oldManager->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($manager->manager_uid);
+        }
+    }
+}

+ 435 - 0
Modules/ManageApi/Controllers/PermissionController.php

@@ -0,0 +1,435 @@
+<?php
+/**
+ * 管理权限分配.
+ *
+ * @author nj
+ * @date   2021-01-23 23:55:52
+ */
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Models\Mysql\ManagerModel;
+use App\Models\Mysql\PermissionResourceModel;
+use App\Models\Mysql\PermissionRoleModel;
+use App\Repositories\ManagerRepository;
+use App\Repositories\PermissionResourceRepository;
+use App\Repositories\PermissionRoleRepository;
+use Modules\ManageApi\Responses\CommonPagesResponse;
+use Modules\ManageApi\Responses\CommonSuccessPkResponse;
+use Modules\ManageApi\Responses\PermissionRoleDetailResponse;
+use Modules\ManageApi\Responses\PermissionRolePagesResponse;
+use Validator;
+use DB;
+
+class PermissionController extends BaseController
+{
+    /**
+     * 分页列表.
+     * @return \Illuminate\Http\JsonResponse|CommonPagesResponse
+     */
+    public function resourceLists()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var PermissionResourceRepository $permissionResourceRepo
+         */
+        $permissionResourceRepo = make(PermissionResourceRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page']);
+        $orders = [
+            ['id', 'DESC'],
+        ];
+        $lists = $permissionResourceRepo->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+
+        return new CommonPagesResponse($lists);
+    }
+
+    /**
+     * 删除.
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessPkResponse
+     * @throws \Exception
+     */
+    public function resourceDelete()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var PermissionResourceRepository $permissionResourceRepo
+         */
+        $permissionResourceRepo = make(PermissionResourceRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $item = $permissionResourceRepo->find($inputData['id']);
+        if (empty($item)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldItem = clone $item;
+        $result = $item->delete();
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个权限资源,id:'. $oldItem->id;
+            $logSvalue = $oldItem->id;
+            $logDbData = [
+                'after_items' => [
+                ],
+                'before_items' => [
+                    $oldItem->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessPkResponse($inputData['id']);
+        }
+    }
+
+    /**
+     * 详情角色.
+     * @return mixed
+     */
+    public function roleDetail()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var PermissionRoleRepository $permissionRoleRepository
+         */
+        $permissionRoleRepository = make(PermissionRoleRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $item = $permissionRoleRepository->find($inputData['id']);
+
+        return new PermissionRoleDetailResponse($item);
+    }
+
+    /**
+     * 添加角色.
+     * @return mixed
+     */
+    public function roleAdd()
+    {
+
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var PermissionRoleRepository $permissionRoleRepository
+         */
+        $permissionRoleRepository = make(PermissionRoleRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'name' => 'required|max:60',
+            'permissions' => 'required|array',
+            'squad' => 'nullable|integer',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        if (isset($inputData['permissions'])) {
+            $permissions = !empty($inputData['permissions']) ? implode(',', $inputData['permissions']) : '';
+        } else {
+            $permissions = '';
+        }
+        if (!isset($inputData['squad'])) {
+            $squad = 1000;
+        } else {
+            $squad = intval($inputData['squad']);
+        }
+        $setarr = [
+            'name' => trim($inputData['name']),
+            'permissions' => $permissions,
+            'squad' => $squad,
+        ];
+        $result = $permissionRoleRepository->create($setarr);
+        if (!$result) {
+            return json_fail('创建失败');
+        } else {
+            $logSubject = '添加一个角色, 新名称:'. $result->name;
+            $logSvalue = $result->id;
+            $logDbData = [
+                'after_items' => [
+                    $result->toArray(),
+                ],
+                'before_items' => [
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessPkResponse($result->id);
+        }
+    }
+
+    /**
+     * 修改角色.
+     * @return mixed|CommonSuccessPkResponse
+     */
+    public function roleModify()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var PermissionRoleRepository $permissionRoleRepository
+         */
+        $permissionRoleRepository = make(PermissionRoleRepository::class);
+
+        /**
+         * @var ManagerRepository $managerRepository
+         */
+        $managerRepository = make(ManagerRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required',
+            'name' => 'required|max:60',
+            'permissions' => 'required|array',
+            'squad' => 'nullable|integer',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $item = $permissionRoleRepository->find($inputData['id']);
+        if (empty($item)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldItem = clone $item;
+        if (isset($inputData['permissions'])) {
+            $permissions = !empty($inputData['permissions']) ? implode(',', $inputData['permissions']) : '';
+        } else {
+            $permissions = '';
+        }
+        if (!isset($inputData['squad'])) {
+            $squad = 1000;
+        } else {
+            $squad = intval($inputData['squad']);
+        }
+        $oldName = $item->name;
+        $name = trim($inputData['name']);
+        $item->name = $name;
+        $item->permissions = $permissions;
+        $item->squad = $squad;
+        $result = false;
+        DB::beginTransaction();
+        try {
+            $result = $item->save();
+            if ($result) {
+                if ($name != $oldName) {
+                    // 批量替换冗余的管理角色名称.
+                    $rows = $managerRepository->update(['role_name' => $name], 'role_id', $inputData['id']);
+                }
+            }
+            DB::commit();
+        } catch (\Throwable $e) {
+            DB::rollBack();
+        }
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '修改一个角色, 新名称:'. $item->name;
+            $logSvalue = $item->id;
+            $logDbData = [
+                'after_items' => [
+                    $item->toArray(),
+                ],
+                'before_items' => [
+                    $oldItem->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessPkResponse($item->id);
+        }
+    }
+
+    /**
+     * 删除角色.
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessPkResponse
+     * @throws \Exception
+     */
+    public function roleDelete()
+    {
+
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var PermissionRoleRepository $permissionRoleRepository
+         */
+        $permissionRoleRepository = make(PermissionRoleRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $item = $permissionRoleRepository->find($inputData['id']);
+        if (empty($item)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldItem = clone $item;
+        $result = $item->delete();
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个角色, 名称:'. $oldItem->name;
+            $logSvalue = $oldItem->id;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldItem->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessPkResponse($inputData['id']);
+        }
+    }
+    /**
+     * 角色页列表.
+     * @return mixed|CommonPagesResponse
+     */
+    public function roleLists()
+    {
+
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var PermissionRoleRepository $permissionRoleRepository
+         */
+        $permissionRoleRepository = make(PermissionRoleRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page']);
+        $orders = [
+            ['squad', 'DESC'],
+            ['id', 'DESC'],
+        ];
+        // 注意,单例分页一定要克隆一个(保证前面没用使用到paginate);
+
+        $lists = $permissionRoleRepository->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+
+        return new PermissionRolePagesResponse($lists);
+    }
+
+    /**
+     * 获取有效树形权限.
+     * @return mixed
+     */
+    public function allTree()
+    {
+        /**
+         * @var PermissionResourceRepository $obj
+         */
+        $obj = make(PermissionResourceRepository::class);
+
+        $rTree = $obj->getAllEnableTree();
+
+        return json_success($rTree);
+    }
+}

+ 230 - 0
Modules/ManageApi/Controllers/PluploadController.php

@@ -0,0 +1,230 @@
+<?php
+/**
+ * author: nj
+ * email:
+ * date: 2021-10-16 14:43
+ */
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Repositories\UploadFileRepository;
+use App\Repositories\Utils\Check;
+use File;
+
+class PluploadController extends BaseController
+{
+    private function runUpload()
+    {
+        $request = request();
+        $result = self::getResetMsgResult();
+        $upload_sys_info = get_upload_sys_info();
+        $upload_default_params = array(
+            'manager_uid' => $this->ctManagerUid,
+            'user_type' => 2,   //0=游客, 1=前台用户, 2=后台用户
+            'is_appointed' => 0,   //0-不固定高宽  1-固定高宽
+            'is_water' => 0,    //0-无水印 1-加水印
+            'max_width' => 0,   //最大宽度
+            'max_height' => 0,  //最大高度
+            'max_size' => $upload_sys_info['upload_max_size'],    //允许最大上传尺寸,单位K
+            'ext_type' => 1,    //扩展名类型 0-不限制  1-图片 2-附件 3-flash 4-多媒体
+            'thumb_width' => array(1600, 600, 120),
+            'thumb_height' => array(1600, 600, 120),
+            'thumb_water' => array(0, 0, 0),
+            'server_no' => config('image_servers')['upload_server_no'], //当前配置的服务器编号.
+        );
+
+        $upload_field_name = $request->input('upload_field_name', '');   //上传字段名
+        $module_alias =$request->input('module_alias', '');    //模块
+        $related_id = intval($request->input('related_id', 0)); //关联ID
+        $item_alias = $request->input('item_alias', '');    //子项别名
+        $item_id = intval($request->input('item_id', 0));   //子项ID
+        $max_size = intval($request->input('max_size', 0));     //上传最大尺寸限制
+        $thumb_appointed = $request->input('thumb_appointed', ''); //是否强制尺寸高宽
+        $thumb_water = $request->input('thumb_water', '');     //是否加水印
+        $thumb_num = intval($request->input('thumb_num', 0));   //缩略图数量
+        $thumb_max_width = $request->input('thumb_max_width', '');  //缩略图最大宽度,多组用半角逗号分隔开
+        $thumb_max_height = $request->input('thumb_max_height', '');//缩略图最大高度,多组用半角逗号分隔开
+        $ext_type = intval($request->input('ext_type', 1)); //文件扩展名限制类型 0-不限制  1-图片 2-附件 3-flash 4-多媒体
+        $sub_dir = $request->input('sub_dir', '');  //子目录
+        $user_type = 2;   //0=游客, 1=前台用户, 2=后台用户
+        $server_type = 0;   //服务器类型, 0=本地服务器,1=自由可控远程服务器,3=第三方比如七牛
+        $need_origin_pic = intval($request->input('need_origin_pic', 0));
+        $unlink_origin_pic = $need_origin_pic == 0 ? true : false;  //是否删除原图
+        $serverNo = $request->input('server_no', null); // 服务器编号.
+
+        if ($sub_dir) {
+            if (!Check::dirName($sub_dir)) {
+                $result['error']['code'] = -92;
+                $result['error']['message'] = '子目录名有误';
+                return $result;
+            }
+        }
+        //判断外部提交的最大尺寸是否超出系统允许的最大尺寸
+        if ($max_size > $upload_default_params['max_size']) {
+            $max_size = $upload_default_params['max_size'];
+        } elseif (empty($max_size)) {
+            $max_size = $upload_default_params['max_size'];
+        }
+        $water_picture = public_path('assets/sys/watermark.png');    //水印图
+        $water_picture_exists = File::exists($water_picture) ? true : false;
+        $arr_thumb_max_width = $arr_thumb_max_height = $arr_thumb_water = $arr_thumb_appointed = array();
+        $arr_thumb_max_width = explode(',', $thumb_max_width);
+        $arr_thumb_max_height = explode(',', $thumb_max_height);
+        $arr_thumb_water = explode(',', $thumb_water);
+        $arr_thumb_appointed = explode(',', $thumb_appointed);
+
+        $arr_ext = self::getAllowExtByType($ext_type);
+
+        if ($max_size == -1) {
+            $format_max_size = -1;
+        } else {
+            $format_max_size = $max_size * 1024;
+        }
+        $upload_save_dir = 'attachment';  // 目前写死,以后写配置里 todo.
+        $save_root_path = public_path('');
+        $uploader = new UploadFileRepository(
+            $format_max_size,
+            $arr_ext,
+            array(),
+            $upload_save_dir,
+            $save_root_path
+        );
+        $uploader->urlBase = '/';
+        $uploader->thumb = $thumb_num > 0 ? true : false;
+        $uploader->thumbMaxWidth = $arr_thumb_max_width;
+        $uploader->thumbMaxHeight = $arr_thumb_max_height;
+        $uploader->thumbAppointed = $arr_thumb_appointed;
+        $uploader->thumbWater = $arr_thumb_water;
+        $uploader->waterType = 'picture';
+        if ($water_picture_exists) {
+            $uploader->waterPicture = $water_picture;
+        }
+        $uploader->waterPosition = 9;   //位置9
+        $uploader->unlinkOriginPic = $unlink_origin_pic; //是否除原图
+        $uploader->subDir = $sub_dir;
+        if (!empty($serverNo)) {
+            $uploader->setServerNo($serverNo);
+        }
+
+        $upload_field_name = empty($upload_field_name) ? 'upload_files' : $upload_field_name;
+
+        if ($uploader->upload($upload_field_name)) {
+            $arr_attachments = $arr_origin_pics = array();
+            $arr_attachment_list = $uploader->getResultFileInfo()[0];
+            if ($arr_attachment_list) {
+                foreach ($arr_attachment_list as $k => $v) {
+                    if (isset($v['link_url']) && $v['link_url']) {
+                        $link_url = $v['link_url'];
+                        $link_origin_url = $unlink_origin_pic == true ?
+                            '' :  $v['origin_pic_relative_path'];
+
+                        $arr_attachment_list[$k]['id'] = '';
+                        $arr_attachments[$k] = $arr_attachment_list[$k];
+                        $arr_attachments[$k]['link_url'] = get_url_by_name($link_url);
+                        $arr_attachments[$k]['link_origin_url'] = get_url_by_name($link_origin_url); //原图绝对地址
+                        unset(
+                            $arr_attachments[$k]['user_type'],
+                            $arr_attachments[$k]['user_id'],
+                            $arr_attachments[$k]['origin_alias'],
+                            $arr_attachments[$k]['module_alias'],
+                            $arr_attachments[$k]['related_id'],
+                            $arr_attachments[$k]['ip'],
+                            $arr_attachments[$k]['item_alias'],
+                            $arr_attachments[$k]['item_id'],
+                            $arr_attachments[$k]['origin_pic_native_path'],
+                            $arr_attachments[$k]['file_native_path'],
+                            $arr_attachments[$k]['origin_pic_name'],
+                            $arr_attachments[$k]['origin_pic_size'],
+                            $arr_attachments[$k]['origin_pic_width'],
+                            $arr_attachments[$k]['origin_pic_height']
+                        );
+                    }
+                }
+            }
+
+            $result['content'] = $arr_attachments;
+        } else {
+            $result['error']['code'] = -91;
+            $result['error']['message'] = $uploader->getResultError();
+        }
+        return $result;
+    }
+
+    public function upload()
+    {
+        $result = self::runUpload();
+
+        return response()->json($result);
+    }
+
+    public function uEditorUploadResult()
+    {
+        $result = array('ErrorCode' => 0, 'Message' => '', 'Content' => '');
+        $upload_result = self::runUpload();
+        $result['ErrorCode'] = $upload_result['error']['code'];
+        $result['Message'] = $upload_result['error']['message'];
+        $result['Content'] = $upload_result['content'];
+        return $result;
+    }
+
+    public function editorMdUploadResult()
+    {
+        $result = array('ErrorCode' => 0, 'Message' => '', 'Content' => '');
+        $upload_result = self::runUpload();
+        $result['ErrorCode'] = $upload_result['error']['code'];
+        $result['Message'] = $upload_result['error']['message'];
+        $result['Content'] = $upload_result['content'];
+        return $result;
+    }
+
+    /**
+     * 获取重设的消息结果
+     * @return array
+     */
+    private function getResetMsgResult()
+    {
+        return array( 'jsonrpc' => '2.0',
+            'error' => array('code' => 0, 'message' => '', 'id' => 'id'), 'content' => '' );
+    }
+
+    /**
+     * 获取允许的扩展名
+     * @param int $ext_type       扩展类型
+     * @return array
+     */
+    private function getAllowExtByType($ext_type)
+    {
+        $arr_exts = array(
+            'picture' => array('jpg', 'gif', 'png', 'jpeg'),
+            'compress' => array('zip', 'rar', '7z', 'tar', 'bz2', 'gz', 'apk'),
+            'office' => array('docx', 'xlsx', 'pptx', 'doc', 'xls',
+                'ppt', 'pps', 'wps', 'et', 'dps', 'xml', 'kml'),    //办公
+            'flash' => array('swf', 'flv', 'as', 'fla'),    //FLASH
+            'media' => array("ra","rm","rmvb","avi","mpeg","mid","wma","wav","mp3","wmv","mov", 'mp4', 'm3u8', 'lrc'),    //多媒体
+        );
+        $return_ext = array();
+        switch ($ext_type) {
+            case 1:
+                $return_ext = $arr_exts['picture'];
+                break;
+            case 2:
+                $return_ext = array_merge($arr_exts['compress'], $arr_exts['office']);
+                break;
+            case 3:
+                $return_ext = $arr_exts['flash'];
+                break;
+            case 4:
+                $return_ext = $arr_exts['media'];
+                break;
+            case 5:
+                $return_ext = array_merge($arr_exts['picture'], $arr_exts['flash']);
+                break;
+            case 6:
+                $return_ext = array_merge($arr_exts['flash'], $arr_exts['media']);
+                break;
+            default:
+                break;
+        }
+        return $return_ext;
+    }
+}

+ 188 - 0
Modules/ManageApi/Controllers/SystemController.php

@@ -0,0 +1,188 @@
+<?php
+/**
+ * 系统配置.
+ *
+ * @author nj
+ * @date   2021-01-31 10:44:33
+ */
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Repositories\Criterias\ManagerLogPagesCriteria;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Repositories\ConfigRepoistory;
+use App\Repositories\ManagerLogRepository;
+use Modules\ManageApi\Responses\CommonDetailResponse;
+use Modules\ManageApi\Responses\CommonSuccessPkResponse;
+use Modules\ManageApi\Responses\SystemLogPagesResponse;
+use Validator;
+
+class SystemController extends BaseController
+{
+    /**
+     * 读取配置信息.
+     * @return mixed
+     */
+    public function configInfo()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var ConfigRepoistory $configRepository
+         */
+        $configRepository = make(ConfigRepoistory::class);
+
+        $inputData = $this->getJsonData();
+        $rules = [
+            'sgroup' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $data = $configRepository->pickByKeys([], $inputData['sgroup']);
+        return new CommonDetailResponse($data);
+    }
+
+    /**
+     * 配置保存.
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessPkResponse
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function configSave()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var ConfigRepoistory $configRepository
+         */
+        $configRepository = make(ConfigRepoistory::class);
+
+        $inputData = $this->getJsonData();
+        $rules = [
+            'sgroup' => 'required|string',
+            'input'  => 'required',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $repo = $configRepository;
+        $repo->saveByKeys($inputData['input'], $inputData['sgroup']);
+        // 保存后同时更新缓存.
+        $repo->updateConfigCache($inputData['sgroup']);
+
+        // 操作日志.
+        $logSubject = '保存系统配置';
+        $logSvalue = '';
+        $logDbData = [
+            'after_items' => [],
+            'before_items' => [],
+        ];
+        $this->log($logSubject, $logSvalue, $logDbData);
+
+        return new CommonSuccessPkResponse('');
+    }
+
+    /**
+     * 配置缓存更新.
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessPkResponse
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function configUpdateCache()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var ConfigRepoistory $configRepository
+         */
+        $configRepository = make(ConfigRepoistory::class);
+
+        $inputData = $this->getJsonData();
+        $rules = [
+            'sgroup' => 'required|string',
+        ];
+        $validator =$validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $configRepository->updateConfigCache($inputData['sgroup']);
+
+        // 操作日志.
+        $logSubject = '更新系统配置缓存';
+        $logSvalue = '';
+        $logDbData = [
+            'after_items' => [],
+            'before_items' => [],
+        ];
+        $this->log($logSubject, $logSvalue, $logDbData);
+
+        return new CommonSuccessPkResponse('');
+    }
+
+    /**
+     * 管理日志列表.
+     * @return mixed
+     */
+    public function logLists()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var ConfigRepoistory $configRepository
+         */
+        $configRepository = make(ConfigRepoistory::class);
+
+        /**
+         * @var ManagerLogRepository $managerLogRepository
+         */
+        $managerLogRepository = make(ManagerLogRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'username' => 'nullable|max:60',
+            'nick' => 'nullable|max:60',
+            'subject' => 'nullable|max:700',
+            'module' => 'nullable|max:30',
+            'ip' => 'nullable|max:40',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+        // 注意,单例分页一定要克隆一个(保证前面没用使用到paginate);
+        $lists = $managerLogRepository->pushCriteria(new ManagerLogPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+
+        return new SystemLogPagesResponse($lists);
+    }
+}

+ 47 - 0
Modules/ManageApi/Controllers/TotalController.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Repositories\AgentAttrsDayMongoRepository;
+use App\Repositories\AgentAttrsDayRepository;
+use Carbon\Carbon;
+use Illuminate\Support\Facades\Validator;
+
+class TotalController extends BaseController
+{
+    /**
+     * 总数据
+     *
+     * @return \Illuminate\Http\JsonResponse|mixed
+     */
+    public function income()
+    {
+        $inputData = $this->getJsonData();
+        $rules = [
+            'start_date' => 'nullable|date',
+            'end_date' => 'nullable|date|after_or_equal:start_date',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        /**
+         * @var AgentAttrsDayMongoRepository $dayReps
+         */
+        $dayReps = app(AgentAttrsDayMongoRepository::class);
+        if (isset($inputData['data']['start_date']) && isset($inputData['data']['end_date'])) {
+            $startDate = Carbon::parse($inputData['data']['start_date'])->startOfDay()->toDateTimeString();
+            $endDate = Carbon::parse($inputData['data']['end_date'])->endOfDay()->toDateTimeString();
+        } else {
+            $startDate = Carbon::parse()->startOfDay()->toDateTimeString();
+            $endDate = Carbon::parse()->endOfDay()->toDateTimeString();
+        }
+
+
+        $res =$dayReps->sumTotalData($startDate, $endDate);
+
+        return json_success($res);
+    }
+}

+ 416 - 0
Modules/ManageApi/Controllers/UeditorController.php

@@ -0,0 +1,416 @@
+<?php
+/**
+ * author: nj
+ * email:
+ * date: 2021-11-04 11:32
+ */
+
+namespace Modules\ManageApi\Controllers;
+
+use Carbon\Carbon;
+use File;
+
+class UeditorController extends BaseController
+{
+    public function service()
+    {
+        /* start 检查是否登陆 */
+        /*$check_result = self::checkLogined();
+        if ($check_result['ErrorCode'] < 0) {
+            $ue_result = array(
+                "state" => "请先登录",
+            );
+            return Response::json($ue_result);
+        };*/
+        /* end 检查是否登陆 */
+
+        $action = request()->get('action', '');
+        switch ($action) {
+            case 'config':
+                $cfg = self::getJsonConfig();
+                return response()->json($cfg);
+                break;
+            case 'UploadFile':
+            case 'UploadImage':
+            case 'UploadScrawl':
+            case 'UploadSnapScreen':
+            case 'UploadVideo':
+                return self::actionUploadImage();
+                break;
+            case 'CatchImage':
+                $ue_result = array(
+                    'state' => 'ERROR',
+                    'list' => array(),
+                );
+                //return response()->json($ue_result);
+                return self::actionCatchImage(); //启用远程采集图片,取消这个注释
+                break;
+            case 'ListImage':
+                return self::actionListImage(1);
+                break;
+            case 'ListFile':
+                return self::actionListImage(2);
+                break;
+            default:
+                $ue_result = array(
+                    "state" => "非法请求Action",
+                );
+                return response()->json($ue_result);
+                break;
+        }
+    }
+
+    private function actionUploadImage()
+    {
+        $Plupload = new PluploadController();
+        $arr_result = $Plupload->uEditorUploadResult();
+        $ue_result = array(
+            'state' => '',  //状态
+            'url' => '',    //地址
+            'title' => '',  //生成的文件名
+            'original' => '',   //原文件名
+        );
+        if ($arr_result['ErrorCode'] < 0) {
+            $ue_result['state'] = $arr_result['Message'];
+        } else {
+            $ue_result['state'] = 'SUCCESS';
+            $ue_result['url'] = $arr_result['Content'][0]['link_url'];
+            $ue_result['title'] = $arr_result['Content'][0]['file_origin_name'];
+            $ue_result['original'] = $arr_result['Content'][0]['file_origin_name'];
+        }
+        return response()->json($ue_result);
+    }
+
+    private function actionUploadFile()
+    {
+        $Plupload = new PluploadController();
+        $arr_result = $Plupload->uEditorUploadResult();
+        $ue_result = array(
+            'state' => '',  //状态
+            'url' => '',    //地址
+            'title' => '',  //生成的文件名
+            'original' => '',   //原文件名
+        );
+        if ($arr_result['ErrorCode'] < 0) {
+            $ue_result['state'] = $arr_result['Message'];
+        } else {
+            $ue_result['state'] = 'SUCCESS';
+            $ue_result['url'] = $arr_result['Content'][0]['link_url'];
+            $ue_result['title'] = $arr_result['Content'][0]['file_origin_name'];
+            $ue_result['original'] = $arr_result['Content'][0]['file_origin_name'];
+        }
+        return response()->json($ue_result);
+    }
+
+    private function actionCatchImage()
+    {
+        $request = request();
+        $arr_list = array();
+        $arr_return = array(
+            'state' => 'SUCCESS',
+            'list' => $arr_list,
+        );
+        $cfg = self::getJsonConfig();
+        $sources = request()->get($cfg['catcherFieldName'], array());
+        if ($sources) {
+            $upload_sys_info = get_upload_sys_info();
+            $upload_default_params = array(
+                'manager_uid' => $this->ctManagerUid,
+                'user_type' => 2,   //0=游客, 1=前台用户, 2=后台用户
+                'is_appointed' => 0,   //0-不固定高宽  1-固定高宽
+                'is_water' => 0,    //0-无水印 1-加水印
+                'max_width' => 0,   //最大宽度
+                'max_height' => 0,  //最大高度
+                'max_size' => $upload_sys_info['upload_max_size'],    //允许最大上传尺寸,单位K
+                'ext_type' => 1,    //扩展名类型 0-不限制  1-图片 2-附件 3-flash 4-多媒体
+                'thumb_width' => array(160, 600, 120),
+                'thumb_height' => array(1600, 600, 120),
+                'thumb_water' => array(0, 0, 0),
+            );
+
+            $scraper_referer = $request->get('scraper_referer', '');    //爬虫来源.
+            $module_alias = $request->get('module_alias', '');    //模块
+            $related_id = intval($request->get('related_id', 0)); //关联ID
+            $item_alias = $request->get('item_alias', '');    //子项别名
+            $item_id = intval($request->get('item_id', 0));   //子项ID
+            $max_size = intval($request->get('max_size', 0));     //上传最大尺寸限制
+            $thumb_appointed = $request->get('thumb_appointed', ''); //是否强制尺寸高宽
+            $thumb_water = $request->get('thumb_water', '');     //是否加水印
+            $thumb_num = intval($request->get('thumb_num', 0));   //缩略图数量
+            $thumb_max_width = $request->get('thumb_max_width', '');  //缩略图最大宽度,多组用半角逗号分隔开
+            $thumb_max_height = $request->get('thumb_max_height', '');//缩略图最大高度,多组用半角逗号分隔开
+            $ext_type = intval($request->get('ext_type', 1)); //文件扩展名限制类型 0-不限制  1-图片 2-附件 3-flash 4-多媒体
+            $user_type = 2;   //0=游客, 1=前台用户, 2=后台用户
+            $server_type = 0;   //服务器类型, 0=本地服务器,1=自由可控远程服务器,3=第三方比如七牛
+            $need_origin_pic = 0;
+            $unlink_origin_pic = $need_origin_pic == 0 ? true : false;  //是否删除原图
+
+            $server_bucket = '';    //第三方空间名
+            $server_local_alias = 'img';
+            $origin_alias = 'web';
+
+            //判断外部提交的最大尺寸是否超出系统允许的最大尺寸
+            if ($max_size > $upload_default_params['max_size']) {
+                $max_size = $upload_default_params['max_size'];
+            } elseif (empty($max_size)) {
+                $max_size = $upload_default_params['max_size'];
+            }
+            $water_picture = public_path('assets/sys/watermark.png');    //水印图
+            $water_picture_exists = File::exists($water_picture) ? true : false;
+            $arr_thumb_max_width = $arr_thumb_max_height = $arr_thumb_water = $arr_thumb_appointed = array();
+            $arr_thumb_max_width = explode(',', $thumb_max_width);
+            $arr_thumb_max_height = explode(',', $thumb_max_height);
+            $arr_thumb_water = explode(',', $thumb_water);
+            $arr_thumb_appointed = explode(',', $thumb_appointed);
+
+            if ($max_size == -1) {
+                $format_max_size = -1;
+            } else {
+                $format_max_size = $max_size * 1024;
+            }
+
+            //$now_date = Carbon::now()->format('Y-m-d H:i:s');
+            $arr_mimes = include(storage_path('app/data') . DIRECTORY_SEPARATOR . 'mime.php');
+            $img_base_url = '';
+
+            foreach ($sources as $k => $url) {
+                $arr_head = get_remote_file_info($url, 'all');
+                if ($arr_head) {    //已经存在
+                    $file_size = $arr_head['Content-Length'] ? $arr_head['Content-Length'] : 0; //单位bytes
+                    $file_type = $arr_head['Content-Type'] ? $arr_head['Content-Type'] : '';    //文件类型
+                    $file_no_dot_ext = file_ext($url);
+                    $file_ext = '.' . $file_no_dot_ext;
+                    if (!in_array($file_ext, $cfg['catcherAllowFiles'])) {
+                        $arr_list[$k]['state'] = '非法文件扩展名';
+                        continue;
+                    }
+                    if ($file_size > $cfg['catcherMaxSize']) {
+                        $arr_list[$k]['state'] = '尺寸过大';
+                        continue;
+                    }
+
+                    //打开输出缓冲区并获取远程图片
+                    ob_start();
+                    $context = stream_context_create(
+                        array('http' => array(
+                            'follow_location' => false // don't follow redirects
+                        ))
+                    );
+                    readfile($url, false, $context);
+                    $img = ob_get_contents();
+                    ob_end_clean();
+                    preg_match("/[\/]([^\/]*)[\.]?[^\.\/]*$/", $url, $m);
+                    $file_origin_name = $m ? $m[1] : '';
+                    $file_size = strlen($img);
+                    if ($file_size > $cfg['catcherMaxSize']) {
+                        $arr_list[$k]['state'] = '尺寸过大';
+                        continue;
+                    }
+
+                    $dateDir = caesar_encode(Carbon::now()->format('Ymd'), '', 3);
+                    $file_name = $dateDir . $file_ext;
+                    $file_mime = isset($arr_mimes[$file_no_dot_ext]) ?
+                        $arr_mimes[$file_no_dot_ext] : 'image/jpeg';    //文件MIME
+                    $upload_save_dir = 'attachment';  // 目前写死,以后写配置里 todo.
+                    $save_root_path = public_path('');
+                    $arr_path = self::getSaveDir($upload_save_dir, '', $save_root_path);
+                    $save_dir_path = $arr_path['path']; //绝对路径
+                    $save_relative_dir_path = $arr_path['relative_path'];   //相对路径
+                    $save_native_file_path = $save_dir_path . $file_name;
+                    $save_relative_file_path = $save_relative_dir_path . $file_name;   //相对路径
+                    if (!File::put($save_native_file_path, $img)) {
+                        $arr_list[$k]['state'] = '文件移动失败';
+                        continue;
+                    }
+                    $img_local_info = getimagesize($save_native_file_path);
+                    $width = $height = 0;
+                    if ($img_local_info !== false) {
+                        $width = $img_local_info[0];
+                        $height = $img_local_info[1];
+                    }
+                    $link_url = $img_base_url . $save_relative_file_path;
+
+                    $arr_list[$k] = array(
+                        'state' => 'SUCCESS',
+                        'url' => $link_url,
+                        'size' => $file_size,
+                        'title' => htmlspecialchars($file_name),
+                        'original' => htmlspecialchars($file_origin_name),
+                        'source' => htmlspecialchars($url),
+                    );
+                } else {    //不存在
+                    $arr_list[$k]['state'] = '远程图片不存在';
+                    continue;
+                }
+            }
+            //end foreach
+            $arr_return['list'] = $arr_list;
+        }
+        return response()->json($arr_return);
+    }
+
+    private function actionListImage($type = 1)
+    {
+        $request = request();
+        $new_start = 0;
+        $page = 1;
+        $arr_list = $arr_data = $condition = $order_by =  array();
+        if ($type == 1) {
+            $condition = array(
+                array('is_pic' => 1)
+            );
+        }
+        $order_by = array(
+            array('attachment_id', 'DESC'),
+        );
+        $arr_return = array(
+            'state' => 'SUCCESS',
+            'list' => array(),
+            'start' => 0,
+            'total' => 0,
+        );
+
+        $cfg = self::getJsonConfig();
+        $sys_page_size = 20;
+        $page_size = $cfg['imageManagerListSize'] ? $cfg['imageManagerListSize'] : $sys_page_size;
+        $size = (int)$request->get('size', $page_size);
+        if ($size < 0) {
+            $size = $page_size;
+        }
+        $start = (int)$request->get('start', 0);
+        if ($start < 0) {
+            $start = 0;
+        }
+
+        $page = ceil($start / $size) + 1;
+        if ($page < 1) {
+            $page = 1;
+        }
+        $total_items = 0;
+
+        $arr_return['state'] = 'no match file';
+        $arr_return['total'] = $total_items;
+        $arr_return['start'] = $start;
+        return response()->json($arr_return);
+    }
+
+
+    /**
+     * 获得保存的目录
+     * @param string $save_path   保存目录
+     * @param string $sub_dir     子目录
+     * @param string $root_path   绝对路径根目录
+     * @return array
+     */
+    private function getSaveDir($save_path, $sub_dir = '', $root_path = '')
+    {
+        $root_path = rtrim($root_path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+        $dir = trim($save_path, '/') . '/';
+        if ($sub_dir) {
+            $dir .= $sub_dir . '/';
+        }
+        $dir_date = date("Ymd") . '/';
+        $dir .= $dir_date;
+
+        $relative_path = $dir;
+        $dir = $root_path . $dir;
+        if (!File::isDirectory($dir)) {
+            File::makeDirectory($dir, 0777, true, true);
+        }
+        return array(
+            'path' => $dir,
+            'relative_path' => $relative_path,
+        );
+    }
+
+    private function getJsonConfig()
+    {
+        $page_size = 20;
+        $upload_sys_info = get_upload_sys_info();
+        $upload_max_size = $upload_sys_info['upload_max_size'] * 1024;
+        $arr_config = array(
+            //上传图片配置项
+            'imageActionName' => 'UploadImage', //执行上传图片的action名称
+            'imageFieldName' => 'upload_files',  //提交的图片表单名称
+            'imageMaxSize' => $upload_max_size,    //上传大小限制,单位B
+            'imageAllowFiles' => array('.gif', '.jpg', '.jpeg', '.png'), //上传图片格式显示
+            'imageCompressEnable' => true, //是否压缩图片,默认是true
+            'imageCompressBorder' => 3600, //图片压缩最长边限制
+            'imageInsertAlign' => 'none',    //插入的图片浮动方式
+            'imageUrlPrefix' => '',  //图片访问路径前缀
+            'imagePathFormat' => '', //上传保存路径,可以自定义保存路径和文件名格式
+
+            //涂鸦图片上传配置项
+            'scrawlActionName' => 'UploadScrawl',    //执行上传涂鸦的action名称
+            'scrawlFieldName' => 'upload_files', //提交的图片表单名称
+            'scrawlPathFormat' => '',    //上传保存路径,可以自定义保存路径和文件名格式
+            'scrawlMaxSize' => $upload_max_size,   //上传大小限制,单位B
+            'scrawlUrlPrefix' => '',    //图片访问路径前缀
+            'scrawlInsertAlign' => 'none',   //插入的图片浮动方式
+
+            //截图工具上传
+            'snapscreenActionName' => 'UploadSnapScreen',    //执行上传截图的action名称
+            'snapscreenPathFormat' => '',    //上传保存路径,可以自定义保存路径和文件名格式
+            'snapscreenUrlPrefix' => '', //图片访问路径前缀
+            'snapscreenInsertAlign' => 'none',   //插入的图片浮动方式
+
+            //抓取远程图片配置
+            'catcherLocalDomain' => array(
+                '127.0.0.1',
+                'localhost',
+            ),
+            'catcherActionName' => 'CatchImage',    //执行抓取远程图片的action名称
+            'catcherFieldName' => 'upload_files',    //提交的图片列表表单名称
+            'catcherPathFormat' => '',   //上传保存路径,可以自定义保存路径和文件名格式
+            'catcherUrlPrefix' => '',    //图片访问路径前缀
+            'catcherMaxSize' => $upload_max_size,  //上传大小限制,单位B
+            'catcherAllowFiles' => array('.gif', '.jpg', '.jpeg', '.png', '.bmp'), //上传图片格式显示
+
+            //上传视频配置
+            'videoActionName' => 'UploadVideo', //执行上传视频的action名称
+            'videoFieldName' => 'upload_files',  //提交的视频表单名称
+            'videoPathFormat' => '', //上传保存路径,可以自定义保存路径和文件名格式
+            'videoUrlPrefix' => '',  //视频访问路径前缀
+            'videoMaxSize' => $upload_max_size,    //上传大小限制,单位B,默认100MB
+            'videoAllowFiles' => array('.flv', '.swf', '.mkv', '.avi', '.rm', '.rmvb',
+                '.mpeg', '.mpg', '.ogg', '.ogv', '.mov', '.wmv', '.mp4', '.webm', '.mp3', '.wav', '.mid', 'm3u8'), //上传视频格式显示
+
+            //上传文件配置
+            'fileActionName' => 'UploadFile',  //controller里,执行上传视频的action名称
+            'fileFieldName' => 'upload_files',  //提交的文件表单名称
+            'filePathFormat' => '',  //上传保存路径,可以自定义保存路径和文件名格式
+            'fileUrlPrefix' => '',   //文件访问路径前缀
+            'fileMaxSize' => $upload_max_size, //上传大小限制,单位B,默认50MB
+            //上传文件格式显示
+            'fileAllowFiles' => array(
+                '.gif', '.jpg', '.jpeg', '.png', '.bmp',
+                '.flv', '.swf', '.mkv', '.avi', '.rm', '.rmvb',
+                '.mpeg', '.mpg', '.ogg', '.ogv', '.mov', '.wmv', '.mp4', 'm3u8', '.webm', '.mp3', '.wav', '.mid',
+                '.rar', '.zip', '.tar', '.gz', '.7z', '.bz2', '.doc', '.docx', '.xls', '.xlsx',
+                '.ppt', '.pptx', '.pdf', '.txt', '.md', '.xml'
+            ),
+
+            //列出指定目录下的图片
+            'imageManagerActionName' => 'ListImage',  //执行图片管理的action名称
+            'imageManagerListPath' => '',    //指定要列出图片的目录
+            'imageManagerListSize' => $page_size,    //每次列出文件数量
+            'imageManagerUrlPrefix' => '',   //图片访问路径前缀
+            'imageManagerInsertAlign' => 'none', //插入的图片浮动方式
+            'imageManagerAllowFiles' => array('.gif', '.jpg', '.jpeg', '.png', '.bmp'), //列出的文件类型
+
+            //列出指定目录下的文件
+            'fileManagerActionName' => 'ListFile',   //执行文件管理的action名称
+            'fileManagerListPath' => '', //指定要列出文件的目录
+            'fileManagerUrlPrefix' => '',    //文件访问路径前缀
+            'fileManagerListSize' => $page_size, //每次列出文件数量
+            'fileManagerAllowFiles' => array(
+                '.gif', '.jpg', '.jpeg', '.png', '.bmp',
+                '.flv', '.swf', '.mkv', '.avi', '.rm', '.rmvb',
+                '.mpeg', '.mpg', '.ogg', '.ogv', '.mov', '.wmv', '.mp4', 'm3u8', '.webm', '.mp3', '.wav', '.mid',
+                '.rar', '.zip', '.tar', '.gz', '.7z', '.bz2', '.doc', '.docx', '.xls', '.xlsx',
+                '.ppt', '.pptx', '.pdf', '.txt', '.md', '.xml'
+            ),
+        );
+        return $arr_config;
+    }
+}

+ 1273 - 0
Modules/ManageApi/Controllers/VideoController.php

@@ -0,0 +1,1273 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Jobs\Video\AgentUpdateChildUidsByAllParentUidsJob;
+use App\Models\Mysql\VideoCategoryModel;
+use App\Models\Mysql\VideoModel;
+use App\Repositories\AppRepository;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Repositories\Criterias\VideoCategoryPagesCriteria;
+use App\Repositories\Criterias\VideoPagesCriteria;
+use App\Repositories\Criterias\VideoTagPagesCriteria;
+use App\Repositories\VideoCategoryRepository;
+use App\Repositories\VideoRepository;
+use App\Repositories\VideoTagRepository;
+use Illuminate\Support\Facades\Validator;
+use Modules\ManageApi\Responses\CommonAllResponse;
+use Modules\ManageApi\Responses\CommonDetailResponse;
+use Modules\ManageApi\Responses\CommonPagesResponse;
+use Modules\ManageApi\Responses\CommonSuccessPkResponse;
+use Modules\ManageApi\Responses\CommonSuccessUidResponse;
+use Modules\ManageApi\Responses\VideoCategoryDetailResponse;
+use Modules\ManageApi\Responses\VideoCategoryPagesResponse;
+use Modules\ManageApi\Responses\VideoDetailResponse;
+use Modules\ManageApi\Responses\VideoPagesResponse;
+use Modules\ManageApi\Services\VideoTagService;
+
+class VideoController extends BaseController
+{
+    /**
+     * 视频列表
+     * @return \Illuminate\Http\JsonResponse|VideoPagesResponse
+     */
+    public function lists()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoRepository $videoRepo
+         */
+        $videoRepo = app(VideoRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'video_uid' => 'nullable',
+            'type' => 'nullable|integer',
+            'category_uid' => 'nullable',
+            'tags' => 'nullable',
+            'status' => 'nullable|integer',
+            'title' => 'nullable',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+        // 注意,单例分页一定要克隆一个(保证前面没用使用到paginate);
+        $items = $videoRepo->pushCriteria(new VideoPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+
+        return new VideoPagesResponse($items);
+    }
+
+    /**
+     * 视频详情
+     * @return \Illuminate\Http\JsonResponse|VideoDetailResponse
+     */
+    public function detail()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoRepository $videoRepo
+         */
+        $videoRepo = app(VideoRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'video_uid' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $video = $videoRepo->findBy('video_uid', $inputData['video_uid']);
+
+        return new VideoDetailResponse($video);
+    }
+
+    /**
+     * 修改视频
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function modify()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoRepository $videoRepo
+         */
+        $videoRepo = app(VideoRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'video_uid' => 'required|string',
+            'type' => 'required|integer',
+            'category_uid' => 'required|string',
+            'title' => 'required|max:255',
+            'cover' => 'nullable',
+            'duration' => 'nullable',
+            'bitrate' => 'nullable',
+            'resolution' => 'nullable|max:16',
+            'tags' => 'nullable|max:700',
+            'screenshots' => 'nullable|array',
+            'status' => 'nullable|integer',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $video = $videoRepo->findBy('video_uid', $inputData['video_uid']);
+        if (empty($video)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldVideo = clone $video;
+        if (isset($inputData['type'])) {
+            $type = intval($inputData['type']);
+            $video->type = $type;
+        }
+        if (isset($inputData['category_uid'])) {
+            $categoryUid = trim($inputData['category_uid']);
+            $video->category_uid = $categoryUid;
+        }
+        if (isset($inputData['title'])) {
+            $title = trim($inputData['title']);
+            $video->title = $title;
+        }
+        if (isset($inputData['cover'])) {
+            $cover = trim($inputData['cover']);
+            if (!empty($cover)) {
+                if (!stripos($cover, '|')) {
+                    return json_fail('封面必须包含服务器组滚符号');
+                }
+            }
+            $video->cover = $cover;
+        }
+        if (isset($inputData['duration'])) {
+            $duration = intval($inputData['duration']);
+            $video->duration = $duration;
+        }
+        if (isset($inputData['bitrate'])) {
+            $bitrate = intval($inputData['bitrate']);
+            $video->bitrate = $bitrate;
+        }
+        if (isset($inputData['resolution'])) {
+            $resolution = strtolower(trim($inputData['resolution']));
+            $video->resolution = $resolution;
+        }
+        if (isset($inputData['tags'])) {
+            if (is_array($inputData['tags'])) {
+                $strTags = implode(',', $inputData['tags']);
+            } else {
+                $strTags = (string)$inputData['tags'];
+            }
+            $video->tags = $strTags;
+        }
+        if (isset($inputData['screenshots'])) {
+            if (is_array($inputData['screenshots'])) {
+                $strScreenshots = implode(',', $inputData['screenshots']);
+            } else {
+                $strScreenshots = $inputData['screenshots'];
+            }
+            $video->screenshots = $strScreenshots;
+        }
+        if (isset($inputData['queue_status'])) {
+            $queue_status = intval($inputData['queue_status']);
+            $video->queue_status = $queue_status;
+        }
+        if (isset($inputData['status'])) {
+            $status = intval($inputData['status']);
+            $video->status = $status;
+        }
+        $result = $video->save();
+        if (!$result) {
+            return json_fail('编辑失败');
+        } else {
+            $logSubject = '编辑一个视频, 标题:' . $video->title;
+            $logSvalue = $video->video_uid;
+            $logDbData = [
+                'after_items' => [
+                    $video->toArray(),
+                ],
+                'before_items' => [
+                    $oldVideo->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($video->video_uid);
+        }
+    }
+
+    /**
+     * 删除视频
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function delete()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoRepository $videoRepo
+         */
+        $videoRepo = app(VideoRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'video_uid' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $video = $videoRepo->findBy('video_uid', $inputData['video_uid']);
+        if (empty($video)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldVideo = clone $video;
+        $result = $video->delete();
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个视频,标题:' . $oldVideo->title;
+            $logSvalue = $oldVideo->video_uid;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldVideo->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($inputData['video_uid']);
+        }
+    }
+
+    /**
+     * 批量删除视频
+     */
+    public function batchDelete()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+        /**
+         * @var VideoRepository $videoRepo
+         */
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'video_uids' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        // 要删除的UID
+        $videoUids = explode(',', $inputData['video_uids']);
+        $videoRepo = app(VideoRepository::class);
+        $videoModel = clone $videoRepo;
+        // 查询有UID的数据
+        $videoKeys = $videoRepo->findArray('video_uid', $videoUids);
+        if (empty($videoKeys)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        // 批量删除
+        $result = $videoModel->delectUids('video_uid', $videoUids);
+        $videoKeysStr = implode(',', $videoKeys);
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '批量删除视频:' . $videoKeysStr;
+            $logSvalue = $videoKeysStr;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $videoKeysStr,
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($inputData['video_uids']);
+        }
+    }
+
+    /**
+     * 批量操作视频.
+     * @return \Illuminate\Http\JsonResponse|mixed
+     */
+    public function batch()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoRepository $videoRepo
+         */
+        $videoRepo = app(VideoRepository::class);
+
+        $inputData = $this->getJsonData();
+        $operateName = $inputData['operate_name'] ?? '';
+
+        switch ($operateName) {
+            case 'delete':
+                $itemPks = $inputData['item_pks'] ?? [];
+                if (!empty($itemPks)) {
+                    $model = new VideoModel();
+                    $resultItems = $model->whereIn('video_uid', $itemPks)->delete();
+                    if ($resultItems) {
+                        $strPks = implode(',', $itemPks);
+                        $logSubject = '批量删除多个视频';
+                        $logSvalue = $strPks;
+                        $logDbData = [
+                            'after_items' => [
+                            ],
+                            'before_items' => [
+                            ],
+                        ];
+                        $this->log($logSubject, $logSvalue, $logDbData);
+                    }
+                }
+
+                $data = [
+                    'result' => 'success',
+                    'pks' => $itemPks,
+                ];
+                return json_success($data);
+                break;
+            default:
+                break;
+        }
+
+        return json_fail('未知操作');
+    }
+
+    /**
+     * 视频分类分页
+     * @return \Illuminate\Http\JsonResponse|VideoCategoryPagesResponse
+     */
+    public function categoryLists()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoCategoryRepository $categoryRepo
+         */
+        $categoryRepo = app(VideoCategoryRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'category_uid' => 'nullable',
+            'name' => 'nullable',
+            'status' => 'nullable|integer',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+        // 注意,单例分页一定要克隆一个(保证前面没用使用到paginate);
+        $items = $categoryRepo->pushCriteria(new VideoCategoryPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+
+        return new VideoCategoryPagesResponse($items);
+    }
+
+    /**
+     * 分类详情
+     * @return \Illuminate\Http\JsonResponse|VideoCategoryDetailResponse
+     */
+    public function categoryDetail()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoCategoryRepository $categoryRepo
+         */
+        $categoryRepo = app(VideoCategoryRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'category_uid' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $category = $categoryRepo->findBy('category_uid', $inputData['category_uid']);
+
+        return new VideoCategoryDetailResponse($category);
+    }
+
+    /**
+     * 创建分类
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function categoryCreate()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoCategoryRepository $categoryRepo
+         */
+        $categoryRepo = app(VideoCategoryRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'name' => 'required|max:60',
+            'parent_uid' => 'nullable',
+            'squad' => 'nullable|integer',
+            'status' => 'nullable|integer',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        if (!empty($inputData['parent_uid'])) {
+            $parentUid = trim($inputData['parent_uid']);
+        } else {
+            $parentUid = '';
+        }
+        if (!isset($inputData['squad'])) {
+            $squad = 1000;
+        } else {
+            $squad = intval($inputData['squad']);
+        }
+        if (!empty($inputData['status'])) {
+            $status = intval($inputData['status']);
+        } else {
+            $status = 1;
+        }
+        $setarr = [
+            'name' => trim($inputData['name']),
+            'parent_uid' => $parentUid,
+            'squad' => $squad,
+            'status' => $status,
+        ];
+        $result = $categoryRepo->create($setarr);
+        if (!$result) {
+            return json_fail('创建失败');
+        } else {
+            $logSubject = '添加一个视频分类, 名称:' . $result->name;
+            $logSvalue = $result->category_uid;
+            $logDbData = [
+                'after_items' => [
+                    $result->toArray(),
+                ],
+                'before_items' => [
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($result->category_uid);
+        }
+    }
+
+    /**
+     * 修改分类
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function categoryModify()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoCategoryRepository $categoryRepo
+         */
+        $categoryRepo = app(VideoCategoryRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'category_uid' => 'required|string',
+            'name' => 'required|max:60',
+            'parent_uid' => 'nullable',
+            'squad' => 'nullable|integer',
+            'status' => 'nullable|integer',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $category = $categoryRepo->findBy('category_uid', $inputData['category_uid']);
+        if (empty($category)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldCategory = clone $category;
+        if (isset($inputData['name'])) {
+            $name = trim($inputData['name']);
+            $category->name = $name;
+        }
+        if (isset($inputData['parent_uid'])) {
+            $parentUid = trim($inputData['parent_uid']);
+            $category->parent_uid = $parentUid;
+        }
+        if (isset($inputData['squad'])) {
+            $squad = intval($inputData['squad']);
+            $category->squad = $squad;
+        }
+        if (isset($inputData['status'])) {
+            $status = intval($inputData['status']);
+            $category->status = $status;
+        }
+        $result = $category->save();
+        if (!$result) {
+            return json_fail('编辑失败');
+        } else {
+            $logSubject = '编辑一个视频分类, 名称:' . $category->name;
+            $logSvalue = $category->category_uid;
+            $logDbData = [
+                'after_items' => [
+                    $category->toArray(),
+                ],
+                'before_items' => [
+                    $oldCategory->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($category->category_uid);
+        }
+    }
+
+    /**
+     * 修改视频分类状态
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function categoryEditStatus()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+        $inputData = $this->getJsonData();
+        $rules = [
+            'category_uid' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        /**
+         * @var VideoCategoryRepository $categoryRepo
+         */
+        $categoryRepo = app(VideoCategoryRepository::class);
+        $category = $categoryRepo->findBy('category_uid', $inputData['category_uid']);
+        if (empty($category)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldCategory = clone $category;
+        $category->status = $category->status === 1 ? 2 : 1;
+
+        if (!$category->save()) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '修改视屏分类状态:' . $category->name;
+            $logSvalue = $category->category_uid;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldCategory->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+            return new CommonSuccessUidResponse($inputData['category_uid']);
+        }
+    }
+
+    /**
+     * 删除分类
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function categoryDelete()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoCategoryRepository $categoryRepo
+         */
+        $categoryRepo = app(VideoCategoryRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'category_uid' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $category = $categoryRepo->findBy('category_uid', $inputData['category_uid']);
+        if (empty($category)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldCategory = clone $category;
+        $result = $category->delete();
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个视频分类,名称:' . $oldCategory->username;
+            $logSvalue = $oldCategory->category_uid;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldCategory->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($inputData['category_uid']);
+        }
+    }
+
+    /**
+     * 批量删除视频分类
+     */
+    public function categoryBatchDelete()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+        $inputData = $this->getJsonData();
+        $rules = [
+            'category_uids' => 'required|string',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $categoryUidArray = explode(',', $inputData['category_uids']);
+        /**
+         * @var VideoCategoryRepository $categoryRepo
+         */
+        $categoryRepo = app(VideoCategoryRepository::class);
+        $categoryModel = clone $categoryRepo;
+        // 查询有UID的数据
+        $categoryKeys = $categoryRepo->findArray('category_uid', $categoryUidArray);
+        if (empty($categoryKeys)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        // 批量删除
+        $result = $categoryModel->delectUids('category_uid', $categoryKeys);
+        $categoryStr = implode(',', $categoryUidArray);
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '批量删除视频分类:' . $categoryStr;
+            $logSvalue = $categoryStr;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $categoryKeys,
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($inputData['category_uids']);
+        }
+    }
+
+    /**
+     * 批量操作分类
+     * @return \Illuminate\Http\JsonResponse|mixed
+     */
+    public function categoryBatch()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoCategoryRepository $categoryRepo
+         */
+        $categoryRepo = app(VideoCategoryRepository::class);
+
+        $inputData = $this->getJsonData();
+        $operateName = $inputData['operate_name'] ?? '';
+
+        switch ($operateName) {
+            case 'delete':
+                $itemPks = $inputData['item_pks'] ?? [];
+                if (!empty($itemPks)) {
+                    $model = new VideoCategoryModel();
+                    $resultItems = $model->whereIn('category_uid', $itemPks)->delete();
+                    if ($resultItems) {
+                        $strPks = implode(',', $itemPks);
+                        $logSubject = '批量删除多个视频分类';
+                        $logSvalue = $strPks;
+                        $logDbData = [
+                            'after_items' => [
+                            ],
+                            'before_items' => [
+                            ],
+                        ];
+                        $this->log($logSubject, $logSvalue, $logDbData);
+                    }
+                }
+
+                $data = [
+                    'result' => 'success',
+                    'pks' => $itemPks,
+                ];
+                return json_success($data);
+                break;
+            default:
+                break;
+        }
+
+        return json_fail('未知操作');
+    }
+
+    /**
+     * 所有视频分类
+     * @return \Illuminate\Http\JsonResponse|VideoCategoryPagesResponse
+     */
+    public function categoryAll()
+    {
+        /**
+         * @var VideoCategoryRepository $categoryRepo
+         */
+        $categoryRepo = app(VideoCategoryRepository::class);
+
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+        // 注意,单例分页一定要克隆一个(保证前面没用使用到paginate);
+        $items = $categoryRepo->pushCriteria(new OrderLimitCriteria($orders))->all();
+
+        return new CommonAllResponse($items);
+    }
+
+    /**
+     * 推送APP
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function pushApp()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'app_uid' => 'required|string',
+            'video_uids' => 'required|array',
+            'addon_params' => 'nullable|max:700',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        /**
+         * @var $appRepo AppRepository
+         */
+        $appRepo = make(AppRepository::class);
+        $item = $appRepo->findBy('app_uid', $inputData['app_uid']);
+        if ($item->count() == 0) {
+            return json_fail('无效应用');
+        }
+
+        $pushParams = [
+            'app_uid' => $inputData['app_uid'],
+            'video_uids' => $inputData['video_uids'],
+            'addon_params' => $inputData['addon_params'] ?? '',
+        ];
+        dispatch(new AgentUpdateChildUidsByAllParentUidsJob($pushParams));
+
+        $strVideoUids = is_array($inputData['video_uids']) ? implode(',', $inputData['video_uids']) : $inputData['video_uids'];
+        $logSubject = '推送一些视频至应用名称:' . $item->name;
+        $logSvalue = $strVideoUids;
+        $logDbData = [
+            'after_items' => [
+            ],
+            'before_items' => [
+            ],
+        ];
+        $this->log($logSubject, $logSvalue, $logDbData);
+
+        return new CommonSuccessUidResponse($item->app_uid);
+    }
+
+    public function tagLists()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoTagRepository $tagRepo
+         */
+        $tagRepo = app(VideoTagRepository::class);
+
+        /**
+         * @var VideoTagService $tagService
+         */
+        $tagService = app(VideoTagService::class);
+
+        $inputData = $this->getJsonData();
+
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'name' => 'nullable',
+            'origin' => 'nullable',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $orders = [
+            ['created_at', 'DESC'],
+        ];
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+        // 注意,单例分页一定要克隆一个(保证前面没用使用到paginate);
+        $items = $tagRepo->pushCriteria(new VideoTagPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))->paginate($pageSize, ['*'], $page);
+
+        return new CommonPagesResponse($items);
+    }
+
+    public function tagCreate()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoTagRepository $tagRepo
+         */
+        $tagRepo = app(VideoTagRepository::class);
+
+        /**
+         * @var VideoTagService $tagService
+         */
+        $tagService = app(VideoTagService::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'tags' => 'required',
+            'origin' => 'nullable',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        if (!empty($inputData['origin'])) {
+            $origin = trim($inputData['origin']);
+        } else {
+            $origin = '';
+        }
+        $result = $tagService->batchCreate($inputData['tags'], $origin);
+        if (empty($result['names'])) {
+            return json_fail('创建失败');
+        } else {
+            $strNames = implode(',', $result['names']);
+            $strIds = implode(',', $result['ids']);
+            $logSubject = '添加视频标签, 名称:' . mb_substr($strNames, 0, 255);
+            $logSvalue = $strIds;
+            $logDbData = [
+                'after_items' => [
+                ],
+                'before_items' => [
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            $data = [
+                'result' => 'success',
+                'pks' => $result['ids'],
+            ];
+            return json_success($data);
+        }
+    }
+
+    public function tagDetail()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoTagRepository $tagRepo
+         */
+        $tagRepo = app(VideoTagRepository::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $tag = $tagRepo->findBy('id', $inputData['id']);
+
+        return new CommonDetailResponse($tag);
+    }
+
+    public function tagModify()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoTagRepository $tagRepo
+         */
+        $tagRepo = app(VideoTagRepository::class);
+
+        /**
+         * @var VideoTagService $tagService
+         */
+        $tagService = app(VideoTagService::class);
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required',
+            'name' => 'required',
+            'origin' => 'nullable',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $name = trim(urldecode($inputData['name']));
+        $tag = $tagRepo->findBy('id', $inputData['id']);
+        if (empty($tag)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldTag = clone $tag;
+        if (isset($inputData['origin'])) {
+            if (!empty($inputData['origin'])) {
+                $origin = trim($inputData['origin']);
+            } else {
+                $origin = '';
+            }
+            $tag->origin = $origin;
+        }
+        if (!$tagService->isExists($name, $tag->id)) {
+            $tag->name = $name;
+            $result = $tag->save();
+        } else {
+            $result = false;
+        }
+        if (!$result) {
+            return json_fail('创建失败');
+        } else {
+            $logSubject = '编辑视频标签, 名称:' . $name;
+            $logSvalue = $inputData['id'];
+            $logDbData = [
+                'after_items' => [
+                    $tag->toArray(),
+                ],
+                'before_items' => [
+                    $oldTag->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessPkResponse($tag->id);
+        }
+    }
+
+    public function tagDelete()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoTagRepository $tagRepo
+         */
+        $tagRepo = app(VideoTagRepository::class);
+
+        /**
+         * @var VideoTagService $tagService
+         */
+        $tagService = app(VideoTagService::class);
+
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $tag = $tagRepo->findBy('id', $inputData['id']);
+        if (empty($tag)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldTag = clone $tag;
+        $result = $tag->delete();
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个视频标签,名称:' . $oldTag->name;
+            $logSvalue = $oldTag->id;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldTag->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessPkResponse($inputData['id']);
+        }
+    }
+
+    /**
+     * 批量删除标签
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessPkResponse
+     */
+    public function tagBatchDelete()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoTagRepository $tagRepo
+         */
+        $tagRepo = app(VideoTagRepository::class);
+
+        /**
+         * @var VideoTagService $tagService
+         */
+        $tagService = app(VideoTagService::class);
+
+
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'tagIds' => 'required',
+        ];
+        $validator = $validatorFactory->make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $tagModel = clone $tagRepo;
+        $tahIdsArray = explode(',', $inputData['tagIds']);
+        $tagIds = $tagRepo->findArray('id', $tahIdsArray);
+        if (empty($tagIds)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $tagIdsStr = implode(',', $tagIds);
+        if (!$tagModel->delectUids('id', $tagIds)) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '批量删除视频标签 id:' . $tagIdsStr;
+            $logSvalue = $tagIdsStr;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $tagIds,
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+            return new CommonSuccessPkResponse($inputData['tagIds']);
+        }
+    }
+
+    public function tagBatch()
+    {
+        /**
+         * @var Validator $validatorFactory
+         */
+        $validatorFactory = app('validator');
+
+        /**
+         * @var VideoTagRepository $tagRepo
+         */
+        $tagRepo = app(VideoTagRepository::class);
+
+        /**
+         * @var VideoTagService $tagService
+         */
+        $tagService = app(VideoTagService::class);
+
+        $inputData = $this->getJsonData();
+        $operateName = $inputData['operate_name'] ?? '';
+
+        switch ($operateName) {
+            case 'delete':
+                $itemPks = $inputData['item_pks'] ?? [];
+                if (!empty($itemPks)) {
+                    $model = new VideoModel();
+                    $resultItems = $model->whereIn('id', $itemPks)->delete();
+                    if ($resultItems) {
+                        $strPks = implode(',', $itemPks);
+                        $logSubject = '批量删除多个视频标签';
+                        $logSvalue = $strPks;
+                        $logDbData = [
+                            'after_items' => [
+                            ],
+                            'before_items' => [
+                            ],
+                        ];
+                        $this->log($logSubject, $logSvalue, $logDbData);
+                    }
+                }
+
+                $data = [
+                    'result' => 'success',
+                    'pks' => $itemPks,
+                ];
+                return json_success($data);
+                break;
+            default:
+                break;
+        }
+
+        return json_fail('未知操作');
+    }
+}

+ 291 - 0
Modules/ManageApi/Controllers/shareUrlsController.php

@@ -0,0 +1,291 @@
+<?php
+
+namespace Modules\ManageApi\Controllers;
+
+use App\Constants\CommonConstant;
+use App\Repositories\AppRepository;
+use App\Repositories\Criterias\OrderLimitCriteria;
+use App\Repositories\Criterias\ShareUrlsPagesCriteria;
+use App\Repositories\ShareUrlsRepository;
+use Illuminate\Support\Facades\Validator;
+use Modules\ManageApi\Responses\CommonSuccessUidResponse;
+use Modules\ManageApi\Responses\NotIncrementIdDetailResponse;
+use Modules\ManageApi\Responses\shareUrlsPagesResponse;
+
+class shareUrlsController extends BaseController
+{
+    public function lists(Validator $validatorFactory, ShareUrlsRepository $shareUrlsRepository)
+    {
+        $inputData = $this->getJsonData();
+        // 针对layui table自动分页请求参数,优先GET值.
+        $queryPage = request()->has('page') ? request()->input('page', 1) : null;
+        $queryPageSize = request()->has('page_size') ? request()->input('page_size', 10) : null;
+        if (!is_null($queryPage)) {
+            $inputData['page'] = $queryPage;
+        }
+        if (!is_null($queryPageSize)) {
+            $inputData['page_size'] = $queryPageSize;
+        }
+        $rules = [
+            'page' => 'nullable|integer',
+            'page_size' => 'nullable|integer',
+            'title' => 'nullable',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+        $pageSize = $inputData['page_size'] ?? CommonConstant::PAGE_SIZE;
+        if ($pageSize > 500) {
+            $pageSize = CommonConstant::PAGE_SIZE;
+        }
+        $page = format_page($inputData['page'] ?? 0);
+        $orders = [
+            ['sort', 'DESC'],
+        ];
+        $select = ['id', 'title', 'url', 'mom_app_uid', 'cover', 'sort', 'status', 'note', 'updated_at'];
+        $items = $shareUrlsRepository->pushCriteria(new ShareUrlsPagesCriteria($inputData))->pushCriteria(new OrderLimitCriteria($orders))
+            ->with(['appName'])->paginate($pageSize, $select, $page);
+        return new shareUrlsPagesResponse($items);
+    }
+
+    public function editStatus(Validator $validatorFactory, ShareUrlsRepository $shareUrlsRepository)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required|integer',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $shareUrlsRepository->findBy('id', $inputData['id']);
+        if (empty($app)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldApp = clone $app;
+        $app->status = $app->status === 1 ? 2 : 1;
+        $result = $app->save();
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '编辑一个代理分享url地址, 名称:' . $app->title;
+            $logSvalue = $app->uid;
+            $logDbData = [
+                'after_items' => [
+                    $app->toArray(),
+                ],
+                'before_items' => [
+                    $oldApp->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($app->id);
+        }
+    }
+
+
+    /**
+     * 添加地址
+     * @param Validator $validatorFactory
+     * @param ShareUrlsRepository $shareUrlsRepository
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     */
+    public function create(Validator $validatorFactory, ShareUrlsRepository $shareUrlsRepository)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'title' => 'required|string|max:120',
+            'url' => 'required|string|max:700',
+            'cover' => 'nullable|string|max:700',
+            'mom_app_uid' => 'required|max:16',
+            'note' => 'nullable|max:700',
+            'sort' => 'required|integer',
+            'status' => 'nullable|integer'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $setarr = [
+            'title' => $inputData['title'],
+            'url' => $inputData['url'],
+            'cover' => $inputData['cover'],
+            'mom_app_uid' => $inputData['mom_app_uid'],
+            'note' => $inputData['note'],
+            'sort' => $inputData['sort'],
+            'status' => $inputData['status'] ?? 1,
+        ];
+
+        $result = $shareUrlsRepository->create($setarr);
+        if (!$result) {
+            return json_fail('创建失败');
+        } else {
+            $logSubject = '添加一个代理分享url地址, 标题为:' . $result->title;
+            $logSvalue = $result->uid;
+            $logDbData = [
+                'after_items' => [
+                    $result->toArray(),
+                ],
+                'before_items' => [
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+            return new CommonSuccessUidResponse($result->id);
+        }
+    }
+
+    /**
+     * 地址详情
+     * @return \Illuminate\Http\JsonResponse|NotIncrementIdDetailResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function detail(Validator $validatorFactory, ShareUrlsRepository $shareUrlsRepository)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required|integer',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $shareUrlsRepository->findBy('id', $inputData['id']);
+
+        return new NotIncrementIdDetailResponse($app);
+    }
+
+
+    /**
+     * 编辑
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function modify(Validator $validatorFactory, ShareUrlsRepository $shareUrlsRepository)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required|integer',
+            'title' => 'required|string|max:120',
+            'url' => 'required|string|max:700',
+            'cover' => 'nullable|string|max:700',
+            'mom_app_uid' => 'required|max:16',
+            'note' => 'nullable|max:700',
+            'sort' => 'required|integer',
+            'status' => 'nullable|integer'
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $shareUrlsRepository->findBy('id', $inputData['id']);
+        if (empty($app)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $oldApp = clone $app;
+
+        if (isset($inputData['title'])) {
+            $app->title = trim($inputData['title']);
+        }
+
+        if (isset($inputData['url'])) {
+            $app->url = trim($inputData['url']);
+        }
+
+        if (isset($inputData['cover'])) {
+            $app->cover = trim($inputData['cover']);
+        }
+        if (isset($inputData['mom_app_uid'])) {
+            $app->mom_app_uid = trim($inputData['mom_app_uid']);
+        }
+        if (isset($inputData['note'])) {
+            $app->note = trim($inputData['note']);
+        }
+        if (isset($inputData['status'])) {
+            $app->status = trim($inputData['status']);
+        }
+        if (isset($inputData['sort'])) {
+            $app->sort = trim($inputData['sort']);
+        }
+
+        $result = $app->save();
+
+        if (!$result) {
+            return json_fail('修改失败');
+        } else {
+            $logSubject = '编辑一个代理分享url地址,标题:' . $app->title;
+            $logSvalue = $app->uid;
+            $logDbData = [
+                'after_items' => [
+                    $app->toArray(),
+                ],
+                'before_items' => [
+                    $oldApp->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($app->id);
+        }
+    }
+
+    /**
+     * 删除
+     * @return \Illuminate\Http\JsonResponse|CommonSuccessUidResponse
+     * @throws \Illuminate\Contracts\Container\BindingResolutionException
+     */
+    public function delete(Validator $validatorFactory, ShareUrlsRepository $shareUrlsRepository)
+    {
+        $inputData = $this->getJsonData();
+
+        $rules = [
+            'id' => 'required|integer',
+        ];
+        $validator = Validator::make($inputData, $rules);
+        if ($validator->fails()) {
+            // 如果有错误(可能是多个信息),这里只取第一个错误信息返回.
+            return json_fail($validator->errors()->first());
+        }
+
+        $app = $shareUrlsRepository->findBy('id', $inputData['id']);
+        if (empty($app)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+
+        $oldApp = clone $app;
+        $result = $app->delete();
+
+        if (!$result) {
+            return json_fail('删除失败');
+        } else {
+            $logSubject = '删除一个代理分享url地址,标题:' . $app->title;
+            $logSvalue = $app->uid;
+            $logDbData = [
+                'after_items' => [
+
+                ],
+                'before_items' => [
+                    $oldApp->toArray(),
+                ],
+            ];
+            $this->log($logSubject, $logSvalue, $logDbData);
+
+            return new CommonSuccessUidResponse($app->id);
+        }
+    }
+
+}

+ 123 - 0
Modules/ManageApi/Middlewares/ManageAuthMiddleware.php

@@ -0,0 +1,123 @@
+<?php
+/**
+ * 后台认证中间件.
+ *
+ * @author nj
+ * @date   2019-11-03 14:07:35
+ */
+
+namespace Modules\ManageApi\Middlewares;
+
+use App\Constants\ErrorCodeConstant;
+use Closure;
+use App\Repositories\Utils\AccessControllerListsRepository;
+use App\Repositories\Utils\JwtRepository;
+use Modules\ManageApi\Services\ManageAuthService;
+use Cache;
+
+class ManageAuthMiddleware
+{
+    protected $except = [];   //URI里排除的操作
+
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle($request, Closure $next)
+    {
+        if ($this->shouldPassThrough($request)) {
+            return $next($request);
+        }
+        /**
+         * @var ManageAuthService $authService
+         */
+        $authService = app(ManageAuthService::class);
+        $token = $authService->getHeaderToken();
+        if (empty($token)) {
+            return json_fail(trans('common.err_failed_authorization'), 403);
+        }
+        /**
+         * @var JwtRepository $jwtRepo
+         */
+        $jwtRepo = make(JwtRepository::class);
+        $aud = 'manage-users';
+        $validateResult = $jwtRepo->validateToken($token, $aud);
+        if (!$validateResult['is_valid']) {
+            return json_fail(trans('common.err_failed_authorization'), 403);
+        } else {
+            if ($validateResult['is_expired']) {
+                return json_fail('认证过期', 401);
+            }
+            // 验证单点.
+            $claim = $validateResult['token_obj']->claims();
+            $tokenManagerUid = $claim->get('manager_uid');
+            $tokenIp = $claim->get('ip');
+            $clientIp = $jwtRepo->getClientIp();
+            $cacheKey = $authService->getManagerCacheKey($tokenManagerUid);
+            /**
+             * @var Cache $cache
+             */
+            $cache = make('cache');
+            $cacheToken = $cache->has($cacheKey) ? $cache->get($cacheKey) : '';
+            if (empty($cacheToken) || $cacheToken !== $token) {
+                return json_fail(trans('common.err_failed_authorization'), 403);
+            }
+            if (empty($tokenIp) || empty($clientIp) || $tokenIp !== $clientIp) {
+                return json_fail(trans('common.err_failed_authorization'), 403);
+            }
+        }
+        $managerInfo = $authService->getManagerInfo();
+        $roleId = $managerInfo['role_id'];
+        $managerUid = $managerInfo['manager_uid'];
+        $authConfig = config('manage_acl');
+        /**
+         * @var AccessControllerListsRepository $aclRepo
+         */
+        $aclRepo = make(AccessControllerListsRepository::class);
+        $aclRepo->init($authConfig, $roleId);
+        $aclStatus = $aclRepo->check('Controller');
+
+        if ($aclStatus !== true) {
+            return json_fail('无权访问', ErrorCodeConstant::NO_PERMISSION);
+        }
+
+        return $next($request);
+    }
+
+    private function getExcepts()
+    {
+        $uriPrefix = env('MANAGE_API_URI_PREFIX', 'manage_api');
+        $this->except = [
+            $uriPrefix . '/pong',
+            $uriPrefix .'/auth/login',
+            $uriPrefix . '/auth/refresh/token',
+            $uriPrefix . '/auth/logout',
+        ];
+
+        return $this->except;
+    }
+
+    /**
+     * Determine if the request has a URI that should pass through CSRF verification.
+     *
+     * @param  \Illuminate\Http\Request $request
+     * @return bool
+     */
+    protected function shouldPassThrough($request)
+    {
+        foreach ($this->getExcepts() as $except) {
+            if ($except !== '/') {
+                $except = trim($except, '/');
+            }
+
+            if ($request->is($except)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 57 - 0
Modules/ManageApi/Requests/BaseRequest.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:25
+ */
+namespace Modules\ManageApi\Requests;
+
+use App\Exceptions\ApiException;
+use Illuminate\Foundation\Http\FormRequest;
+use \Illuminate\Contracts\Validation\Validator;
+
+class BaseRequest extends FormRequest
+{
+
+    /**
+     * Determine if the user is authorized to make this request.
+     *
+     * @return bool
+     */
+    public function authorize()
+    {
+        //默认放行auth校验
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        return [
+            //
+        ];
+    }
+
+    /**
+     * 重定义校验失败
+     * @param Validator $validator
+     * @throws ApiException
+     */
+    protected function failedValidation(Validator $validator)
+    {
+        throw new ApiException($validator->messages()->first(), 500);
+    }
+
+    /**
+     * 重定义认证失败
+     * @throws ApiException
+     */
+    protected function failedAuthorization()
+    {
+        throw new ApiException('认证错误', 403);
+    }
+}

+ 49 - 0
Modules/ManageApi/Responses/AgentPagesResponse.php

@@ -0,0 +1,49 @@
+<?php
+/**
+ * 代理分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 16:19:51
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+use Illuminate\Support\Facades\Auth;
+
+class AgentPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                unset($v['id']);
+            }
+            unset($v['id'], $v['password'], $v['salt'], $v['otp_screct'], $v['withdraw_pwd']);
+        }
+
+        return json_success($result);
+    }
+}

+ 43 - 0
Modules/ManageApi/Responses/AnnouncePagesResponse.php

@@ -0,0 +1,43 @@
+<?php
+
+
+namespace Modules\ManageApi\Responses;
+
+
+use Illuminate\Contracts\Support\Responsable;
+
+class AnnouncePagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse($request = null)
+    {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                unset($v['id']);
+            }
+            unset($v['id']);
+        }
+
+        return json_success($result);
+    }
+}

+ 42 - 0
Modules/ManageApi/Responses/AppDeductionRuleResponse.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class AppDeductionRuleResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+
+    public function __construct($data = null)
+    {
+        $this->data = $data;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                $this->unsetField($v);
+            }
+            $this->unsetField($v);
+        }
+
+        return json_success($result);
+    }
+
+    private function unsetField(&$v)
+    {
+        unset($v['id']);
+        unset($v['deduction_type']);
+        unset($v['app_uid']);
+    }
+}

+ 37 - 0
Modules/ManageApi/Responses/AppPagesResponse.php

@@ -0,0 +1,37 @@
+<?php
+
+
+namespace Modules\ManageApi\Responses;
+
+
+use Illuminate\Contracts\Support\Responsable;
+
+class AppPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse($request = null)
+    {
+        $result = $this->data->toArray();
+
+        return json_success($result);
+    }
+}

+ 37 - 0
Modules/ManageApi/Responses/CommonAllResponse.php

@@ -0,0 +1,37 @@
+<?php
+
+
+namespace Modules\ManageApi\Responses;
+
+
+use Illuminate\Contracts\Support\Responsable;
+
+class CommonAllResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse($request = null)
+    {
+        $result = $this->data->toArray();
+
+        return json_success($result);
+    }
+}

+ 53 - 0
Modules/ManageApi/Responses/CommonDetailResponse.php

@@ -0,0 +1,53 @@
+<?php
+/**
+ * 通用详情返回封装.
+ *
+ * @author nj
+ * @date   2021-01-24 02:20:30
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Arrayable;
+use Illuminate\Contracts\Support\Responsable;
+
+class CommonDetailResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    )
+    {
+        if (empty($this->data)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+
+        if (is_array($this->data)) {
+            $result = $this->data;
+        } else {
+            $result = $this->data->toArray();
+        }
+
+
+        return json_success($result);
+    }
+}

+ 42 - 0
Modules/ManageApi/Responses/CommonPagesResponse.php

@@ -0,0 +1,42 @@
+<?php
+/**
+ * 通用分页响应.
+ *
+ * @author nj
+ * @date   2021-01-24 00:08:44
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class CommonPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+
+        return json_success($result);
+    }
+}

+ 45 - 0
Modules/ManageApi/Responses/CommonSuccessPkResponse.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * 通用返回成功.
+ *
+ * @author nj
+ * @date   2021-01-24 01:11:06
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class CommonSuccessPkResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $pk = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($pk = null, $params = [])
+    {
+        $this->pk = $pk;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = [
+            'result' => 'success',
+            'id' => $this->pk,
+        ];
+
+        return json_success($result);
+    }
+}

+ 45 - 0
Modules/ManageApi/Responses/CommonSuccessUidResponse.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * 通用返回成功.
+ *
+ * @author nj
+ * @date   2021-01-24 01:11:06
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class CommonSuccessUidResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $uid = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($uid = null, $params = [])
+    {
+        $this->uid = $uid;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = [
+            'result' => 'success',
+            'uid' => $this->uid,
+        ];
+
+        return json_success($result);
+    }
+}

+ 29 - 0
Modules/ManageApi/Responses/ConfigSuccessResponse.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class ConfigSuccessResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $uid = null;
+
+    public function __construct($skey = null)
+    {
+        $this->skey = $skey;
+    }
+
+    public function toResponse($request = null)
+    {
+        $result = [
+            'result' => 'success',
+        ];
+
+        return json_success($result);
+    }
+}

+ 46 - 0
Modules/ManageApi/Responses/FinanceRechargePageResponse.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class FinanceRechargePageResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 充值统计数据
+     *
+     * @var array|mixed
+     */
+    private $recTotal = [];
+
+
+    public function __construct($data = null, $recTotal = [])
+    {
+        $this->data = $data;
+
+        $this->recTotal = $recTotal;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                unset($v['_id']);
+            }
+            unset($v['_id']);
+        }
+
+        $result['rec_total'] = $this->recTotal;
+
+        return json_success($result);
+    }
+}

+ 46 - 0
Modules/ManageApi/Responses/FinanceWithdrawResponse.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class FinanceWithdrawResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 提现统计数据
+     *
+     * @var array|mixed
+     */
+    private $recTotal = [];
+
+    public function __construct($data = null, $witTotal = [])
+    {
+        $this->data = $data;
+
+        $this->witTotal = $witTotal;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        if (!$this->data->isEmpty()) {
+            foreach ($this->data as &$v) {
+                $v->agent_name = $v->agent->username ?? '';
+                $v->commission_ratio = $v->agent->commission_ratio ?? '';
+                unset($v->agent);
+            }
+        }
+
+        $result = $this->data->toArray();
+        $result['wit_total'] = $this->recTotal;
+
+        return json_success($result);
+    }
+}

+ 51 - 0
Modules/ManageApi/Responses/ManagerDetailResponse.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * 管理员详情返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 17:36:29
+ */
+
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class ManagerDetailResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        if (empty($this->data)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $result = $this->data->toArray();
+        if (isset($result['role']['permissions']) && is_string($result['role']['permissions'])) {
+            $arrPermissions = !empty($result['role']['permissions']) ? explode(',', $result['role']['permissions']) : [];
+            $result['role']['permissions'] = $arrPermissions;
+        }
+        unset($result['id']);
+
+        return json_success($result);
+    }
+}

+ 48 - 0
Modules/ManageApi/Responses/ManagerPagesResponse.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * 管理员分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 16:19:51
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class ManagerPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                unset($v['id']);
+            }
+            unset($v['id']);
+        }
+
+        return json_success($result);
+    }
+}

+ 48 - 0
Modules/ManageApi/Responses/NotIncrementIdDetailResponse.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * 管理员分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 16:19:51
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Arrayable;
+use Illuminate\Contracts\Support\Responsable;
+
+class NotIncrementIdDetailResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        if ($this->data instanceof Arrayable) {
+            $result = $this->data->toArray();
+        } else {
+            $result = $this->data;
+        }
+        unset($result['id']);
+
+        return json_success($result);
+    }
+}

+ 48 - 0
Modules/ManageApi/Responses/NotIncrementIdPagesResponse.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * 管理员分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 16:19:51
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class NotIncrementIdPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                unset($v['id']);
+            }
+            unset($v['id']);
+        }
+
+        return json_success($result);
+    }
+}

+ 51 - 0
Modules/ManageApi/Responses/PermissionRoleDetailResponse.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * 角色详情.
+ *
+ * @author nj
+ * @date   2021-01-24 02:23:36
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class PermissionRoleDetailResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        if (empty($this->data)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $result = $this->data->toArray();
+        $strPermissions = $result['permissions'];
+        $arrPermissions = [];
+        if (!empty($strPermissions)) {
+            $arrPermissions = explode(',', $strPermissions);
+        }
+        $result['permissions'] = $arrPermissions;
+
+        return json_success($result);
+    }
+}

+ 52 - 0
Modules/ManageApi/Responses/PermissionRolePagesResponse.php

@@ -0,0 +1,52 @@
+<?php
+/**
+ * 角色分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-24 02:26:39
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class PermissionRolePagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                $strPermissions = $v['permissions'];
+                $arrPermissions = [];
+                if (!empty($strPermissions)) {
+                    $arrPermissions = explode(',', $strPermissions);
+                }
+                $v['permissions'] = $arrPermissions;
+            }
+        }
+
+        return json_success($result);
+    }
+}

+ 45 - 0
Modules/ManageApi/Responses/SuccessIdResponse.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:57
+ */
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class SuccessIdResponse implements Responsable
+{
+    protected $data = null;
+
+    private $params = [];
+
+    /**
+     * 构造
+     * GetTokenResponse constructor.
+     * @param $data
+     * @param $params
+     */
+    public function __construct($data, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    /**
+     * 响应结果
+     *
+     * @param \Illuminate\Http\Request $request
+     * @return array
+     */
+    public function toResponse($request)
+    {
+        $result = [];
+        $data = is_object($this->data) ? $this->data->toArray() : $this->data;
+        if (!empty($data['id'])) {
+            $result['id'] = $data['id'];
+        }
+
+        return json_success($result);
+    }
+}

+ 48 - 0
Modules/ManageApi/Responses/SystemLogPagesResponse.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * 管理日志分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 16:19:51
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class SystemLogPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                $arrDbData = !empty($v['db_data']) ? json_decode($v['db_data'], true) : [];
+                $v['db_data'] = $arrDbData;
+            }
+        }
+
+        return json_success($result);
+    }
+}

+ 47 - 0
Modules/ManageApi/Responses/VideoCategoryDetailResponse.php

@@ -0,0 +1,47 @@
+<?php
+/**
+ * 视频分类详情返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 17:36:29
+ */
+
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class VideoCategoryDetailResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        if (empty($this->data)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $result = $this->data->toArray();
+        unset($result['id']);
+
+        return json_success($result);
+    }
+}

+ 48 - 0
Modules/ManageApi/Responses/VideoCategoryPagesResponse.php

@@ -0,0 +1,48 @@
+<?php
+/**
+ * 管理员分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 16:19:51
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class VideoCategoryPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                unset($v['id']);
+            }
+            unset($v['id']);
+        }
+
+        return json_success($result);
+    }
+}

+ 57 - 0
Modules/ManageApi/Responses/VideoDetailResponse.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * 视频分类详情返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 17:36:29
+ */
+
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class VideoDetailResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        if (empty($this->data)) {
+            return json_fail(trans('common.err_resource_not_found'));
+        }
+        $result = $this->data->toArray();
+        if (!empty($result['tags'])) {
+            $result['tags'] = explode(',', $result['tags']);  //单独处理为数组.
+        } else {
+            $result['tags'] = [];
+        }
+        if (!empty($result['screenshots'])) {
+            $result['screenshots'] = explode(',', $result['screenshots']);  //单独处理为数组.
+        } else {
+            $result['screenshots'] = [];
+        }
+        unset($result['id']);
+
+        return json_success($result);
+    }
+}

+ 58 - 0
Modules/ManageApi/Responses/VideoPagesResponse.php

@@ -0,0 +1,58 @@
+<?php
+/**
+ * 管理员分页返回封装.
+ *
+ * @author nj
+ * @date   2021-01-23 16:19:51
+ */
+
+namespace Modules\ManageApi\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class VideoPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse(
+        $request = null
+    ) {
+        $result = $this->data->toArray();
+        if (!empty($result['data'])) {
+            foreach ($result['data'] as &$v) {
+                if (!empty($v['tags'])) {
+                    $v['tags'] = explode(',', $v['tags']);  //单独处理为数组.
+                } else {
+                    $v['tags'] = [];
+                }
+                if (!empty($v['screenshots'])) {
+                    $v['screenshots'] = explode(',', $v['screenshots']);  //单独处理为数组.
+                } else {
+                    $v['screenshots'] = [];
+                }
+                unset($v['id']);
+            }
+            unset($v['id']);
+        }
+
+        return json_success($result);
+    }
+}

+ 37 - 0
Modules/ManageApi/Responses/shareUrlsPagesResponse.php

@@ -0,0 +1,37 @@
+<?php
+
+
+namespace Modules\ManageApi\Responses;
+
+
+use Illuminate\Contracts\Support\Responsable;
+
+class shareUrlsPagesResponse implements Responsable
+{
+    /**
+     * 录入的数据.
+     *
+     * @var mixed
+     */
+    private $data = null;
+
+    /**
+     * 附加条件参数.
+     *
+     * @var array
+     */
+    private $params = [];
+
+    public function __construct($data = null, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    public function toResponse($request = null)
+    {
+        $result = $this->data->toArray();
+
+        return json_success($result);
+    }
+}

+ 12 - 0
Modules/ManageApi/Services/DemoService.php

@@ -0,0 +1,12 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:59
+ */
+namespace Modules\ManageApi\Services;
+
+class DemoService
+{
+
+}

+ 112 - 0
Modules/ManageApi/Services/ManageAuthService.php

@@ -0,0 +1,112 @@
+<?php
+/**
+ * 认证服务.
+ *
+ * @author nj
+ * @date   2021-01-22 11:36:11
+ */
+
+namespace Modules\ManageApi\Services;
+
+use App\Constants\CommonConstant;
+use App\Constants\ManageConstant;
+use App\Repositories\Utils\JwtRepository;
+use Psr\SimpleCache\CacheInterface;
+use Cache;
+use Str;
+
+class ManageAuthService
+{
+    /**
+     * Jwt受众.
+     *
+     * @var string
+     */
+    private $jwtAud = 'manage-users';
+
+    /**
+     * 获取存储用户设备Token的缓存Key.
+     * @param string $managerUid
+     * @return string
+     */
+    public function getManagerCacheKey($managerUid)
+    {
+        $cacheKey = 'manager_auth_'. $managerUid;
+
+        return $cacheKey;
+    }
+
+    /**
+     * 创建单点Jwt.
+     * @param string $managerUid 管理员UID
+     * @param int $roleId 角色ID
+     * @return array
+     * @throws \Psr\SimpleCache\InvalidArgumentException
+     */
+    public function createSingleJwt($managerUid, $roleId)
+    {
+        $manager = new \stdClass();
+        $manager->manager_uid = $managerUid;
+        $manager->role_id = $roleId;
+        /**
+         * @var JwtRepository $jwtRepository
+         */
+        $jwtRepository = make(JwtRepository::class);
+        $tokenInfo = $jwtRepository->createManageJwt($manager);
+        $token = $tokenInfo['token'];
+
+        $cacheKey = $this->getManagerCacheKey($managerUid);
+        /**
+         * @var Cache $cache
+         */
+        $cache = make('cache');
+        $cacheToken = $cache->has($cacheKey) ? $cache->get($cacheKey) : '';
+        if (!empty($cacheToken)) {
+            $cache->delete($cacheKey);
+        }
+        $cache->set($cacheKey, $token, config('jwt.ttl') * 60);
+
+        return $tokenInfo;
+    }
+
+    /**
+     * 获取头部Token.
+     * @return mixed
+     */
+    public function getHeaderToken()
+    {
+        $token = request()->header(ManageConstant::MG_TOKEN_NAME, null);
+        if (empty($token)) {
+            // 支持GET/PUT获取token
+            $token = request()->input(ManageConstant::MG_TOKEN_NAME, null);
+        }
+
+        return $token;
+    }
+
+    /**
+     * 获取Token中的ManagerInfo
+     * @return array  返回manager_uid, role_id
+     */
+    public function getManagerInfo()
+    {
+        $managerInfo = [];
+        $token = $this->getHeaderToken();
+        if (!empty($token)) {
+            /**
+             * @var JwtRepository $jwtRepository
+             */
+            $jwtRepository = make(JwtRepository::class);
+            $validateResult = $jwtRepository->validateToken($token, $this->jwtAud);
+            if ($validateResult['is_valid']) {
+                $claim = $validateResult['token_obj']->claims();
+                $managerInfo = [
+                    'manager_uid' => !empty($claim->get('manager_uid')) ? $claim->get('manager_uid') : '',
+                    'role_id' => !empty($claim->get('role_id')) ? $claim->get('role_id') : 0,
+                ];
+            }
+        }
+
+        return $managerInfo;
+    }
+}

+ 78 - 0
Modules/ManageApi/Services/VideoTagService.php

@@ -0,0 +1,78 @@
+<?php
+
+
+namespace Modules\ManageApi\Services;
+
+use App\Models\Mysql\VideoTagModel;
+use App\Repositories\VideoTagRepository;
+
+class VideoTagService
+{
+    /**
+     * 批量创建.
+     * @param $tags
+     * @param string $origin
+     * @return array
+     */
+    public function batchCreate($tags, $origin = '')
+    {
+        $result = [
+            'names' => [],
+            'ids' => [],
+        ];
+        if (is_string($tags)) {
+            $tags = explode(',', $tags);
+        }
+        $tags = array_unique($tags);    // 去重
+        $tags = array_filter($tags);    // 去空
+        if (!empty($tags)) {
+            foreach ($tags as $tag) {
+                $item = self::createOne($tag, $origin);
+                if ($item) {
+                    $result['names'][] = $item->name;
+                    $result['ids'][] = $item->id;
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * 创建单个.
+     * @param $tag
+     * @param string $origin
+     * @return mixed
+     */
+    public function createOne($tag, $origin = '')
+    {
+        $isExists = $this->isExists($tag);
+        if (!$isExists) {
+            $setarr = [
+                'name' => $tag,
+                'origin' => $origin,
+            ];
+            $repo = app(VideoTagRepository::class);
+            return $repo->create($setarr);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 判断是否存在.
+     * @param $currentName
+     * @param string $excludeId
+     * @return bool
+     */
+    public function isExists($currentName, $excludeId = '')
+    {
+        $model = new VideoTagModel();
+        $model = $model->where(['name' => $currentName]);
+        if (!empty($excludeId)) {
+            $model = $model->where('id', '!=', $excludeId);
+        }
+
+        return $model->exists();
+    }
+}

+ 17 - 0
Modules/Web/Controllers/BaseController.php

@@ -0,0 +1,17 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:24
+ */
+namespace Modules\Web\Controllers;
+
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+use Illuminate\Foundation\Bus\DispatchesJobs;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Routing\Controller as RoutingController;
+
+class BaseController extends RoutingController
+{
+    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+}

+ 57 - 0
Modules/Web/Requests/BaseRequest.php

@@ -0,0 +1,57 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:25
+ */
+namespace Modules\Web\Requests;
+
+use App\Exceptions\BizException;
+use Illuminate\Foundation\Http\FormRequest;
+use \Illuminate\Contracts\Validation\Validator;
+
+class BaseRequest extends FormRequest
+{
+
+    /**
+     * Determine if the user is authorized to make this request.
+     *
+     * @return bool
+     */
+    public function authorize()
+    {
+        //默认放行auth校验
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        return [
+            //
+        ];
+    }
+
+    /**
+     * 重定义校验失败
+     * @param Validator $validator
+     * @throws BizException
+     */
+    protected function failedValidation(Validator $validator)
+    {
+        throw new BizException($validator->messages()->first(), 500);
+    }
+
+    /**
+     * 重定义认证失败
+     * @throws BizException
+     */
+    protected function failedAuthorization()
+    {
+        throw new BizException('认证错误', 403);
+    }
+}

+ 39 - 0
Modules/Web/Responses/DemoResponse.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:57
+ */
+namespace Modules\Web\Responses;
+
+use Illuminate\Contracts\Support\Responsable;
+
+class DemoResponse implements Responsable
+{
+    protected $data = null;
+
+    private $params = [];
+
+    /**
+     * 构造
+     * GetTokenResponse constructor.
+     * @param $data
+     * @param $params
+     */
+    public function __construct($data, $params = [])
+    {
+        $this->data = $data;
+        $this->params = $params;
+    }
+
+    /**
+     * 响应结果
+     *
+     * @param \Illuminate\Http\Request $request
+     * @return mixed
+     */
+    public function toResponse($request)
+    {
+        return response()->noContent();
+    }
+}

+ 12 - 0
Modules/Web/Services/DemoService.php

@@ -0,0 +1,12 @@
+<?php
+/**
+ * 描述
+ * @auth nj911<nj-x@gmail.com>
+ * @date: 2021-04-18 12:59
+ */
+namespace Modules\Web\Services;
+
+class DemoService
+{
+
+}

+ 150 - 2
README.md

@@ -1,3 +1,151 @@
-# agent_server
+# 多应用代理系统
+
+## 技术
+- Laravel 6.x [中文6.x文档](https://learnku.com/docs/laravel/6.x)
+
+## 环境依赖
+- php8.0
+- composer
+- ext-BCMath
+- ext-Ctype
+- ext-JSON
+- ext-Mbstring
+- ext-OpenSSL
+- ext-PDO
+- ext-Tokenizer
+- ext-XML
+- ext-redis
+- ext-mongodb
+
+## 项目初始化步骤
+1. 下载laravel指定版本 https://github.com/laravel/laravel/archive/refs/tags/v6.20.1.zip
+2. 安装laravel依赖
+```shell
+composer install -vvv
+```
+3. 复制本地.env文件
+```shell
+cp .env.example .env
+```
+4. 生产laravel 密钥
+```shell
+php artisan key:generate
+
+5. .gitignore 追加忽略
+6. 安装本地开发工具barryvdh/laravel-ide-helper
+```shell
+composer require --dev "barryvdh/laravel-ide-helper:v2.8.2" -vvv
+```
+
+9. 修改app/Providers/AppServiceProvider.php,注册启动时根据环境启用laravel-ide-helper
+```php
+
+public function register()
+    {
+        if ($this->app->isLocal()) {
+            $this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
+        }
+    }
+
+```
+
+10. 安装其他扩展包
+```shell
+composer require "zhuzhichao/ip-location-zh:^2.4" -vvv
+composer require "jenssegers/mongodb:^3.6" -vvv
+composer require "intervention/image" -vvv
+composer require "christian-riesen/otp:^2.7" -vvv
+# https://github.com/vinkla/hashids
+composer require "hashids/hashids" -vvv
+composer require guzzlehttp/guzzle -vvv
+composer require lcobucci/jwt:4.1.5 -vvv
+composer require overtrue/laravel-pinyin -vvv
+composer require "jacobcyl/ali-oss-storage:^2.1" -vvv
+# https://docs.laravel-excel.com/3.1/getting-started/installation.html
+composer require "maatwebsite/excel:^3.1" -vvv
+# https://github.com/mewebstudio/captcha
+composer require "mews/captcha" -vvv
+```
+11. 执行数据库初始化.
+```shell
+php artisan migrate:install
+php artisan cache:table
+php artisan notifications:table
+php artisan queue:table 
+php artisan session:table
+# 最后migrate:fresh, 根据migration生成sql数据库相关表
+php artisan migrate:fresh
+php artisan ide-helper:generate
+php artisan ide-helper:meta
+# php artisan vendor:publish --tag="cors"
+php artisan vendor:publish --provider='Mews\Captcha\CaptchaServiceProvider'
+```
+
+## 本地调试服务
+```shell
+php artisan serve --host=0.0.0.0 --port=8601
+# 本地处理异步队列.
+php artisan queue:listen
+
+# 权限管理DB更新.
+php artisan permission:resource
+```
+
+## 规范
+### 代码命名规范
+1. 必须使用SANGUO CodeSniffer检测脚本,进行代码PSR-2规范
+2. 目录、文件命名,必须满足PSR-4规范
+3. Windows下,由于同文件或者目录名大小写书写错误,请重写不同命名进行2次commit提交
+4. 所有代码文件,文件编码必须是utf-8,其次换行符标准LF(` Mac/Linux \n ` )
+5. 业务代码逻辑相对复杂的,约定要用注释写清楚关键信息.
+6. 所有方法,尽可能注释详细(名称、类型、备注等)
+
+### .env规范
+1. .env.example,所有增加的变量,都必须先放这里
+2. 不能存放自己开发、测试、生产环境的env提交到git
+
+### 数据库规范
+1. 数据库名、表名、字段名等,必须使用蛇形半角小写风格。例如`prefix_order_logs`
+2. 数据库表业务设计必须遵从,禁止超过3张表关联
+3. MySql数据库统一引擎InnoDB,字符集`utf8mb4`,字符编码`utf8mb4_unicode_ci`
+4. 生产环境数据库账号信息,务必独立分配账号和归属schema,禁止使用root等超级用户权限.
+5. MySql时间类型字段,统一使用`datetime(0)`,存储时间格式`Y-m-d H:i:s`
+6. 业务有对外暴露的ID,务必设计成 自增ID+自定义唯一ID,禁止暴露吐出自增ID.
+7. MySql增量sql,请放置在`storage/sql/inc/`目录下,命名规则`[database name]_inc_[日期Y-m-d].sql`
+
+### 接口规范
+1. 返回数据各下标节点名,一律使用蛇形半角小写风格.
+2. 规避返回无效(没有被使用)的字段以及数据.在业务层处理过滤.
+3. 不需要严格按照RESTFUL接口方法约定规范,但如果是http-api模式,除了特殊接口,一律统一使用post请求方法.
+4. 所有接口必须提交到Yapi平台,测试通过后,勾选状态完成可用.
+
+## 项目特殊
+
+## 部署
+1. 【第一次】cp .env.example .env
+2. 【第一次】修改好.env里的配置
+3. 【第一次】chmod -R 777 ./public/attachment ./storage/
+4. 【每次】rm -f ./composer.lock
+5. 【每次】composer install -vvv
+6. 【第一次】确认.env 中的`QUEUE_CONNECTION=redis`
+7. 【第一次】在计划任务www-data里增加 `* * * * * /usr/local/bin/php /var/www/agent_server/artisan schedule:run >> /dev/null 2>&1`
+8. 【第一次】在supervisor配置里增加 `/usr/local/bin/php /var/www/agent_server/artisan queue:work --daemon --sleep=1 --tries=1`, 是`user=www-data`
+
+
+## 测试说明
+
+提前准备
+1. redis 清空
+2. mongo 相关集合清空
+3. mysql 数据初始化 (full -> backup -> full)
+
+1. 造数据
+   php -d memory_limit=100M artisan db:seed --class=GenerateTestSeeder
+2. 统计1
+   php artisan statistics:pay --date=2021-08-01
+   php artisan statistics:new_users --date=2021-08-01
+   php artisan statistics:active --date=2021-08-01
+   php artisan statistics:users_attrs --date=2021-08-01
+3. 统计2
+   php artisan statistics:users_attrs_today --date=2021-08-01
 
-代理后台

Неке датотеке нису приказане због велике количине промена