Browse Source

正式服代码首次提交

王大坤 1 year ago
commit
a9c8392d8b
100 changed files with 8769 additions and 0 deletions
  1. BIN
      .DS_Store
  2. 94 0
      README.md
  3. 82 0
      _ide_helper_models.php
  4. BIN
      app/.DS_Store
  5. 52 0
      app/Admin/Actions/Form/BatchEditEpisodeForm.php
  6. 52 0
      app/Admin/Actions/Form/BatchEditEpisodeListForm.php
  7. 30 0
      app/Admin/Actions/Form/UserRechargeRemark.php
  8. 55 0
      app/Admin/Actions/Form/WithdrawReview.php
  9. 36 0
      app/Admin/Actions/Grid/BatchEditEpisode.php
  10. 36 0
      app/Admin/Actions/Grid/BatchEditEpisodeList.php
  11. 50 0
      app/Admin/Actions/Grid/UserConsumeRecord.php
  12. 53 0
      app/Admin/Actions/Grid/UserEpisodeRecord.php
  13. 54 0
      app/Admin/Actions/Grid/UserRechargeRecord.php
  14. 39 0
      app/Admin/Actions/Grid/UserRechargeRemark.php
  15. 35 0
      app/Admin/Actions/Grid/WithdrawReview.php
  16. 72 0
      app/Admin/Controllers/AiCountController.php
  17. 94 0
      app/Admin/Controllers/ApplyController.php
  18. 9 0
      app/Admin/Controllers/AuthController.php
  19. 113 0
      app/Admin/Controllers/BannerController.php
  20. 162 0
      app/Admin/Controllers/Episode/EpisodeController.php
  21. 94 0
      app/Admin/Controllers/Episode/EpisodesBatchUploadController.php
  22. 86 0
      app/Admin/Controllers/Episode/EpisodesCategoryController.php
  23. 150 0
      app/Admin/Controllers/Episode/EpisodesListController.php
  24. 85 0
      app/Admin/Controllers/GiveawayController.php
  25. 30 0
      app/Admin/Controllers/HomeController.php
  26. 67 0
      app/Admin/Controllers/KeywordController.php
  27. 74 0
      app/Admin/Controllers/Market/RechargeComboController.php
  28. 102 0
      app/Admin/Controllers/Market/UserRechargeRecordController.php
  29. 78 0
      app/Admin/Controllers/Market/VipComboController.php
  30. 105 0
      app/Admin/Controllers/Order/UserConsumeRecordController.php
  31. 108 0
      app/Admin/Controllers/Order/UserEpisodesRecordController.php
  32. 107 0
      app/Admin/Controllers/Order/UserRechargeRecordController.php
  33. 110 0
      app/Admin/Controllers/OrderController.php
  34. 83 0
      app/Admin/Controllers/OtherController.php
  35. 94 0
      app/Admin/Controllers/PaymentConfigController.php
  36. 83 0
      app/Admin/Controllers/Program/BannerController.php
  37. 56 0
      app/Admin/Controllers/Program/HomeColumnController.php
  38. 14 0
      app/Admin/Controllers/Program/HomeController.php
  39. 58 0
      app/Admin/Controllers/Program/NavBarController.php
  40. 63 0
      app/Admin/Controllers/Program/TabbarController.php
  41. 69 0
      app/Admin/Controllers/PromotionController.php
  42. 67 0
      app/Admin/Controllers/ProtocolController.php
  43. 75 0
      app/Admin/Controllers/RecommendationsController.php
  44. 82 0
      app/Admin/Controllers/ServiceController.php
  45. 72 0
      app/Admin/Controllers/Setting/PayConfigController.php
  46. 128 0
      app/Admin/Controllers/Setting/SettingController.php
  47. 88 0
      app/Admin/Controllers/Share/ShareConfigController.php
  48. 96 0
      app/Admin/Controllers/Share/UserController.php
  49. 116 0
      app/Admin/Controllers/Share/UserWithdrawController.php
  50. 83 0
      app/Admin/Controllers/ShareAController.php
  51. 84 0
      app/Admin/Controllers/ShareController.php
  52. 77 0
      app/Admin/Controllers/ShareDataController.php
  53. 119 0
      app/Admin/Controllers/ShareUserController.php
  54. 189 0
      app/Admin/Controllers/TaskListController.php
  55. 143 0
      app/Admin/Controllers/UserController.php
  56. 55 0
      app/Admin/Controllers/UserVipRecordController.php
  57. 111 0
      app/Admin/Controllers/UsersRoleController.php
  58. 209 0
      app/Admin/Controllers/WinnowController.php
  59. 116 0
      app/Admin/Controllers/WithdrawController.php
  60. 150 0
      app/Admin/Metrics/Consume.php
  61. 111 0
      app/Admin/Metrics/Episodes.php
  62. 95 0
      app/Admin/Metrics/Examples/NewDevices.php
  63. 104 0
      app/Admin/Metrics/Examples/NewUsers.php
  64. 110 0
      app/Admin/Metrics/Examples/ProductOrders.php
  65. 113 0
      app/Admin/Metrics/Examples/Sessions.php
  66. 112 0
      app/Admin/Metrics/Examples/Tickets.php
  67. 127 0
      app/Admin/Metrics/Examples/TotalUsers.php
  68. 171 0
      app/Admin/Metrics/Recharge.php
  69. 169 0
      app/Admin/Metrics/TotalMember.php
  70. 168 0
      app/Admin/Metrics/TotalUsers.php
  71. 23 0
      app/Admin/bootstrap.php
  72. 134 0
      app/Admin/routes.php
  73. 12 0
      app/Auth.php
  74. 19 0
      app/Casts/DefaultAvatar.php
  75. 36 0
      app/Casts/HttpToHttps.php
  76. 41 0
      app/Console/Commands/AnJuKePicker.php
  77. 195 0
      app/Console/Commands/DataSeeder.php
  78. 41 0
      app/Console/Commands/DongFangDiPicker.php
  79. 72 0
      app/Console/Commands/KuaishowPush.php
  80. 64 0
      app/Console/Commands/UserRepair.php
  81. 164 0
      app/Console/Commands/importMap.php
  82. 43 0
      app/Console/Kernel.php
  83. 7 0
      app/Exceptions/ApiException.php
  84. 7 0
      app/Exceptions/Exceptions/ApiException.php
  85. 25 0
      app/Exceptions/Exceptions/ApiHandler.php
  86. 60 0
      app/Exceptions/Exceptions/Handler.php
  87. 38 0
      app/Exceptions/Handler.php
  88. 165 0
      app/Helper/AttachmentHelper.php
  89. 276 0
      app/Helper/ByteDance.php
  90. 243 0
      app/Helper/Kuaishou.php
  91. 35 0
      app/Helper/SerialNumber.php
  92. 13 0
      app/Helper/UniPlatform/BaseAPI.php
  93. 139 0
      app/Helper/UniPlatform/BaseUniPlatform.php
  94. 52 0
      app/Helper/UniPlatform/Bytedance/ByteDanceAPI.php
  95. 79 0
      app/Helper/UniPlatform/Bytedance/Payment.php
  96. 53 0
      app/Helper/UniPlatform/Kuaishou/KuaishouAPI.php
  97. 82 0
      app/Helper/UniPlatform/Kuaishou/Payment.php
  98. 13 0
      app/Helper/UniPlatform/Wechat/WechatAPI.php
  99. 90 0
      app/Helper/Wechat.php
  100. 374 0
      app/Helper/function.php

BIN
.DS_Store


+ 94 - 0
README.md

xqd
@@ -0,0 +1,94 @@
+
+### 项目概要
+该项目为  思维定制基础API标准 
+https://docs.qq.com/doc/DVUxlS1FrWVJYcmtG 
+接口文档:https://www.postman.com/collections/f6d58843184203bbaef2
+```
+
+## 技术架构
+
+* PHP >8.0.0
+* MySQL >5.6.0
+* Nginx >1.12.0
+* Laravel 8.x (8.0)
+* PHP扩展安装fileinfo, redis, 删除禁用函数 putenv,proc_open
+##安装
+1. git clone 到本地
+2. 执行 composer install (导入sql创建好数据库)
+3. 配置 .env 中数据库连接信息,没有.env请复制.env.example命名为.env
+4. 执行 php artisan key:generate
+执行 php artisan migrate (可忽略)
+执行 php artisan db:seed (可忽略)
+
+```
+## Dcat-Admin 资源发布
+```
+$ php artisan admin:publish
+$ php artisan admin:install
+```
+
+
+## 创建 JWT secret
+```
+$ php artisan jwt:secret
+
+```
+
+## 安装第三方组件
+
+>注意:可以自定义 [`composer`](https://mirrors.aliyun.com/composer/) 镜像,加快拉取速度
+
+```
+composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
+
+```
+
+## 短信
+$result = SmsService::send($request->mobile, $request->event, [], $user);
+## 邮箱
+1. Mail::raw($notice, function ($message) use ($email) {
+2. $message->subject(trans('api.MAILBOX_VALIDATION_NOTIFICATION'));
+    $message->to($email);
+3. 邮箱配置
+   MAIL_MAILER=smtp
+   MAIL_HOST=ssl://smtp.163.com
+   MAIL_PORT=465
+   MAIL_USERNAME=youxiang@163.com
+   MAIL_PASSWORD=VIBTDHQAYLKJYIGC
+   MAIL_ENCRYPTION=
+   MAIL_FROM_ADDRESS=youxiang@163.com
+   MAIL_FROM_NAME="${APP_NAME}"
+});
+## 文件上传
+$api->post('attachment/upload', ['uses' => 'AttachmentController@upload',]);
+
+### 创建目录
+
+> 注意:git clone 从仓库拉取的代码,可能会存在 storage 目录缺失的问题,需要手动创建
+
+```
+$ mkdir -p storage/{app,debugbar,framework,logs}
+$ mkdir -p storage/framework/{cache,sessions,testing,views}
+
+```
+
+```
+$ chmod -R 777 storage bootstrap/cache
+```
+
+## 创建 storage 到 public 的软链接
+
+> 注意:如果是 Docker 环境,此步骤必须在容器内执行
+
+```
+$ php artisan storage:link
+```
+
+### 数据迁移
+
+> 注意:如果是 Docker 环境,此步骤必须在容器内执行
+> 注意:原始SQL 迁移文件不在本项目
+
+```
+$ php artisan migrate
+```

+ 82 - 0
_ide_helper_models.php

xqd
@@ -0,0 +1,82 @@
+<?php
+
+// @formatter:off
+/**
+ * A helper file for your Eloquent Models
+ * Copy the phpDocs from this file to the correct Model,
+ * And remove them from this file, to prevent double declarations.
+ *
+ * @author Barry vd. Heuvel <barryvdh@gmail.com>
+ */
+
+
+namespace App\Models{
+/**
+ * App\Models\User
+ *
+ * @property int $id
+ * @property string $nickname
+ * @property string $avatar
+ * @property string $password
+ * @property string $email
+ * @property string $mobile
+ * @property string $open_id
+ * @property string $union_id
+ * @property int $status
+ * @property string|null $email_verified_at
+ * @property string|null $remember_token
+ * @property \Illuminate\Support\Carbon|null $created_at
+ * @property \Illuminate\Support\Carbon|null $updated_at
+ * @property-read \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications
+ * @property-read int|null $notifications_count
+ * @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery()
+ * @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
+ * @method static \Illuminate\Database\Query\Builder|User onlyTrashed()
+ * @method static \Illuminate\Database\Eloquent\Builder|User query()
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereAvatar($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereEmailVerifiedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereMobile($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereNickname($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereOpenId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User wherePassword($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereRememberToken($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereStatus($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereUnionId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereUpdatedAt($value)
+ * @method static \Illuminate\Database\Query\Builder|User withTrashed()
+ * @method static \Illuminate\Database\Query\Builder|User withoutTrashed()
+ * @mixin \Eloquent
+ * @property \Illuminate\Support\Carbon|null $deleted_at
+ * @property-read \App\Models\UserInfo|null $info
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereDeletedAt($value)
+ * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\UserConsumeRecord[] $consumeRecords
+ * @property-read int|null $consume_records_count
+ * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\UserRechargeRecord[] $rechargeRecords
+ * @property-read int|null $recharge_records_count
+ * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\UserVipRecord[] $vipRecords
+ * @property-read int|null $vip_records_count
+ * @property int $parent_id 上级推荐用户
+ * @property string|null $become_child_at 成为下线时间
+ * @property string $income 收入
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereBecomeChildAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereIncome($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereParentId($value)
+ * @property string $is_share 是否分销
+ * @property string $share_qrcode 分享二维码
+ * @property string $scene_code 情景CODE
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereIsShare($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereSceneCode($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereShareQrcode($value)
+ * @property-read User|null $parent
+ * @property string $total_income 收入
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereTotalIncome($value)
+ * @property string|null $become_share_at 成为分销商时间
+ * @method static \Illuminate\Database\Eloquent\Builder|User whereBecomeShareAt($value)
+ * @property-read User|null $child
+ */
+//	class User extends \Eloquent implements \PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject {}
+}
+

BIN
app/.DS_Store


+ 52 - 0
app/Admin/Actions/Form/BatchEditEpisodeForm.php

xqd
@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Admin\Actions\Form;
+
+use App\Models\Episode;
+use Dcat\Admin\Contracts\LazyRenderable;
+use Dcat\Admin\Traits\LazyWidget;
+use Dcat\Admin\Widgets\Form;
+
+class BatchEditEpisodeForm extends Form implements LazyRenderable
+{
+    use LazyWidget;
+
+    public function handle(array $input)
+    {
+        // 选择的行
+        $ids = explode(',', $input['id'] ?? null);
+        if (empty($ids)) {
+            return $this->response()->error('参数错误');
+        }
+
+        foreach ($ids as $id) {
+            Episode::where('id', $id)->update([
+                'is_opend'     => $input['is_opend'],
+                'is_vip_watch' => $input['is_vip_watch'],
+                'platform'     => $input['platform'],
+            ]);
+        }
+
+        return $this->response()->success('修改成功')->refresh();
+    }
+
+    public function form()
+    {
+        $this->radio('is_opend')
+            ->options(config('global.episode_opend'))
+            ->default(1);
+
+        $this->radio('is_vip_watch')
+            ->options(config('global.bool_status'))
+            ->default(1);
+
+        $this->checkbox('platform')
+            ->options(config('global.platform'));
+
+        $this->hidden('id')->attribute('id', 'batch-id');
+    }
+
+    public function default()
+    {
+    }
+}

+ 52 - 0
app/Admin/Actions/Form/BatchEditEpisodeListForm.php

xqd
@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Admin\Actions\Form;
+
+use App\Models\EpisodesList;
+use Dcat\Admin\Contracts\LazyRenderable;
+use Dcat\Admin\Traits\LazyWidget;
+use Dcat\Admin\Widgets\Form;
+
+class BatchEditEpisodeListForm extends Form implements LazyRenderable
+{
+    use LazyWidget;
+
+    public function handle(array $input)
+    {
+        // 选择的行
+        $ids = explode(',', $input['id'] ?? null);
+        if (empty($ids)) {
+            return $this->response()->error('参数错误');
+        }
+
+        $isFree      = $input['is_free']      ?? null;
+        $salePrice   = $input['sale_price']   ?? 0;
+        $originPrice = $input['origin_price'] ?? 0;
+
+        foreach ($ids as $id) {
+            EpisodesList::where('id', $id)->update([
+                'is_free'      => $isFree,
+                'sale_price'   => $salePrice,
+                'origin_price' => $originPrice,
+            ]);
+        }
+
+        return $this->response()->success('修改成功')->refresh();
+    }
+
+    public function form()
+    {
+        $this->radio('is_free')
+            ->options(config('global.episode_free'))
+            ->when(0, function (Form $form) {
+                $form->number('origin_price')->min(1);
+                $form->number('sale_price')->min(1);
+            })->default(1);
+
+        $this->hidden('id')->attribute('id', 'batch-id');
+    }
+
+    public function default()
+    {
+    }
+}

+ 30 - 0
app/Admin/Actions/Form/UserRechargeRemark.php

xqd
@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Admin\Actions\Form;
+
+use App\Models\UserEpisodesRecord;
+use Dcat\Admin\Contracts\LazyRenderable;
+use Dcat\Admin\Traits\LazyWidget;
+use Dcat\Admin\Widgets\Form;
+
+class UserRechargeRemark extends Form implements LazyRenderable
+{
+    use LazyWidget;
+
+    public function handle(array $input)
+    {
+        if (!$input['remark']) {
+            return $this->response()->error('请填写备注');
+        }
+
+        UserEpisodesRecord::where('id', $input['id'])->update(['remark' => $input['remark']]);
+
+        return $this->response()->success('修改成功')->refresh();
+    }
+
+    public function form()
+    {
+        $this->textarea('remark', '备注');
+        $this->hidden('id')->attribute('id', 'recharge-id');
+    }
+}

+ 55 - 0
app/Admin/Actions/Form/WithdrawReview.php

xqd
@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Admin\Actions\Form;
+
+use App\Models\User;
+use App\Models\UserWithdraw;
+use Carbon\Carbon;
+use Dcat\Admin\Contracts\LazyRenderable;
+use Dcat\Admin\Traits\LazyWidget;
+use Dcat\Admin\Widgets\Form;
+
+class WithdrawReview extends Form implements LazyRenderable
+{
+    use LazyWidget;
+
+    public function handle(array $input)
+    {
+        $status = $input['status'];
+
+        try {
+            $withdraw = UserWithdraw::find($input['id']);
+            if ($status > 0) {
+                $withdraw->status = $status;
+                // 审核通过或者拒绝
+                if (1 == $status || 3 == $status) {
+                    $withdraw->review_at = Carbon::now()->toDateTimeString();
+                }
+                if (2 == $status) {
+                    $withdraw->withdraw_at = Carbon::now()->toDateTimeString();
+                }
+                $withdraw->save();
+            }
+
+            // 驳回返还收益
+            if (3 == $withdraw->status) {
+                $user         = User::find($withdraw->user_id);
+                $user->income = $user->income + $withdraw->price;
+                $user->save();
+            }
+        } catch (\Exception $exception) {
+            return $this->response()->error($exception->getMessage());
+        }
+
+        return $this->response()->success('success')->refresh();
+    }
+
+    public function form()
+    {
+        $id = isset($this->payload['id']) ? $this->payload['id'] : 0;
+        $this->hidden('id')->value($id);
+        $this->radio('status', '审核结果')
+            ->options(config('global.withdraw_status'))->default(1);
+        $this->disableResetButton();
+    }
+}

+ 36 - 0
app/Admin/Actions/Grid/BatchEditEpisode.php

xqd
@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Admin\Actions\Grid;
+
+use Dcat\Admin\Grid\BatchAction;
+use Dcat\Admin\Widgets\Modal;
+use App\Admin\Actions\Form\BatchEditEpisodeForm;
+
+class BatchEditEpisode extends BatchAction
+{
+    /**
+     * @return string
+     */
+    protected $title = '批量设置';
+
+    public function render()
+    {
+        $form = BatchEditEpisodeForm::make();
+
+        return Modal::make()
+            ->lg()
+            ->title($this->title)
+            ->body($form)
+            ->onLoad($this->getModalScript())
+            ->button($this->title);
+    }
+
+    protected function getModalScript()
+    {
+        return <<<JS
+//获取选中的ID
+let key = {$this->getSelectedKeysScript()}
+$("#batch-id").val(key)
+JS;
+    }
+}

+ 36 - 0
app/Admin/Actions/Grid/BatchEditEpisodeList.php

xqd
@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Admin\Actions\Grid;
+
+use Dcat\Admin\Grid\BatchAction;
+use Dcat\Admin\Widgets\Modal;
+use App\Admin\Actions\Form\BatchEditEpisodeListForm;
+
+class BatchEditEpisodeList extends BatchAction
+{
+    /**
+     * @return string
+     */
+    protected $title = '批量设置';
+
+    public function render()
+    {
+        $form = BatchEditEpisodeListForm::make();
+
+        return Modal::make()
+            ->lg()
+            ->title($this->title)
+            ->body($form)
+            ->onLoad($this->getModalScript())
+            ->button($this->title);
+    }
+
+    protected function getModalScript()
+    {
+        return <<<JS
+//获取选中的ID
+let key = {$this->getSelectedKeysScript()}
+$("#batch-id").val(key)
+JS;
+    }
+}

+ 50 - 0
app/Admin/Actions/Grid/UserConsumeRecord.php

xqd
@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Admin\Actions\Grid;
+
+use App\Models\UserConsumeRecord as UserConsumeRecordModel;
+use Dcat\Admin\Form\AbstractTool;
+use Dcat\Admin\Grid;
+
+class UserConsumeRecord extends AbstractTool
+{
+    /**
+     * @return string
+     */
+    protected $title = '金币收支';
+
+    public function handle($a = '')
+    {
+        return $this->getKey();
+    }
+
+    public function grid()
+    {
+        $id = \request()->input('key');
+
+        return Grid::make(new UserConsumeRecordModel(), function (Grid $grid) use ($id) {
+            $grid->model()->where('user_id', $id)->orderByDesc('created_at');
+            $grid->column('id')->sortable();
+            $grid->column('type', admin_trans('user-consume-record.fields.type'))
+                ->using(config('global.consume_type'))
+                ->label(['success', 'info', 'primary']);
+            $grid->column('before', admin_trans('user-consume-record.fields.before'))
+                ->label('info');
+            $grid->column('change', admin_trans('user-consume-record.fields.change'))
+                ->label('danger');
+            $grid->column('current', admin_trans('user-consume-record.fields.current'))
+                ->label('success');
+            $grid->column('remark', admin_trans('user-consume-record.fields.remark'));
+            $grid->column('created_at', '消费时间');
+
+            $grid->disableRowSelector();
+            $grid->disableActions();
+            $grid->disableToolbar();
+        });
+    }
+
+    public function render()
+    {
+        return $this->grid();
+    }
+}

+ 53 - 0
app/Admin/Actions/Grid/UserEpisodeRecord.php

xqd
@@ -0,0 +1,53 @@
+<?php
+
+namespace App\Admin\Actions\Grid;
+
+use App\Models\UserEpisodesRecord;
+use Dcat\Admin\Contracts\LazyRenderable;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Grid\RowAction;
+use Dcat\Admin\Traits\LazyWidget;
+
+class UserEpisodeRecord extends RowAction implements LazyRenderable
+{
+    use LazyWidget;
+
+    /**
+     * @return string
+     */
+    protected $title = '消费记录';
+
+    public function handle($a = '')
+    {
+        return $this->getKey();
+    }
+
+    public function grid()
+    {
+        $id = \request()->input('key');
+
+        return Grid::make(UserEpisodesRecord::with(['user', 'episodes']), function (Grid $grid) use ($id) {
+            $grid->model()->where('user_id', $id)->orderByDesc('created_at');
+            $grid->column('id')->sortable();
+            $grid->column('episodes.name', '短剧名称')->label('info');
+            $grid->column('list_id', '第几集')->label('primary');
+            $grid->column('platform', '所属平台')
+                ->display(function () {
+                    return $this->user->info->platform;
+                })
+                ->using(config('global.platform'))
+                ->label([1 => 'primary', 2 => 'success']);
+            $grid->column('price', '金币')->label('danger');
+            $grid->column('created_at', '消费时间');
+
+            $grid->disableActions();
+            $grid->disableRowSelector();
+            $grid->disableToolbar();
+        });
+    }
+
+    public function render()
+    {
+        return $this->grid();
+    }
+}

+ 54 - 0
app/Admin/Actions/Grid/UserRechargeRecord.php

xqd
@@ -0,0 +1,54 @@
+<?php
+
+namespace App\Admin\Actions\Grid;
+
+use Dcat\Admin\Contracts\LazyRenderable;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Grid\RowAction;
+use Dcat\Admin\Traits\LazyWidget;
+use App\Models\UserRechargeRecord as UserRechargeRecordModel;
+
+class UserRechargeRecord extends RowAction implements LazyRenderable
+{
+    use LazyWidget;
+
+    /**
+     * @return string
+     */
+    protected $title = '充值记录';
+
+    public function handle($a = '')
+    {
+        return $this->getKey();
+    }
+
+    public function grid()
+    {
+        $id = \request()->input('key');
+
+        return Grid::make(UserRechargeRecordModel::with(['user', 'combo']), function (Grid $grid) use ($id) {
+            $grid->model()->where('user_id', $id)->orderByDesc('created_at');
+            $grid->column('id')->sortable();
+
+            $grid->column('pay_id', '订单号');
+            $grid->column('combo.name', '充值套餐');
+            $grid->column('price', admin_trans('user-recharge-record.fields.price'))->label('danger');
+            $grid->column('platform', '所属平台')->display(function () {
+                return $this->user->info->platform;
+            })->using(config('global.platform'))->label([1 => 'primary', 2 => 'success']);
+            $grid->column('gold', admin_trans('user-recharge-record.fields.gold'))->label('info');
+            $grid->column('gift', admin_trans('user-recharge-record.fields.gift'))->label('primary');
+            // $grid->column('pay_id');
+            $grid->column('created_at', '充值时间')->sortable();
+
+            $grid->disableRowSelector();
+            $grid->disableActions();
+            $grid->disableToolbar();
+        });
+    }
+
+    public function render()
+    {
+        return $this->grid();
+    }
+}

+ 39 - 0
app/Admin/Actions/Grid/UserRechargeRemark.php

xqd
@@ -0,0 +1,39 @@
+<?php
+
+namespace App\Admin\Actions\Grid;
+
+use Dcat\Admin\Contracts\LazyRenderable;
+use Dcat\Admin\Grid\RowAction;
+use Dcat\Admin\Traits\LazyWidget;
+use Dcat\Admin\Widgets\Modal;
+
+class UserRechargeRemark extends RowAction implements LazyRenderable
+{
+    use LazyWidget;
+
+    /**
+     * @return string
+     */
+    protected $title = '备注';
+
+    public function render()
+    {
+        $form = \App\Admin\Actions\Form\UserRechargeRemark::make();
+
+        return Modal::make()
+            ->lg()
+            ->title($this->title)
+            ->body($form)
+            ->onLoad($this->getModalScript())
+            ->button($this->title);
+    }
+
+    protected function getModalScript()
+    {
+        return <<<JS
+//获取选中的ID
+let key = {$this->getKey()}
+$("#recharge-id").val(key)
+JS;
+    }
+}

+ 35 - 0
app/Admin/Actions/Grid/WithdrawReview.php

xqd
@@ -0,0 +1,35 @@
+<?php
+
+namespace App\Admin\Actions\Grid;
+
+use Dcat\Admin\Form\AbstractTool;
+use Dcat\Admin\Widgets\Modal;
+
+class WithdrawReview extends AbstractTool
+{
+    /**
+     * @return string
+     */
+    protected $title = '提现审核';
+
+    protected $model;
+
+    public function __construct(string $model = null, $id = 0, $status = 0)
+    {
+        $this->model  = $model;
+        $this->title  = '<i class="feather icon-check-square"></i> ' . $this->title;
+        $this->id     = $id;
+        $this->status = $status;
+    }
+
+    public function render()
+    {
+        $form = \App\Admin\Actions\Form\WithdrawReview::make()->payload(['id' => $this->id, 'status' => $this->status]);
+
+        return Modal::make()
+            ->lg()
+            ->title($this->title)
+            ->body($form)
+            ->button($this->title);
+    }
+}

+ 72 - 0
app/Admin/Controllers/AiCountController.php

xqd
@@ -0,0 +1,72 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Config;
+use Dcat\Admin\Form;
+use Dcat\Admin\Form\NestedForm;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class AiCountController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Config(), function (Grid $grid) {
+            $grid->model()->whereIn('id',[18]);
+            $grid->column('id')->sortable();
+            $grid->column('desc','标题');
+
+            $grid->column('value', '值')->display(function ($item) use ($grid) {});
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Config(), function (Show $show) {
+            $show->field('id');
+            $show->field('key');
+            $show->field('value');
+            $show->field('desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Config(), function (Form $form) {
+            $form->display('id');
+            $form->table('value', function (NestedForm $table) {
+                $table->select('lv','请选择年纪')->options([0 => '幼儿园',1 => '一年级',2 => '二年级',3 => '三年级',4 => '四年级',5 => '五年级',6 => '六年级',]);
+                $table->number('count','字数');
+            });
+            $form->display('desc','标题');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 94 - 0
app/Admin/Controllers/ApplyController.php

xqd
@@ -0,0 +1,94 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Apply;
+use App\Models\User;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class ApplyController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Apply(), function (Grid $grid) {
+            $grid->model()->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('share_name');
+            $grid->column('share_phone');
+            $grid->column('state')->display(function($item){
+                return $item == 0 ? '待审核' : ($item == 1 ? '已通过' : '已拒绝');
+            });
+            $grid->column('user_id');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+            $grid->disableDeleteButton();
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->equal('id');
+
+            });
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Apply(), function (Show $show) {
+            $show->field('id');
+            $show->field('share_name');
+            $show->field('share_phone');
+            $show->field('state');
+            $show->field('user_id');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Apply(), function (Form $form) {
+            $form->display('id');
+            $form->display('share_name');
+            $form->display('share_phone');
+            $form->select('state')
+                ->when(2, function (Form $form) {
+                    $form->textarea('not_desc','原因');
+                })
+                ->options([1 => '通过申请',2 => '不通过申请'])->saving(function ($item) use(&$form){
+                    if ($item == 1){
+                        User::query()->where('id',$form->model()->user_id)->update([
+                            'is_share' => 1,
+                            'share_name'    => $form->model()->share_name,
+                            'share_phone'    => $form->model()->share_phone,
+                        ]);
+                    }
+                    return $item;
+                });
+            $form->hidden('user_id');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 9 - 0
app/Admin/Controllers/AuthController.php

xqd
@@ -0,0 +1,9 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use Dcat\Admin\Http\Controllers\AuthController as BaseAuthController;
+
+class AuthController extends BaseAuthController
+{
+}

+ 113 - 0
app/Admin/Controllers/BannerController.php

xqd
@@ -0,0 +1,113 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Banner;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class BannerController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Banner(), function (Grid $grid) {
+            $grid->column('id')->sortable();
+            $grid->column('title');
+            $grid->column('sort');
+            $grid->column('url');
+            $grid->column('state')->switch();
+            $grid->column('image_path')->image();
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->like('title');
+                $filter->panel();
+
+
+            });
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Banner(), function (Show $show) {
+            $show->field('id');
+            $show->field('title');
+            $show->field('sort');
+            $show->field('url');
+            $show->field('state');
+            $show->field('image_path');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Banner(), function (Form $form) {
+            $form->display('id');
+            $form->text('title')->required();
+            $form->number('sort')->required();
+            $form->text('url')->required()->help('
+            路径: "/pages/index/index"
+            名称: "首页"
+            路径: "/pages/my/index",
+            名称: "我的"
+            路径: "/pages/my/huiBen_record/index",
+            名称: "绘本记录"
+            路径: "/pages/my/charge/index",
+            名称: "充值次数"
+            路径: "/pages/my/userInfo/index",
+            名称: "个人资料"
+            路径: "/pages/index/genHuiBen/index",
+            名称: "绘本生成参数填写页"
+            路径: "/pages/index/genRes/index",
+            名称: "绘本生成结果/绘本详情页"
+            路径: "/pages/my/pubCenter/index",
+            名称: "推广中心"
+            路径: "/pages/my/jiangli/index",
+            名称: "推荐奖励"
+            路径: "/pages/my/tuiguangDashi/index",
+            名称: "推广大使"
+            路径: "/pages/my/yongjinDetail/index",
+            名称: "佣金明细"
+            路径: "/pages/my/cash/index",
+            名称: "提现"
+            路径: "/pages/my/cashRecord/index",
+            名称: "提现记录"
+            路径: "/pages/my/team/index",
+            名称: "推广团队"
+            路径: "/pages/my/kefu/index",
+            名称: "客服中心"
+            ');
+            $form->switch('state');
+            $form->image('image_path')->disk('oss')->autoUpload()->saving(function ($res) {
+                return $res;
+            })->required();
+            $form->display('created_at');
+            $form->display('updated_at');
+            $form->disableViewButton();
+        });
+    }
+}

+ 162 - 0
app/Admin/Controllers/Episode/EpisodeController.php

xqd
@@ -0,0 +1,162 @@
+<?php
+
+namespace App\Admin\Controllers\Episode;
+
+use App\Admin\Actions\Grid\BatchEditEpisode;
+use App\Models\Episode;
+use App\Models\EpisodesCategory;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class EpisodeController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(Episode::with(['category', 'userEpisodesRecords']), function (Grid $grid) {
+            // $grid->model()->orderByDesc('id');
+            $grid->model()->orderBy('sort');
+            $grid->column('id')->sortable();
+            $grid->column('sort')->editable()->sortable();
+            $grid->column('category.name', '分类');
+            $grid->column('platform')->display(function () {
+                $html     = '';
+                $platform = config('global.platform');
+                $colors   = [1 => '#21b978', 2 => '#586cb1', 3 => '#3085d6'];
+                if (is_array($this->platform)) {
+                    foreach ($this->platform as $item) {
+                        $html .= '<span class="label" style="background:' . $colors[$item] . '; margin-right: 5px;">' . $platform[$item] . '</span>';
+                    }
+                } else {
+                    $html .= '<span class="label" style="background:' . $colors[$this->platform] . '">' . $platform[$this->platform] . '</span>';
+                }
+
+                return $html;
+            });
+            $grid->column('name')->editable();
+            $grid->column('episodes_price')->editable();
+            $grid->column('sale_count', '已售出')->display(function () {
+                return $this->userEpisodesRecords->count();
+            })->label('primary');
+            $grid->column('status')->using(config('global.episode_status'))->label(['gray', 'primary'])->sortable();
+            $grid->column('is_opend')->using(config('global.episode_opend'))->label(['gray', 'primary'])->sortable();
+            $grid->column('is_vip_watch')->using(config('global.bool_status'))->label(['gray', 'primary'])->sortable();
+            $grid->column('', '短剧上传')->display(function () {
+                $url = admin_url('/episodes/batch/' . $this->id . '/upload');
+
+                return '<a href="' . $url . '"><i class="fa fa-upload"></i> 上传剧集</a>';
+            });
+            $grid->column('created_at');
+
+            $grid->column('剧集管理', '规格')->display(function () {
+                $url = '/admin/episodes/' . $this->id . '/lists';
+
+                return "<a href='$url'>剧集管理</a>";
+            });
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->like('name')->width(2);
+                $filter->equal('category_id')->select(function () {
+                    return EpisodesCategory::select(['id', 'name'])->get()->pluck('name', 'id')->toArray();
+                })->width(2);
+                $filter->like('platform')->select(config('global.platform'))->width(2);
+                $filter->equal('status')->select(function () {
+                    return ['持续更新中', '已完结'];
+                })->width(2);
+                $filter->equal('is_opend')->select(config('global.episode_opend'))->width(2);
+                $filter->between('created_at', '添加时间')->datetime()->width(4);
+            });
+
+            $grid->batchActions([new BatchEditEpisode()]);
+
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, Episode::with(['category', 'userEpisodesRecords']), function (Show $show) {
+            $show->field('id');
+            $show->field('name');
+            $show->field('category.name', '分类');
+            $show->field('cover_img')->image();
+            $show->field('status')->using(config('global.episode_status'));
+            $show->field('is_opend')->using(config('global.episode_opend'));
+            $show->field('sort');
+            $show->field('is_vip_watch')->using(config('global.bool_status'));
+            $show->field('free_episodes');
+            $show->field('paid_episodes');
+            $show->field('episodes_price');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Episode(), function (Form $form) {
+            $form->display('id');
+            $form->text('name')->required();
+            $categoryModel = app(\App\Models\EpisodesCategory::class);
+            $cates         = $categoryModel::select(['id', 'name'])->get()->toArray();
+            $form->select('category_id')
+                ->options(array_column($cates, 'name', 'id'))
+                ->required();
+
+            $form->checkbox('platform')
+                ->options(config('global.platform'))
+                ->default(1);
+
+            $form->image('cover_img', '封面图(宽高比: 0.68:1)')->saveFullUrl()
+                ->uniqueName()->autoUpload()
+                ->autoSave(false)
+                ->removable(false)
+                ->width(4)->required();
+            $form->number('sort');
+            $form->radio('status')->options(config('global.episode_status'))->default(1);
+            $form->switch('is_opend')->default(1);
+            $form->switch('is_vip_watch')->default(1);
+            //            $form->number('free_episodes')->required();
+            //            $form->number('paid_episodes')->required();
+            $form->number('episodes_price')->required();
+            $form->number('favorite_num');
+            $form->number('collect_num');
+            $form->number('buy_num');
+            $form->number('play_num');
+            $form->number('virtual_share');
+
+            $form->saved(function (Form $form) {
+                if ($form->isCreating()) {
+                    return $form
+                        ->response()
+                        ->success(trans('admin.save_succeeded'))
+                        ->redirect("/episodes/{$form->getKey()}/lists/create");
+                }
+            });
+            //            $form->display('created_at');
+            //            $form->display('updated_at');
+
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+            $form->disableListButton();
+            $form->disableEditingCheck();
+            $form->disableViewCheck();
+            $form->disableCreatingCheck();
+        });
+    }
+}

+ 94 - 0
app/Admin/Controllers/Episode/EpisodesBatchUploadController.php

xqd
@@ -0,0 +1,94 @@
+<?php
+
+namespace App\Admin\Controllers\Episode;
+
+/* 设置内存 保证上传到 OSS */
+ini_set('memory_limit', '512M');
+
+use App\Models\Episode;
+use App\Models\EpisodesList;
+use Dcat\Admin\Form;
+use Dcat\Admin\Layout\Content;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class EpisodesBatchUploadController extends AdminController
+{
+    private $id;
+
+    public function index(Content $content, $id = 0)
+    {
+        $this->id = $id;
+        $episode  = Episode::find($id);
+        $form     = $this->form();
+
+        return $content
+            ->title($episode->name)
+            ->breadcrumb(
+                ['text' => $episode->name, 'url' => "/episodes/{$episode->id}/lists"],
+                ['text' => '剧集上传'],
+            )
+            ->body($form)
+            ->description('剧集上传');
+    }
+
+    public function store($id = 0)
+    {
+        $req      = request()->post();
+        $form     = new Form();
+        $response = $form->response();
+        if (isset($req['lists']) && empty($req['lists'])) {
+            return $response->error('请上传剧集');
+        } elseif (!isset($req['lists'])) {
+            return $this->form()->store();
+        }
+        $lists = explode(',', $req['lists']);
+        $sort  = EpisodesList::where('episodes_id', $id)->max('sort');
+
+        foreach ($lists as $list) {
+            $episode              = new EpisodesList();
+            $episode->episodes_id = $id;
+            $episode->sort        = ++$sort;
+            $episode->url         = $list;
+            $episode->save();
+        }
+
+        return $response->success(__('admin.save_succeeded'))
+            ->redirect(admin_url("/episodes/{$id}/lists"));
+    }
+
+    public function form()
+    {
+        $id = $this->id;
+
+        return new Form(new EpisodesList(), function (Form $form) use ($id) {
+            $form->action(admin_url("/episodes/batch/{$id}/upload"));
+            $form->tools(
+                function (Form\Tools $tools) {
+                    $tools->disableList();
+                }
+            );
+            $form->disableHeader();
+            $form->disableCreatingCheck();
+            $form->disableEditingCheck();
+            $form->disableViewCheck();
+
+            $form->multipleFile('lists', '剧集')
+                ->mimeTypes('video/*')
+                ->chunkSize(4096)
+                ->maxSize(1024 * 1024)
+                ->saveFullUrl()
+                ->uniqueName()
+                ->autoUpload()
+                ->autoSave(false)
+                ->removable(false)
+                ->required();
+
+            $form->saved(function (Form $form) use ($id) {
+                return $form
+                    ->response()
+                    ->success(trans('admin.update_succeeded'))
+                    ->redirect(admin_url("/episodes/{$id}/lists"));
+            });
+        });
+    }
+}

+ 86 - 0
app/Admin/Controllers/Episode/EpisodesCategoryController.php

xqd
@@ -0,0 +1,86 @@
+<?php
+
+namespace App\Admin\Controllers\Episode;
+
+use App\Models\Episode;
+use App\Models\EpisodesCategory;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class EpisodesCategoryController extends AdminController
+{
+    public function destroy($id)
+    {
+        $childIds   = EpisodesCategory::where('pid', $id)->get()->pluck('id');
+        $childIds[] = $id;
+
+        $hasEpisode = Episode::whereIn('category_id', $childIds)->count();
+        if ($hasEpisode) {
+            $form     = new Form();
+            $response = $form->response();
+
+            return $response->error('当前分类或子分类存在短剧,前先删除短剧后再删除分类');
+        }
+
+        return $this->form()->destroy($id);
+    }
+
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new EpisodesCategory(), function (Grid $grid) {
+            $grid->model()->orderByDesc('sort');
+            $grid->column('id')->sortable();
+            $grid->column('name');
+            // $grid->column('level')->using(config('global.cat_level'));
+            //            $grid->column('pid','父分类')->display(function () {
+            //                /* @var EpisodesCategory $this*/
+            //                return $this->parent ?$this->parent->name : '';
+            //            });
+            $grid->column('sort')->editable();
+            // $grid->column('created_at');
+
+            $grid->disableRowSelector();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new EpisodesCategory(), function (Form $form) {
+            $form->display('id');
+            //            $form->radio('level')
+            //                ->when(2, function (Form $form){
+            //                    $options = EpisodesCategory::select(['id','name'])
+            //                        ->where('level',1)
+            //                        ->get()
+            //                        ->pluck('name', 'id');
+            //                    $form->select('pid')->options($options);
+            //                })
+            //                ->options(config('global.cat_level'))
+            //                ->default(1);
+
+            $form->text('level')->value(1)->display(false);
+
+            $form->text('name')->required();
+            $form->number('sort');
+
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+            $form->disableListButton();
+            $form->disableEditingCheck();
+            $form->disableViewCheck();
+            $form->disableCreatingCheck();
+        });
+    }
+}

+ 150 - 0
app/Admin/Controllers/Episode/EpisodesListController.php

xqd
@@ -0,0 +1,150 @@
+<?php
+
+namespace App\Admin\Controllers\Episode;
+
+/* 设置内存 保证上传到 OSS */
+ini_set('memory_limit', '512M');
+
+use App\Admin\Actions\Grid\BatchEditEpisodeList;
+use App\Models\Episode;
+use App\Models\EpisodesList;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Layout\Content;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class EpisodesListController extends AdminController
+{
+    /* @var Episode $episode */
+    private $episode;
+
+    protected function title()
+    {
+        return $this->episode->name;
+    }
+
+    protected function content(Content $content, $episodeId = 0)
+    {
+        $this->episode = Episode::find($episodeId);
+
+        return $content
+            ->translation($this->translation())
+            ->breadcrumb(
+                ['text' => $this->episode->name, 'url' => "/episodes/{$this->episode->id}/lists"],
+                ['text' => '剧集上传'],
+            )
+            ->title($this->title())
+            ->description('剧集上传');
+    }
+
+    public function index(Content $content, $episodeId = 0)
+    {
+        return $this->content($content, $episodeId)
+            ->body($this->grid());
+    }
+
+    public function create(Content $content, $episodeId = 0)
+    {
+        return $this->content($content, $episodeId)
+            ->body($this->form());
+    }
+
+    public function update($episodeId, $id = 0)
+    {
+        $this->episode = Episode::find($episodeId);
+
+        return $this->form()->update($id);
+    }
+
+    public function store($episodeId = 0)
+    {
+        $this->episode = Episode::find($episodeId);
+
+        return $this->form()->store();
+    }
+
+    public function edit($episodeId, Content $content, $id = 0)
+    {
+        return $this->content($content, $episodeId)
+            ->body($this->form()->edit($id));
+    }
+
+    public function destroy($episodeId, $id = 0)
+    {
+        $this->episode = Episode::find($episodeId);
+        parent::destroy($id);
+
+        return $this->form()->destroy($id);
+    }
+
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(EpisodesList::with(['episode'])->where('episodes_id', $this->episode->id), function (Grid $grid) {
+            $grid->model()->orderBy('sort');
+            // $grid->column('id');
+            $grid->column('sort')->display(function () {
+                return '第' . $this->sort . '集';
+            })->editable(true);
+            $grid->column('episode.name', '剧集名称');
+            $grid->column('is_free')
+                ->using(config('global.episode_free'))
+                ->label(['success', 'primary'])->sortable();
+            $grid->column('origin_price')->editable();
+            $grid->column('sale_price')->editable();
+            $grid->column('url')->display(function () {
+                return '<a href="' . $this->url . '">视频链接</a>';
+            });
+            $url = admin_url('/episodes/batch/' . $this->episode->id . '/upload');
+
+            $grid->tools('<a href="' . $url . '" class="btn btn-primary btn-outline">
+<i class="feather icon-upload"></i><span class="d-none d-sm-inline">&nbsp;&nbsp;批量上传</span></a>');
+
+            $grid->hideColumns(['id']);
+            $grid->batchActions([new BatchEditEpisodeList()]);
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new EpisodesList(), function (Form $form) {
+            $sort = EpisodesList::where('episodes_id', $this->episode->id)->max('sort');
+            $form->hidden('id');
+            $form->hidden('episodes_id')->value($this->episode->id);
+            $form->number('sort')->default($sort + 1)->min(1);
+            $form->radio('is_free')
+                ->options(config('global.episode_free'))
+                ->when(0, function (Form $form) {
+                    $form->number('sale_price')->min(1);
+                })->default(1);
+            $form->file('url')
+                ->chunkSize(4096)
+                ->maxSize(1024 * 1024)
+                ->saveFullUrl()
+                ->mimeTypes('video/*')
+                ->uniqueName()->autoUpload()
+                ->autoSave(false)
+                ->removable(false)
+                ->width(4)->required();
+
+            $form->display('created_at');
+
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+            $form->disableListButton();
+            $form->disableEditingCheck();
+            $form->disableViewCheck();
+            $form->disableCreatingCheck();
+        });
+    }
+}

+ 85 - 0
app/Admin/Controllers/GiveawayController.php

xqd
@@ -0,0 +1,85 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Giveaway;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class GiveawayController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Giveaway(), function (Grid $grid) {
+            $grid->model()->with(['user','order','buyUser'])->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('user.name','购买者昵称');
+            $grid->column('user.avatar','购买者头像')->image('',40,40);
+            $grid->column('buyUser.name','推荐人昵称');
+            $grid->column('buyUser.avatar','推荐人头像')->image('',40,40);
+            $grid->column('order.order','订单号');
+            $grid->column('title');
+            $grid->column('amount','佣金');
+            $grid->column('order.amount','支付金额');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->between('created_at')->datetime();
+            });
+            $grid->disableViewButton();
+            $grid->disableCreateButton();
+            $grid->disableDeleteButton();
+            $grid->disableEditButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Giveaway(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('purchaser_user_id');
+            $show->field('title');
+            $show->field('amount');
+            $show->field('order_id');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Giveaway(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('purchaser_user_id');
+            $form->text('title');
+            $form->text('amount');
+            $form->text('order_id');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 30 - 0
app/Admin/Controllers/HomeController.php

xqd
@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Admin\Metrics\Consume;
+use App\Admin\Metrics\Episodes;
+use App\Admin\Metrics\Recharge;
+use App\Admin\Metrics\TotalMember;
+use App\Admin\Metrics\TotalUsers;
+use App\Http\Controllers\Controller;
+use Dcat\Admin\Layout\Content;
+use Dcat\Admin\Layout\Row;
+
+class HomeController extends Controller
+{
+    public function index(Content $content)
+    {
+                return $content->body('<div style="text-align: center; font-size: 28px; font-weight: 300">欢迎进入短腿鹤伴读管理后台</div>');
+//        return $content
+//            ->header('数据统计卡片')
+//            ->body(function (Row $row) {
+//                $row->column(4, new TotalUsers());
+//                $row->column(4, new TotalMember());
+//                $row->column(4, new Episodes());
+//            })->body(function (Row $row) {
+//                $row->column(6, new Recharge());
+//                $row->column(6, new Consume());
+//            });
+    }
+}

+ 67 - 0
app/Admin/Controllers/KeywordController.php

xqd
@@ -0,0 +1,67 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Config;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class KeywordController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Config(), function (Grid $grid) {
+            $grid->model()->whereIn('id',[26,27,28]);
+            $grid->column('id')->sortable();
+            $grid->column('desc','标题');
+            $grid->column('value', '值');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Config(), function (Show $show) {
+            $show->field('id');
+            $show->field('key');
+            $show->field('value');
+            $show->field('desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Config(), function (Form $form) {
+            $form->display('id');
+            $form->textarea('value','值')->required();
+            $form->display('desc','标题');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 74 - 0
app/Admin/Controllers/Market/RechargeComboController.php

xqd
@@ -0,0 +1,74 @@
+<?php
+
+namespace App\Admin\Controllers\Market;
+
+use App\Models\RechargeCombo;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class RechargeComboController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new RechargeCombo(), function (Grid $grid) {
+            $grid->model()->orderByDesc('status');
+            $grid->column('id')->sortable();
+            $grid->column('name');
+            $grid->column('price');
+            $grid->column('gold');
+            $grid->column('gift');
+            $grid->column('status')->switch();
+
+            $grid->disableViewButton();
+            $grid->disableRowSelector();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new RechargeCombo(), function (Show $show) {
+            $show->field('id');
+            $show->field('name');
+            $show->field('price');
+            $show->field('gift');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new RechargeCombo(), function (Form $form) {
+            $form->display('id');
+            $form->text('name')->required();
+            $form->text('price')->required();
+            $form->text('gold')->required();
+            $form->text('gift')->default(0);
+            $form->switch('status')->default(1);
+
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+            $form->disableListButton();
+            $form->disableEditingCheck();
+            $form->disableViewCheck();
+            $form->disableCreatingCheck();
+        });
+    }
+}

+ 102 - 0
app/Admin/Controllers/Market/UserRechargeRecordController.php

xqd
@@ -0,0 +1,102 @@
+<?php
+
+namespace App\Admin\Controllers\Market;
+
+use App\Models\RechargeCombo;
+use App\Models\UserRechargeRecord;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class UserRechargeRecordController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(UserRechargeRecord::with(['user', 'combo', 'pay']), function (Grid $grid) {
+            $grid->model()->where('status', 1)
+                ->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('avatar', '用户')->display(function () {
+                $str = '';
+                $str .= "<div style='margin-right:10px;display: flex;align-items: center'>";
+                $str .= '<img data-action="preview-img" src="' . $this->user->avatar . '" onerror="this.src=\'https://fourtiao.oss-cn-beijing.aliyuncs.com/zhangsiye/images/6b40343b27263be34cf3212bf44f74c3.png\'" style="height:50px;width:50px;cursor:pointer;margin-right:10px;" class="img img-thumbnail">';
+                $str .= '<div>';
+                $str .= '<p style="margin-bottom: 5px">' . $this->user->nickname . '</p>';
+                $str .= '<p style="margin-bottom: 0px">' . $this->user->mobile . '</p>';
+                $str .= '</div>';
+                $str .= '</div>';
+
+                return $str;
+            });
+            $grid->column('user.info.platform', '所属平台')->using(config('global.platform'))->label([1 => 'primary', 2 => 'success', 3 => 'info']);
+            $grid->column('gold', '收支情况')->display(function () {
+                return '+' . ($this->gold + $this->gift);
+            })->label('success');
+            $grid->column('desc');
+            $grid->column('pay_id', '订单号');
+            $grid->column('created_at', '充值时间');
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->like('user.nickname', '昵称')->width(3);
+                $filter->like('pay_id', '订单号')->width(3);
+                $filter->equal('user.info.platform', '所属平台')->select(config('global.platform'))->width(3);
+                $filter->equal('combo.id', '充值套餐')->select(function () {
+                    return RechargeCombo::select(['id', 'name'])->get()->pluck('name', 'id')->toArray();
+                })->width(3);
+                $filter->between('created_at', '充值时间')->datetime()->width(3);
+            });
+
+            $grid->disableActions();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+            $grid->disableRowSelector();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new UserRechargeRecord(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('combo_id');
+            $show->field('price');
+            $show->field('gift');
+            $show->field('pay_id');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new UserRechargeRecord(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('combo_id');
+            $form->text('price');
+            $form->text('gift');
+            $form->text('pay_id');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 78 - 0
app/Admin/Controllers/Market/VipComboController.php

xqd
@@ -0,0 +1,78 @@
+<?php
+
+namespace App\Admin\Controllers\Market;
+
+use App\Models\VipCombo;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class VipComboController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new VipCombo(), function (Grid $grid) {
+            $grid->model()->orderByDesc('status');
+            $grid->column('id')->sortable();
+            $grid->column('name')->editable();
+            $grid->column('price');
+            $grid->column('valid_day');
+            $grid->column('desc');
+            // $grid->column('status')->switch();
+
+            $grid->disableViewButton();
+            $grid->disableDeleteButton();
+            $grid->disableRowSelector();
+            $grid->disableCreateButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new VipCombo(), function (Show $show) {
+            $show->field('id');
+            $show->field('name');
+            $show->field('price');
+            $show->field('valid_day');
+            $show->field('desc');
+            $show->field('status');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new VipCombo(), function (Form $form) {
+            $form->display('id');
+            $form->text('name')->required();
+            $form->decimal('price')->required();
+            $form->number('valid_day')->required();
+            $form->textarea('desc');
+            // $form->switch('status')->default(1);
+
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+            $form->disableListButton();
+            $form->disableEditingCheck();
+            $form->disableViewCheck();
+            $form->disableCreatingCheck();
+        });
+    }
+}

+ 105 - 0
app/Admin/Controllers/Order/UserConsumeRecordController.php

xqd
@@ -0,0 +1,105 @@
+<?php
+
+namespace App\Admin\Controllers\Order;
+
+use App\Models\UserConsumeRecord;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class UserConsumeRecordController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(UserConsumeRecord::with(['user.info']), function (Grid $grid) {
+            $grid->model()->orderByDesc('created_at');
+            $grid->column('id')->sortable();
+            $grid->column('user_id', '用户')->display(function () {
+                $str = '';
+                $str .= "<div style='margin-right:10px;display: flex;align-items: center'>";
+                $str .= '<img data-action="preview-img" src="' . $this->user->avatar . '" onerror="this.src=\'https://fourtiao.oss-cn-beijing.aliyuncs.com/zhangsiye/images/6b40343b27263be34cf3212bf44f74c3.png\'" style="height:50px;width:50px;cursor:pointer;margin-right:10px;" class="img img-thumbnail">';
+                $str .= '<div>';
+                $str .= '<p style="margin-bottom: 5px">' . $this->user->nickname . '</p>';
+                $str .= '<p style="margin-bottom: 0px">' . $this->user->mobile . '</p>';
+                $str .= '</div>';
+                $str .= '</div>';
+
+                return $str;
+            });
+            $grid->column('user.info.platform', '所属平台')
+                ->using(config('global.platform'))
+                ->label([1 => 'primary', 2 => 'success', 3 => 'info']);
+            $grid->column('type')
+                ->using(config('global.consume_type'))
+                ->label(['success', 'info', 'primary']);
+            $grid->column('before')->label('info');
+            $grid->column('change')->display(function () {
+                return $this->change > 0 ? '+' . $this->change : $this->change;
+            })->label('danger');
+            $grid->column('current')->label('success');
+            $grid->column('remark');
+            $grid->column('created_at');
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->like('user.nickname', '昵称')->width(3);
+                $filter->equal('user.info.platform', '所属平台')->select(config('global.platform'))->width(3);
+                $filter->equal('type', '类型')->select(config('global.consume_type'))->width(3);
+                $filter->between('created_at', '创建时间')->datetime()->width(3);
+            });
+
+            $grid->disableCreateButton();
+            $grid->disableRowSelector();
+            $grid->disableActions();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new UserConsumeRecord(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('type');
+            $show->field('before');
+            $show->field('change');
+            $show->field('current');
+            $show->field('remark');
+            $show->field('order_id');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new UserConsumeRecord(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('type');
+            $form->text('before');
+            $form->text('change');
+            $form->text('current');
+            $form->text('remark');
+            $form->text('order_id');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 108 - 0
app/Admin/Controllers/Order/UserEpisodesRecordController.php

xqd
@@ -0,0 +1,108 @@
+<?php
+
+namespace App\Admin\Controllers\Order;
+
+use App\Admin\Actions\Grid\UserRechargeRemark;
+use App\Models\Episode;
+use App\Models\UserEpisodesRecord;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class UserEpisodesRecordController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(UserEpisodesRecord::with(['user', 'episodes']), function (Grid $grid) {
+            $grid->model()->orderByDesc('created_at');
+            $grid->column('id')->sortable();
+            $grid->column('user_id')->display(function () {
+                $str = '';
+                $str .= "<div style='margin-right:10px;display: flex;align-items: center'>";
+                $str .= '<img data-action="preview-img" src="' . $this->user->avatar . '" onerror="this.src=\'https://fourtiao.oss-cn-beijing.aliyuncs.com/zhangsiye/images/6b40343b27263be34cf3212bf44f74c3.png\'" style="height:50px;width:50px;cursor:pointer;margin-right:10px;" class="img img-thumbnail">';
+                $str .= '<div>';
+                $str .= '<p style="margin-bottom: 5px">' . $this->user->nickname . '</p>';
+                $str .= '<p style="margin-bottom: 0px">' . $this->user->mobile . '</p>';
+                $str .= '</div>';
+                $str .= '</div>';
+
+                return $str;
+            });
+            $grid->column('platform', '所属平台')->display(function () {
+                return $this->user->info->platform;
+            })->using(config('global.platform'))->label([1 => 'primary', 2 => 'success']);
+            $grid->column('episodes.name', '短剧名称')->label('info');
+            $grid->column('list_id')->label('primary');
+            $grid->column('price')->label('danger');
+            $grid->column('created_at');
+
+            $grid->actions(function (Grid\Displayers\Actions $actions) {
+                $actions->append(new UserRechargeRemark());
+            });
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->equal('user_id', '用户ID')->width(3);
+                $filter->like('user.nickname', '用户昵称')->width(3);
+                $filter->equal('episodes_id')->select(function () {
+                    return Episode::select(['id', 'name'])->get()->pluck('name', 'id')->toArray();
+                })->width(3);
+                $filter->equal('user.mobile', '手机号')->width(3);
+                $filter->equal('user.info.platform', '所属平台')->select(config('global.platform'))->width(3);
+                $filter->between('price')->width(3);
+                $filter->between('created_at')->date()->width(4);
+            });
+
+            $grid->disableActions();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, UserEpisodesRecord::with(['user', 'episodes']), function (Show $show) {
+            $show->field('id');
+            $show->field('user.nickname', '用户昵称');
+            $show->field('user.avatar', '用户头像')->image('', 200);
+            $show->field('user.mobile', '用户电话');
+            $show->field('episodes.name', '短剧名称');
+            $show->field('list_id', '第几集')->label('primary');
+            $show->field('price');
+            $show->field('created_at');
+
+            $show->disableEditButton();
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new UserEpisodesRecord(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('episodes_id');
+            $form->text('list_id');
+            $form->text('price');
+            $form->text('discount');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 107 - 0
app/Admin/Controllers/Order/UserRechargeRecordController.php

xqd
@@ -0,0 +1,107 @@
+<?php
+
+namespace App\Admin\Controllers\Order;
+
+use App\Models\RechargeCombo;
+use App\Models\UserRechargeRecord;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class UserRechargeRecordController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(UserRechargeRecord::with(['user.info', 'combo', 'pay']), function (Grid $grid) {
+            $grid->model()->where('status', 1)->orderByDesc('created_at');
+            $grid->column('id')->sortable();
+            $grid->column('pay_id', '订单号');
+            $grid->column('user_id')->display(function () {
+                $str = '';
+                $str .= "<div style='margin-right:10px;display: flex;align-items: center'>";
+                $str .= '<img data-action="preview-img" src="' . $this->user->avatar . '" onerror="this.src=\'https://fourtiao.oss-cn-beijing.aliyuncs.com/zhangsiye/images/6b40343b27263be34cf3212bf44f74c3.png\'" style="height:50px;width:50px;cursor:pointer;margin-right:10px;" class="img img-thumbnail">';
+                $str .= '<div>';
+                $str .= '<p style="margin-bottom: 5px">' . $this->user->nickname . '</p>';
+                $str .= '<p style="margin-bottom: 0px">' . $this->user->mobile . '</p>';
+                $str .= '</div>';
+                $str .= '</div>';
+
+                return $str;
+            });
+            $grid->column('user.info.platform', '所属平台')
+                ->using(config('global.platform'))
+                ->label([1 => 'primary', 2 => 'success', 3 => 'info']);
+            $grid->column('price')->label('danger');
+            $grid->column('combo.name', '套餐名称');
+            $grid->column('gold')->label('info');
+            $grid->column('gift')->label('primary');
+            // $grid->column('pay_id');
+            $grid->column('created_at')->sortable();
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->equal('user.info.platform', '所属平台')
+                    ->select(config('global.platform'))
+                    ->width(3);
+                $filter->between('created_at', '下单时间')->date()->width(4);
+                $filter->equal('pay_id', '订单号')->width(4);
+
+                $combos = RechargeCombo::get()->pluck('name', 'id');
+                $filter->equal('combo_id')
+                    ->select($combos)
+                    ->width(3);
+            });
+
+            $grid->disableActions();
+            $grid->disableCreateButton();
+            $grid->disableBatchDelete();
+            $grid->disableDeleteButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new UserRechargeRecord(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('combo_id');
+            $show->field('price');
+            $show->field('gift');
+            $show->field('pay_id');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new UserRechargeRecord(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('combo_id');
+            $form->text('price');
+            $form->text('gift');
+            $form->text('pay_id');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 110 - 0
app/Admin/Controllers/OrderController.php

xqd
@@ -0,0 +1,110 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Order;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class OrderController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Order(), function (Grid $grid) {
+            $grid->model()->with(['userData','orderData'])->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('userData.name','昵称');
+            $grid->column('userData.mobile','手机号');
+            $grid->column('orderData.title','商品名称');
+            $grid->column('state')->using([0 => '待支付', 1 => '已支付',2 => '支付失败'])
+                ->dot(
+                    [
+                        0 => 'danger',
+                        1 => 'success',
+                        2 => 'primary',
+                    ],
+                    'danger' // 第二个参数为默认值
+                );
+            $grid->column('order');
+            $grid->column('wx_order');
+            $grid->column('amount');
+            $grid->column('diamond');
+            $grid->column('pay_at','支付时间');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->quickSearch(['order', 'wx_order'])->placeholder('搜索...');
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->between('created_at')->datetime();
+                $filter->equal('state')->select([0 => '待支付', 1 => '已支付',2 => '支付失败']);
+                $filter->where('mobile', function ($query) {
+                    $query->whereHas('userData', function ($query) {
+                        $query->where('mobile', 'like', "%{$this->input}%");
+                    });
+                }, '用户手机号');
+            });
+            $grid->disableViewButton();
+            $grid->disableCreateButton();
+            $grid->disableEditButton();
+            $grid->disableDeleteButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Order(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('state');
+            $show->field('order');
+            $show->field('wx_order');
+            $show->field('amount');
+            $show->field('diamond');
+            $show->field('pay_at');
+            $show->field('pay');
+            $show->field('isPost');
+            $show->field('config_id');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Order(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('state');
+            $form->text('order');
+            $form->text('wx_order');
+            $form->text('amount');
+            $form->text('diamond');
+            $form->text('pay_at');
+            $form->text('pay');
+            $form->text('isPost');
+            $form->text('config_id');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 83 - 0
app/Admin/Controllers/OtherController.php

xqd
@@ -0,0 +1,83 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Config;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class OtherController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Config(), function (Grid $grid) {
+            $grid->model()->whereIn('id',[5,6,11,25]);
+            $grid->column('id')->sortable();
+            $grid->column('desc','标题');
+            $grid->column('value', '值')->display(function ($item) use ($grid) {
+                // 在这里通过 $this 获取当前行的数据
+                $id = $this->id;
+                // 根据条件判断是否显示值列
+                if ($id == 12) {
+                    return $item.'%';
+                } else {
+                    return $item;
+                }
+            });
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Config(), function (Show $show) {
+            $show->field('id');
+            $show->field('key');
+            $show->field('value');
+            $show->field('desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Config(), function (Form $form) {
+            $form->display('id');
+            if ($form->model()->id == 11){
+                $form->textarea('value','值')->required();
+            }elseif ($form->model()->id == 12){
+                $form->number('value','值')->required();
+            }
+            else{
+                $form->text('value','值')->required();
+            }
+            $form->display('desc','标题');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 94 - 0
app/Admin/Controllers/PaymentConfigController.php

xqd
@@ -0,0 +1,94 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\PaymentConfig;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class PaymentConfigController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PaymentConfig(), function (Grid $grid) {
+            $grid->model()->orderByDesc('sort');
+            $grid->column('id')->sortable();
+            $grid->column('title');
+            $grid->column('amount');
+            $grid->column('diamond');
+            $grid->column('state')->switch();
+            $grid->column('is_new','是否新用户专享')->using([0 => '不是新用户专享', 1 => '新用户专享'])
+                ->dot(
+                    [
+                        0 => 'danger',
+                        1 => 'success',
+                    ],
+                    'danger' // 第二个参数为默认值
+                );
+            $grid->column('sort');
+            $grid->column('award_count','推荐用户奖励次数');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->quickSearch(['title', 'id'])->placeholder('搜索...');
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->between('created_at')->datetime();
+            });
+            $grid->disableDeleteButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PaymentConfig(), function (Show $show) {
+            $show->field('id');
+            $show->field('title');
+            $show->field('amount');
+            $show->field('diamond');
+            $show->field('state');
+            $show->field('sort');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PaymentConfig(), function (Form $form) {
+            $form->display('id');
+            $form->text('title');
+            $form->text('amount')->rules('numeric')->saving(function ($item){
+                return $item * 100;
+            });
+            $form->text('diamond');
+            $form->switch('state');
+            $form->select('is_new','是否新用户专享')->options([0 => '不是新用户专享', 1 => '新用户专享']);
+            $form->number('sort');
+            $form->number('award_count','推荐用户奖励次数')->default(0);
+            $form->disableDeleteButton();
+            $form->display('created_at');
+            $form->display('updated_at');
+            $form->disableViewButton();
+        });
+    }
+}

+ 83 - 0
app/Admin/Controllers/Program/BannerController.php

xqd
@@ -0,0 +1,83 @@
+<?php
+
+namespace App\Admin\Controllers\Program;
+
+use App\Models\Banner;
+use App\Models\Episode;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class BannerController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(Banner::with(['episode']), function (Grid $grid) {
+            $grid->column('id')->sortable();
+            //            $grid->column('name')->editable();
+            $grid->column('image')->image();
+            $grid->column('episode_id', '跳转剧集')->display(function () {
+                return $this->episode ? "<a href='/admin/episodes/index?name={$this->episode->name}'>{$this->episode->name}</a>" : '';
+            });
+            $grid->column('sort')->editable()->sortable();
+            $grid->column('status')->switch('', true);
+
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Banner(), function (Show $show) {
+            $show->field('id');
+            $show->field('name');
+            $show->field('image');
+            $show->field('sort');
+            $show->field('status');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Banner(), function (Form $form) {
+            $form->display('id');
+            //            $form->text('name');
+            $form->image('image', '轮播图(宽高比:2.7:1)')
+                ->saveFullUrl()
+                ->uniqueName()->autoUpload()
+                ->autoSave(false)
+                ->removable(false)
+                ->width(4);
+            $episode = Episode::select(['id', 'name'])->get()->toArray();
+            $form->select('episode_id', '绑定剧集')
+                ->options(array_column($episode, 'name', 'id'))
+                ->required();
+            $form->number('sort');
+            $form->switch('status')->default(true);
+
+            $form->disableDeleteButton();
+            $form->disableViewButton();
+            $form->disableViewCheck();
+            $form->disableEditingCheck();
+            $form->disableCreatingCheck();
+        });
+    }
+}

+ 56 - 0
app/Admin/Controllers/Program/HomeColumnController.php

xqd
@@ -0,0 +1,56 @@
+<?php
+
+namespace App\Admin\Controllers\Program;
+
+use App\Models\HomeColumn;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class HomeColumnController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new HomeColumn(), function (Grid $grid) {
+            $grid->column('id')->sortable();
+            $grid->column('name')->editable();
+            $grid->column('sort')->editable()->sortable();
+            $grid->column('status')->switch('', true);
+
+            $grid->disableCreateButton();
+            $grid->disableBatchDelete();
+            $grid->disableDeleteButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new HomeColumn(), function (Form $form) {
+            $form->display('id');
+            $form->text('name');
+            //            $form->image('icon')->uniqueName()->autoUpload()
+            //                ->autoSave(false)
+            //                ->removable(false)
+            //                ->width(4);
+            $form->number('sort');
+            $form->switch('status');
+
+            $form->disableDeleteButton();
+            $form->disableViewButton();
+            $form->disableViewCheck();
+            $form->disableEditingCheck();
+            $form->disableCreatingCheck();
+        });
+    }
+}

+ 14 - 0
app/Admin/Controllers/Program/HomeController.php

xqd
@@ -0,0 +1,14 @@
+<?php
+
+namespace App\Admin\Controllers\Program;
+
+use App\Http\Controllers\Controller;
+use Dcat\Admin\Layout\Content;
+
+class HomeController extends Controller
+{
+    public function index(Content $content)
+    {
+        return $content->body('<div style="text-align: center; font-size: 28px; font-weight: 300">欢迎进入张四爷短剧管理后台</div>');
+    }
+}

+ 58 - 0
app/Admin/Controllers/Program/NavBarController.php

xqd
@@ -0,0 +1,58 @@
+<?php
+
+namespace App\Admin\Controllers\Program;
+
+use App\Models\NavBar;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class NavBarController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new NavBar(), function (Grid $grid) {
+            $grid->column('id')->sortable();
+            $grid->column('name')->editable();
+            $grid->column('icon')->image('', 50);
+            $grid->column('sort')->editable()->sortable();
+            $grid->column('status')->switch('', true);
+
+            $grid->disableCreateButton();
+            $grid->disableBatchDelete();
+            $grid->disableDeleteButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new NavBar(), function (Form $form) {
+            $form->display('id');
+            $form->text('name');
+            $form->image('icon')->saveFullUrl()
+                ->uniqueName()->autoUpload()
+                ->autoSave(false)
+                ->removable(false)
+                ->width(4)->required();
+            $form->number('sort');
+            $form->switch('status');
+
+            $form->disableDeleteButton();
+            $form->disableViewButton();
+            $form->disableViewCheck();
+            $form->disableEditingCheck();
+            $form->disableCreatingCheck();
+        });
+    }
+}

+ 63 - 0
app/Admin/Controllers/Program/TabbarController.php

xqd
@@ -0,0 +1,63 @@
+<?php
+
+namespace App\Admin\Controllers\Program;
+
+use App\Models\Tabbar;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class TabbarController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Tabbar(), function (Grid $grid) {
+            $grid->column('id')->sortable();
+            $grid->column('name')->editable();
+            $grid->column('icon')->image('', 50);
+            $grid->column('selected_icon')->image('', 50);
+            //            $grid->column('sort')->editable()->sortable();
+            $grid->column('status')->switch('', true);
+
+            $grid->disableCreateButton();
+            $grid->disableBatchDelete();
+            $grid->disableDeleteButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Tabbar(), function (Form $form) {
+            $form->display('id');
+            $form->text('name')->required();
+            $form->image('icon')->saveFullUrl()->uniqueName()->autoUpload()
+                ->autoSave(false)
+                ->removable(false)
+                ->width(4)->required();
+
+            $form->image('selected_icon')->saveFullUrl()->uniqueName()->autoUpload()
+            ->autoSave(false)
+            ->removable(false)
+            ->width(4)->required();
+            //            $form->number('sort');
+            $form->switch('status', true);
+
+            $form->disableDeleteButton();
+            $form->disableViewButton();
+            $form->disableViewCheck();
+            $form->disableEditingCheck();
+            $form->disableCreatingCheck();
+        });
+    }
+}

+ 69 - 0
app/Admin/Controllers/PromotionController.php

xqd
@@ -0,0 +1,69 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Config;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class PromotionController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Config(), function (Grid $grid) {
+            $grid->model()->whereIn('id',[14]);
+            $grid->column('id')->sortable();
+            $grid->column('desc','标题');
+            $grid->column('value', '推广背景图')->image();
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Config(), function (Show $show) {
+            $show->field('id');
+            $show->field('key');
+            $show->field('value');
+            $show->field('desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Config(), function (Form $form) {
+            $form->display('id');
+            $form->image('value','推广背景图')->disk('oss')->autoUpload()->saving(function ($res) {
+                    return $res;
+            })->required();
+            $form->display('desc','标题');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 67 - 0
app/Admin/Controllers/ProtocolController.php

xqd
@@ -0,0 +1,67 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Config;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class ProtocolController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Config(), function (Grid $grid) {
+            $grid->model()->whereIn('id',[1,2,10]);
+            $grid->column('id')->sortable();
+            $grid->column('desc','标题');
+            $grid->column('value','值');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Config(), function (Show $show) {
+            $show->field('id');
+            $show->field('key');
+            $show->field('value');
+            $show->field('desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Config(), function (Form $form) {
+            $form->display('id');
+            $form->editor('value','值')->required();
+            $form->display('desc','标题');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 75 - 0
app/Admin/Controllers/RecommendationsController.php

xqd
@@ -0,0 +1,75 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Config;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class RecommendationsController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Config(), function (Grid $grid) {
+            $grid->model()->whereIn('id',[3,13,4]);
+            $grid->column('id')->sortable();
+            $grid->column('desc','标题');
+            $grid->column('value','值')->display(function ($item){
+                if ($this->id == 3){
+                    return $item.'%';
+                }
+                if ($this->id == 4){
+                    return $item.'元';
+                }
+                return $item;
+            });
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Config(), function (Show $show) {
+            $show->field('id');
+            $show->field('key');
+            $show->field('value');
+            $show->field('desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Config(), function (Form $form) {
+            $form->display('id');
+            $form->text('value','值')->required();
+            $form->display('desc','标题');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 82 - 0
app/Admin/Controllers/ServiceController.php

xqd
@@ -0,0 +1,82 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Config;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class ServiceController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Config(), function (Grid $grid) {
+            $grid->model()->whereIn('id',[7,8,9]);
+            $grid->column('id')->sortable();
+            $grid->column('desc','标题');
+            $grid->column('value', '值')->display(function ($item) use ($grid) {
+                // 在这里通过 $this 获取当前行的数据
+                $id = $this->id;
+                // 根据条件判断是否显示值列
+                if ($id == 9) {
+                    return "<a target='_blank' href='https://zhengda.oss-cn-chengdu.aliyuncs.com/".$this->value."'>查看二维码</a>";
+                } else {
+                    return $item;
+                }
+            });
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Config(), function (Show $show) {
+            $show->field('id');
+            $show->field('key');
+            $show->field('value');
+            $show->field('desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Config(), function (Form $form) {
+            $form->display('id');
+            if ($form->model()->id == 9){
+                $form->image('value','二维码')->disk('oss')->autoUpload()->saving(function ($res) {
+                    return $res;
+                })->required();
+            }else{
+                $form->text('value','值')->required();
+            }
+            $form->display('desc','标题');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 72 - 0
app/Admin/Controllers/Setting/PayConfigController.php

xqd
@@ -0,0 +1,72 @@
+<?php
+
+namespace App\Admin\Controllers\Setting;
+
+use App\Models\PayConfig;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class PayConfigController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(PayConfig::with(['setting']), function (Grid $grid) {
+            $grid->disableColumnSelector();
+            $grid->column('douyin_app_id')->editable();
+            $grid->column('douyin_app_secret')->display('*****');
+            $grid->column('douyin_salt')->display('*****');
+            $grid->column('douyin_token')->display('*****');
+
+            $grid->column('kuaishou_app_id')->editable();
+            $grid->column('kuaishou_app_secret')->display('*****');
+            $grid->column('mini_app_id')->editable();
+            $grid->column('mini_app_key')->display('*****');
+            $grid->column('wechat_mch_id')->editable();
+            $grid->column('wechat_mch_key')->display('*****');
+
+            $grid->column('setting.is_watch_auto_pay', trans('setting.fields.is_watch_auto_pay'))->switch();
+
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+            $grid->disableRowSelector();
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(PayConfig::with(['setting']), function (Form $form) {
+            $form->display('id');
+            $form->text('douyin_app_id')->required();
+            $form->text('douyin_app_secret')->required();
+            $form->text('douyin_salt')->required();
+            $form->text('douyin_token')->required();
+
+            $form->text('kuaishou_app_id');
+            $form->text('kuaishou_app_secret');
+            $form->text('mini_app_id');
+            $form->text('mini_app_key');
+            $form->text('wechat_mch_id');
+            $form->text('wechat_mch_key');
+
+            $form->switch('setting.is_watch_auto_pay', trans('setting.fields.is_watch_auto_pay'))->default(0);
+
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+            $form->disableListButton();
+            $form->disableEditingCheck();
+            $form->disableViewCheck();
+        });
+    }
+}

+ 128 - 0
app/Admin/Controllers/Setting/SettingController.php

xqd
@@ -0,0 +1,128 @@
+<?php
+
+namespace App\Admin\Controllers\Setting;
+
+use App\Models\Setting;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class SettingController extends AdminController
+{
+    public const TYPE_BASIC = 'basic'; // 基础设置
+    public const TYPE_TIPS  = 'tips'; // 消息设置
+    public const TYPE_GOLD  = 'gold'; // 金币设置
+    public const TYPE_ROLE  = 'role'; // 会员权限
+    public const TYPE_SIGN  = 'sign'; // 签到权限
+
+    protected $type = '';
+
+    public function __construct()
+    {
+        $route      = \request()->route();
+        $arr        = explode('/', $route->uri);
+        $this->type = $arr[3];
+    }
+
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Setting(), function (Grid $grid) {
+            $grid->column('id')->sortable();
+            if (self::TYPE_BASIC == $this->type) {
+                $grid->column('name');
+                $grid->column('logo')->image('', 150);
+                $grid->column('contact')->editable();
+                $grid->column('is_review')->switch();
+            } elseif (self::TYPE_TIPS == $this->type) {
+                $grid->column('tips')->editable();
+            } elseif (self::TYPE_GOLD == $this->type) {
+                $grid->column('recharge_bg_img')->image('', 150);
+                $grid->column('recharge_button_txt');
+                $grid->column('recharge_desc');
+            } elseif (self::TYPE_ROLE == $this->type) {
+                $grid->column('vip_role')->radio(config('global.vip_role'));
+            } elseif (self::TYPE_SIGN == $this->type) {
+                $grid->column('is_open_sign')->radio(config('global.open_sign'));
+            }
+
+            if (self::TYPE_ROLE == $this->type || self::TYPE_SIGN == $this->type) {
+                $grid->disableEditButton();
+                $grid->disableActions();
+            }
+
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+            $grid->disableRowSelector();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Setting(), function (Show $show) {
+            $show->field('id');
+            $show->field('name');
+            $show->field('logo');
+            $show->field('contact');
+            $show->field('tips');
+            $show->field('is_watch_auto_pay');
+            $show->field('recharge_bg_img');
+            $show->field('recharge_button_txt');
+            $show->field('recharge_desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Setting(), function (Form $form) {
+            $form->display('id');
+            if (self::TYPE_BASIC == $this->type) {
+                $form->text('name');
+                $form->image('logo')->saveFullUrl()
+                    ->uniqueName()->autoUpload()
+                    ->autoSave(false)
+                    ->removable(false)
+                    ->width(4);
+                $form->text('contact')->minLength(11)->maxLength(11);
+                $form->radio('is_review')->options(config('global.bool_status'))->default(0);
+                $form->editor('protocol');
+            } elseif (self::TYPE_TIPS == $this->type) {
+                $form->text('tips');
+            } elseif (self::TYPE_GOLD == $this->type) {
+                $form->image('recharge_bg_img')->saveFullUrl()
+                    ->uniqueName()->autoUpload()
+                    ->autoSave(false)
+                    ->removable(false)
+                    ->width(4);
+                $form->text('recharge_button_txt')->maxLength(10);
+                $form->textarea('recharge_desc');
+            } elseif (self::TYPE_ROLE == $this->type) {
+                $form->radio('vip_role')->options(config('global.vip_role'))->default(1);
+            }
+
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+            $form->disableListButton();
+            $form->disableEditingCheck();
+            $form->disableViewCheck();
+        });
+    }
+}

+ 88 - 0
app/Admin/Controllers/Share/ShareConfigController.php

xqd
@@ -0,0 +1,88 @@
+<?php
+
+namespace App\Admin\Controllers\Share;
+
+use App\Models\ShareConfig;
+use App\Models\VipCombo;
+use Dcat\Admin\Form;
+use Dcat\Admin\Http\JsonResponse;
+use Dcat\Admin\Layout\Column;
+use Dcat\Admin\Layout\Content;
+use Dcat\Admin\Layout\Row;
+use Dcat\Admin\Http\Controllers\AdminController;
+use Dcat\Admin\Widgets\Form as WidgetForm;
+use Dcat\Admin\Widgets\Box;
+
+class ShareConfigController extends AdminController
+{
+    public function index(Content $content)
+    {
+        return $content
+            ->title(trans('share-config.title'))
+            ->body(function (Row $row) {
+                $row->column(12, function (Column $column) {
+                    $form   = new WidgetForm();
+                    $config = ShareConfig::first();
+                    $form->width(9);
+                    $form->hidden('id', 'ID')->value($config->id);
+                    $form->radio('become_type', '成为分销条件')
+                        ->options(config('global.become_type'))
+                        ->when(2, function (WidgetForm $form) use ($config) {
+                            $vipCombo = VipCombo::select(['id', 'name'])->get()->toArray();
+                            $form->select('become_vip_id', '对应会员')
+                                ->options(array_column($vipCombo, 'name', 'id'))
+                                ->default($config->become_vip_id)
+                                ->width(3)
+                                ->required();
+                        })
+                        ->when(3, function (WidgetForm $form) use ($config) {
+                            $form->decimal('become_gold', '对应金币')->placeholder('1231')
+                                ->value($config->become_gold)
+                                ->width(3)
+                                ->required();
+                        })
+                        ->default($config->become_type)->required();
+
+                    $form->radio('share_type', '分佣类型')
+                        ->options(config('global.share_type'))
+                        ->when(1, function (WidgetForm $form) use ($config) {
+                            $form->rate('share_percent', '分销百分比')->default($config->share_number)->required()->width(3);
+                        })
+                        ->when(2, function (WidgetForm $form) use ($config) {
+                            $form->decimal('share_price', '分销金额')->default($config->share_number)->required()->width(3);
+                        })
+                        ->default($config->share_type)
+                        ->required();
+
+                    $form->decimal('withdraw_min', '最少提现额度')->value($config->withdraw_min)->required()->width(3);
+                    $form->decimal('withdraw_max', '每日提现上限')->value($config->withdraw_max)->required()->width(3);
+                    $form->rate('withdraw_discount', '提现手续费')->value($config->withdraw_discount)->required()->width(3);
+                    $form->editor('withdraw_desc', '提现说明')->value($config->withdraw_desc);
+
+                    $form->action('share/setting/save');
+                    $column->append(Box::make(trans('admin.edit'), $form));
+                    $form->disableResetButton();
+                });
+            });
+    }
+
+    public function save(): JsonResponse
+    {
+        $req                       = request()->post();
+        $config                    = ShareConfig::first();
+        $config->become_type       = $req['become_type'];
+        $config->become_vip_id     = $req['become_vip_id'];
+        $config->become_gold       = $req['become_gold'];
+        $config->share_type        = $req['share_type'];
+        $config->share_number      = 1 == $req['share_type'] ? $req['share_percent'] : $req['share_price'];
+        $config->withdraw_min      = $req['withdraw_min'];
+        $config->withdraw_max      = $req['withdraw_max'];
+        $config->withdraw_discount = $req['withdraw_discount'];
+        $config->withdraw_desc     = $req['withdraw_desc'];
+        $config->save();
+        $form     = new Form();
+        $response = $form->response();
+
+        return $response->success(__('admin.save_succeeded'));
+    }
+}

+ 96 - 0
app/Admin/Controllers/Share/UserController.php

xqd
@@ -0,0 +1,96 @@
+<?php
+
+namespace App\Admin\Controllers\Share;
+
+use App\Models\User;
+use App\Models\UserShare;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class UserController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(User::with(['parent.info', 'info'])->withCount('child'), function (Grid $grid) {
+            $grid->model()->where('is_share', 1)
+                ->orderByDesc('become_share_at');
+            $grid->column('id', '用户ID')->sortable();
+            $grid->column('avatar', '基本信息')->display(function () {
+                $str = '';
+                $str .= "<div style='margin-right:10px;display: flex;align-items: center'>";
+                $str .= '<img data-action="preview-img" src="' . $this->avatar . '" onerror="this.src=\'https://fourtiao.oss-cn-beijing.aliyuncs.com/zhangsiye/images/6b40343b27263be34cf3212bf44f74c3.png\'" style="height:50px;width:50px;cursor:pointer;margin-right:10px;" class="img img-thumbnail">';
+                $str .= '<div>';
+                $str .= '<p style="margin-bottom: 5px">' . $this->nickname . '</p>';
+                $str .= '<p style="margin-bottom: 5px">' . $this->mobile . '</p>';
+                $str .= '</div>';
+                $str .= '</div>';
+
+                return $str;
+            });
+            $grid->column('info.platform', '所属平台')
+                ->using(config('global.platform'))
+                ->label([1 => 'primary', 2 => 'success', 3 => 'info']);
+            $grid->column('income');
+            $grid->column('total_income');
+            $grid->column('parent_id')->display(function () {
+                return $this->parent ? $this->parent->nickname : '-';
+            })->label('primary');
+            $grid->column('child_count');
+            $grid->column('become_share_at');
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->equal('info.platform', '所属平台')->select(config('global.platform'))->width(3);
+                $filter->like('nickname', '昵称')->width(3);
+                $filter->equal('mobile', '手机号')->width(3);
+            });
+
+            $grid->disableCreateButton();
+            $grid->disableDeleteButton();
+            $grid->disableRowSelector();
+            $grid->disableActions();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new UserShare(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('child_id');
+            $show->field('income');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new UserShare(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('child_id');
+            $form->text('income');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 116 - 0
app/Admin/Controllers/Share/UserWithdrawController.php

xqd
@@ -0,0 +1,116 @@
+<?php
+
+namespace App\Admin\Controllers\Share;
+
+use App\Admin\Actions\Grid\WithdrawReview;
+use App\Models\UserWithdraw;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class UserWithdrawController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(UserWithdraw::with('user'), function (Grid $grid) {
+            $grid->model()->orderBy('status')->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('avatar', '基本信息')->display(function () {
+                $str = '';
+                $str .= "<div style='margin-right:10px;display: flex;align-items: center'>";
+                $str .= '<img data-action="preview-img" src="' . $this->user->avatar . '" onerror="this.src=\'https://fourtiao.oss-cn-beijing.aliyuncs.com/zhangsiye/images/6b40343b27263be34cf3212bf44f74c3.png\'" style="height:50px;width:50px;cursor:pointer;margin-right:10px;" class="img img-thumbnail">';
+                $str .= '<div>';
+                $str .= '<p style="margin-bottom: 5px">' . $this->user->nickname . '</p>';
+                $str .= '</div>';
+                $str .= '</div>';
+
+                return $str;
+            });
+            $grid->column('name');
+            $grid->column('phone_num');
+            $grid->column('type')->using(config('global.withdraw_type'))
+                ->label(['default', 'primary', 'success', 'info']);
+            $grid->column('account');
+            $grid->column('bank_name');
+            $grid->column('price')->label('primary');
+            $grid->column('discount')->label('warning');
+            $grid->column('real', '实际打款金额')->display(function () {
+                return number_format($this->price - $this->discount, 2);
+            })->label('success');
+            $grid->column('status', '状态')
+                ->using(config('global.withdraw_status'))
+                ->label(['default', 'primary', 'success', 'danger'])
+            ;
+            $grid->column('created_at', '申请时间');
+            $grid->column('review_at', '审核时间');
+            $grid->column('withdraw_at', '打款时间');
+            $grid->actions(function (Grid\Displayers\Actions $actions) {
+                if (2 != $actions->row->status) {
+                    $actions->append(new WithdrawReview(UserWithdraw::class, $actions->row->id, $actions->row->status));
+                }
+            });
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->between('created_at', '申请时间')->date()->width(3);
+                $filter->equal('status', '状态')->select(config('global.withdraw_status'))->width(3);
+                $filter->like('nickname', '昵称')->width(2);
+                $filter->like('name', '姓名')->width(2);
+                $filter->equal('mobile', '手机号')->width(2);
+            });
+
+            $grid->disableCreateButton();
+            $grid->disableEditButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new UserWithdraw(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('name');
+            $show->field('type');
+            $show->field('account');
+            $show->field('price');
+            $show->field('desc');
+            $show->field('status');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new UserWithdraw(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('name');
+            $form->text('type');
+            $form->text('account');
+            $form->text('price');
+            $form->text('desc');
+            $form->text('status');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 83 - 0
app/Admin/Controllers/ShareAController.php

xqd
@@ -0,0 +1,83 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Config;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class ShareAController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Config(), function (Grid $grid) {
+            $grid->model()->whereIn('id',[22,24,12]);
+            $grid->column('id')->sortable();
+            $grid->column('desc','标题');
+            $grid->column('value', '值')->display(function ($item) use ($grid) {
+                // 在这里通过 $this 获取当前行的数据
+                $id = $this->id;
+                // 根据条件判断是否显示值列
+                if ($id == 12) {
+                    return $item.'%';
+                } else {
+                    return $item;
+                }
+            });
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Config(), function (Show $show) {
+            $show->field('id');
+            $show->field('key');
+            $show->field('value');
+            $show->field('desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Config(), function (Form $form) {
+            $form->display('id');
+            if ($form->model()->id == 22){
+                $form->editor('value','值')->required();
+            }elseif ($form->model()->id == 12){
+                $form->number('value','值')->required();
+            }
+            else{
+                $form->textarea('value','值')->required();
+            }
+            $form->display('desc','标题');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 84 - 0
app/Admin/Controllers/ShareController.php

xqd
@@ -0,0 +1,84 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Config;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class ShareController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Config(), function (Grid $grid) {
+            $grid->model()->whereIn('id',[15,16,17]);
+            $grid->column('id')->sortable();
+            $grid->column('desc','标题');
+            $grid->column('value', '值')->display(function ($item) use ($grid) {
+                // 在这里通过 $this 获取当前行的数据
+                $id = $this->id;
+                // 根据条件判断是否显示值列
+                if ($id == 16) {
+                    return "<img style='width:50px;'  src='https://zhengda.oss-cn-chengdu.aliyuncs.com/".$this->value."'/>";
+                } else {
+                    return $item;
+                }
+            });
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Config(), function (Show $show) {
+            $show->field('id');
+            $show->field('key');
+            $show->field('value');
+            $show->field('desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Config(), function (Form $form) {
+            $form->display('id');
+            if ($form->model()->id == 16){
+                $form->image('value','二维码')->disk('oss')->autoUpload()->saving(function ($res) {
+                    return $res;
+                })->required();
+            }elseif ($form->model()->id == 17){
+                $form->textarea('value','值')->required();
+            }else{
+                $form->text('value','值')->required();
+            }
+            $form->display('desc','标题');
+            $form->disableViewButton();
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 77 - 0
app/Admin/Controllers/ShareDataController.php

xqd
@@ -0,0 +1,77 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Share;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class ShareDataController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Share(), function (Grid $grid) {
+            $grid->model()->with(['inviteData','pidData'])->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('inviteData.name','邀请人昵称');
+            $grid->column('inviteData.avatar','邀请人头像')->image('',80,80);
+            $grid->column('pidData.name','被邀请人昵称');
+            $grid->column('pidData.avatar','被邀请人头像')->image('',80,80);
+            $grid->column('diamond','获得次数');
+            $grid->column('desc','备注');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableViewButton();
+            $grid->disableCreateButton();
+            $grid->disableEditButton();
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->like('desc');
+            });
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Share(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('pid');
+            $show->field('diamond');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Share(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('pid');
+            $form->text('diamond');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 119 - 0
app/Admin/Controllers/ShareUserController.php

xqd
@@ -0,0 +1,119 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\User;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class ShareUserController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new User(), function (Grid $grid) {
+            $grid->model()->withCount('invite_data')->where('is_share',1)->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('share_name','分销商姓名');
+            $grid->column('share_phone','分销商电话');
+            $grid->column('last_login_ip','最后登录IP');
+            $grid->column('last_login_time','最后登录时间');
+            $grid->column('is_black','是否拉黑')->switch();
+            $grid->column('diamond','剩余次数');
+            $grid->column('income','可提现佣金');
+            $grid->column('invite_data_count','已邀请人数');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->quickSearch(['name', 'id', 'mobile'])->placeholder('搜索...');
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->equal('id');
+                $filter->like('share_phone');
+                $filter->between('created_at')->datetime();
+            });
+            $grid->disableCreateButton();
+            $grid->disableDeleteButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new User(), function (Show $show) {
+            $show->model()->with('userShare');
+            $show->field('id');
+            $show->field('name');
+            $show->field('open_id','微信openid');
+            $show->field('avatar')->image();
+            $show->field('mobile');
+            $show->field('online');
+            $show->field('last_login_ip');
+            $show->field('last_login_time');
+            $show->field('is_black')->as(function ($item){
+                return $item ? '拉黑' : '正常';
+            });
+            $show->field('diamond');
+            $show->field('is_share')->as(function ($item){
+                return $item ? '是推广人' : '不是推广人';
+            });
+            $show->field('share_name');
+            $show->field('share_phone');
+            $show->field('userShare.name','推广人昵称(被)');
+            $show->field('share_date');
+            $show->field('income');
+            $show->field('qr_code')->image();
+            $show->field('created_at');
+            $show->field('updated_at');
+            $show->disableDeleteButton();
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new User(), function (Form $form) {
+            $form->display('id');
+            $form->text('name');
+//            $form->text('open_id');
+            $form->image('avatar')->disk('oss')->autoUpload()->saving(function ($res) {
+                return $res;
+            })->required();;
+            $form->text('mobile');
+//            $form->text('status');
+//            $form->text('online');
+//            $form->text('last_login_ip');
+//            $form->text('register_ip');
+//            $form->text('last_login_time');
+            $form->switch('is_black');
+//            $form->text('sessionKey');
+            $form->text('diamond');
+            $form->switch('is_share');
+            $form->text('share_name');
+            $form->text('share_phone');
+//            $form->text('share_pid');
+//            $form->text('share_date');
+            $form->text('income');
+//            $form->text('qr_code');
+//
+//            $form->display('created_at');
+//            $form->display('updated_at');
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 189 - 0
app/Admin/Controllers/TaskListController.php

xqd
@@ -0,0 +1,189 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\TaskList;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+use TencentCloud\Asr\V20190614\Models\Task;
+
+class TaskListController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new TaskList(), function (Grid $grid) {
+            $grid->model()->with('userData')->where('is_handpick',0)->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('userData.name','昵称');
+            $grid->column('userData.mobile','手机号');
+            $grid->column('userData.avatar','头像')->image('',40,40);
+            $grid->column('title')->display(function ($initContent) {
+                $maxLength = 10; // 你希望显示的最大字符数
+                $ellipsis = '...'; // 省略号
+
+                // 使用 mb_substr 截取字符串
+                $displayContent = mb_substr($initContent, 0, $maxLength);
+
+                // 如果原始字符串长度超过最大长度,添加省略号
+                if (mb_strlen($initContent) > $maxLength) {
+                    $displayContent .= $ellipsis;
+                }
+
+                return $displayContent;
+            });;
+//            $grid->column('role_id');
+//            $grid->column('content');
+            $grid->column('state')->using(TaskList::$state)
+                ->dot(
+                    [
+                        0 => 'primary',
+                        1 => 'secondary',
+                        2 => 'danger',
+                        3 => 'warning',
+                        4 => 'info',
+                        5 => 'light',
+                        6 => 'success',
+                    ],
+                    'danger' // 第二个参数为默认值
+                );;
+//            $grid->column('image')->image('',80,80);
+//            $grid->column('init_content')->width('300px')->display(function ($initContent) {
+//                $maxLength = 10; // 你希望显示的最大字符数
+//                $ellipsis = '...'; // 省略号
+//
+//                // 使用 mb_substr 截取字符串
+//                $displayContent = mb_substr($initContent, 0, $maxLength);
+//
+//                // 如果原始字符串长度超过最大长度,添加省略号
+//                if (mb_strlen($initContent) > $maxLength) {
+//                    $displayContent .= $ellipsis;
+//                }
+//
+//                return $displayContent;
+//            });
+//            $grid->column('keyword');
+//            $grid->column('sd_image');
+//            $grid->column('desc');
+//            $grid->column('sd_id')->image('',80,80);
+            $grid->column('surplus_diamond','剩余次数');
+//            $grid->column('nickname');
+//            $grid->column('plot');
+            $grid->column('is_handpick')->switch();
+            $grid->column('sort')->sortable();
+            $grid->column('pdf_path')->display(function ($pdfPath) {
+//                $downloadUrl = url($pdfPath);
+                if (!empty($pdfPath)){
+                    return "<a target='_blank' href='".$pdfPath."'>下载PDF</a>";
+                }else{
+                    return "还未生成PDF";
+                }
+            });
+            $grid->column('image_path','图片地址')->display(function ($image_path) {
+//                $downloadUrl = url($pdfPath);
+                if (!empty($image_path)){
+                    return "<a target='_blank' href='".$image_path."'>浏览图片</a>";
+                }else{
+                    return "还未生成图片";
+                }
+            });
+//            $grid->column('pinyin_content');
+
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableEditButton();
+            // $grid->disableDeleteButton();
+            $grid->disableViewButton();
+            $grid->disableCreateButton();
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->between('created_at')->datetime();
+                $filter->equal('is_handpick','是否精选')->select([0 => '不是精选',1 => '精选']);
+                $filter->where('mobile', function ($query) {
+                    $query->whereHas('userData', function ($query) {
+                        $query->where('mobile', 'like', "%{$this->input}%");
+                    });
+                }, '用户手机号');
+                $filter->where('name', function ($query) {
+                    $query->whereHas('userData', function ($query) {
+                        $query->where('name', 'like', "%{$this->input}%");
+                    });
+                }, '用户昵称');
+
+            });
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new TaskList(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('title');
+            $show->field('role_id');
+            $show->field('content');
+            $show->field('state');
+            $show->field('image');
+            $show->field('init_content');
+            $show->field('keyword');
+            $show->field('sd_image');
+            $show->field('desc');
+            $show->field('sd_id');
+            $show->field('surplus_diamond');
+            $show->field('nickname');
+            $show->field('plot');
+            $show->field('is_handpick');
+            $show->field('sort');
+            $show->field('pinyin_content');
+            $show->field('pdf_path');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new TaskList(), function (Form $form) {
+            $form->display('id');
+            $form->text('user_id');
+            $form->text('title');
+            $form->text('role_id');
+            $form->text('content');
+            $form->text('state');
+            $form->text('image');
+            $form->text('init_content');
+            $form->text('keyword');
+            $form->text('sd_image');
+            $form->text('desc');
+            $form->text('sd_id');
+            $form->text('surplus_diamond');
+            $form->text('nickname');
+            $form->text('plot');
+            $form->text('is_handpick');
+            $form->text('sort');
+            $form->text('pinyin_content');
+            $form->text('pdf_path');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 143 - 0
app/Admin/Controllers/UserController.php

xqd
@@ -0,0 +1,143 @@
+<?php
+
+namespace App\Admin\Controllers;
+use Illuminate\Support\Facades\Log;
+use App\Models\User;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class UserController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new User(), function (Grid $grid) {
+            $grid->model()->withCount('creation')->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('name');
+//            $grid->column('open_id');
+            $grid->column('avatar')->image('',80,80);
+            $grid->column('mobile');
+//            $grid->column('status')->switch();
+            $grid->column('online','是否在线')->switch();
+            $grid->column('last_login_ip','最后登录IP');
+//            $grid->column('register_ip');
+            $grid->column('last_login_time','最后登录时间');
+            $grid->column('is_black','是否拉黑')->switch();
+//            $grid->column('sessionKey');
+            $grid->column('diamond','剩余次数');
+            $grid->column('is_share','是否分销商')->using([0 => '不是分销商', 1 => '是分销商'])
+                ->dot(
+                    [
+                        0 => 'danger',
+                        1 => 'success',
+                    ],
+                    'danger' // 第二个参数为默认值
+                );
+//            $grid->column('share_name');
+//            $grid->column('share_phone');
+            $grid->column('share_pid','上级邀请用户Id')->display(function($item){
+                return $item ? $item : '没有上级邀请';
+            });
+//            $grid->column('share_date');
+            $grid->column('income','可提现佣金');
+            $grid->column('creation_count','创作次数');
+            $grid->column('qr_code','邀请二维码')->image();
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->quickSearch(['name', 'id', 'mobile'])->placeholder('搜索...');
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->equal('id');
+                $filter->like('mobile');
+                $filter->between('created_at')->datetime();
+            });
+            $grid->disableCreateButton();
+            $grid->disableDeleteButton();
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new User(), function (Show $show) {
+            $show->model()->with('userShare');
+            $show->field('id');
+            $show->field('name');
+            $show->field('open_id','微信openid');
+            $show->field('avatar')->image();
+            $show->field('mobile');
+            $show->field('online');
+            $show->field('last_login_ip');
+            $show->field('last_login_time');
+            $show->field('is_black')->as(function ($item){
+                return $item ? '拉黑' : '正常';
+            });
+            $show->field('diamond');
+            $show->field('is_share')->as(function ($item){
+                return $item ? '是推广人' : '不是推广人';
+            });
+            $show->field('share_name');
+            $show->field('share_phone');
+            $show->field('userShare.name','推广人昵称(被)');
+            $show->field('share_date');
+            $show->field('income');
+            $show->field('qr_code')->image();
+            $show->field('created_at');
+            $show->field('updated_at');
+            $show->disableDeleteButton();
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new User(), function (Form $form) {
+            $form->display('id');
+            $form->text('name');
+//            $form->text('open_id');
+            $form->image('avatar')->disk('oss')->autoUpload()->saving(function ($res) {
+                return $res;
+            });
+            $form->text('mobile');
+//            $form->text('status');
+//            $form->text('online');
+//            $form->text('last_login_ip');
+//            $form->text('register_ip');
+//            $form->text('last_login_time');
+            $form->switch('is_black');
+//            $form->text('sessionKey');
+            $form->number('diamond')->saving(function ($item) use(&$form){
+                Log::warning("用户次数后台变动:用户id:{$form->model()->id}变动前:{$form->model()->diamond} 变动后:{$item}");
+                return $item;
+            });
+            $form->switch('is_share');
+            $form->text('share_name');
+            $form->text('share_phone');
+//            $form->text('share_pid');
+//            $form->text('share_date');
+            $form->text('income');
+//            $form->text('qr_code');
+//
+//            $form->display('created_at');
+//            $form->display('updated_at');
+            $form->disableDeleteButton();
+        });
+    }
+}

+ 55 - 0
app/Admin/Controllers/UserVipRecordController.php

xqd
@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\UserVipRecord;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class UserVipRecordController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(UserVipRecord::with(['user.info', 'combo']), function (Grid $grid) {
+            $grid->model()->where('status', 1)->orderByDesc('created_at');
+            $grid->column('id')->sortable();
+            $grid->column('pay_id');
+            $grid->column('user_id')->display(function () {
+                $str = '';
+                $str .= "<div style='margin-right:10px;display: flex;align-items: center'>";
+                $str .= '<img data-action="preview-img" src="' . @$this->user->avatar . '" onerror="this.src=\'https://fourtiao.oss-cn-beijing.aliyuncs.com/zhangsiye/images/6b40343b27263be34cf3212bf44f74c3.png\'" style="height:50px;width:50px;cursor:pointer;margin-right:10px;" class="img img-thumbnail">';
+                $str .= '<div>';
+                $str .= '<p style="margin-bottom: 5px">' . @$this->user->nickname . '</p>';
+                $str .= '<p style="margin-bottom: 0px">' . @$this->user->mobile . '</p>';
+                $str .= '</div>';
+                $str .= '</div>';
+
+                return $str;
+            });
+            $grid->column('user.info.platform', '所属平台')
+                ->using(config('global.platform'))
+                ->label([1 => 'primary', 2 => 'success', 3 => 'info']);
+            $grid->column('combo.name', '购买套餐');
+            $grid->column('valid_day');
+            $grid->column('created_at', '支付时间')->sortable();
+
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->like('user.nickname', '昵称')->width(3);
+                $filter->equal('user.mobile', '手机号')->width(3);
+                $filter->equal('user.info.platform', '所属平台')->select(config('global.platform'))->width(3);
+            });
+
+            $grid->disableActions();
+            $grid->disableCreateButton();
+            $grid->disableDeleteButton();
+            $grid->disableRowSelector();
+            $grid->disableViewButton();
+        });
+    }
+}

+ 111 - 0
app/Admin/Controllers/UsersRoleController.php

xqd
@@ -0,0 +1,111 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\User;
+use App\Models\UserRole;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class UsersRoleController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new UserRole(), function (Grid $grid) {
+            $grid->model()->with('userData')->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('userData.name','昵称');
+            $grid->column('userData.avatar','头像')->image('',80,80);
+            $grid->column('userData.mobile','手机号');
+            $grid->column('name');
+            $grid->column('sex');
+            $grid->column('age');
+            $grid->column('star');
+            $grid->column('level','年级');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+            $grid->export() ->rows(function ($rows) {
+                $exportRows = [];
+                foreach ($rows as $index => $row) {
+                    // 确保 $row->userData 不为 null
+                    if ($row->userData !== null) {
+                        $user = $row->userData;
+                        $exportRow = [
+                            'id' => $row->id,
+                            'userData.name' => $user->name,  // Add 'userData.name' to export
+                            'userData.avatar' => $user->avatar,  // Add 'userData.avatar' to export
+                            'userData.mobile' => $user->mobile,  // Add 'userData.avatar' to export
+                            'name' => $row->name,
+                            'sex' => $row->sex,
+                            'age' => $row->age,
+                            'star' => $row->star,
+                            'level' => $row->level,
+                            'created_at' => $row->created_at,
+                            'updated_at' => $row->updated_at,
+                        ];
+                        $exportRows[] = $exportRow;
+                    }
+                }
+//                var_dump($exportRows);
+                return collect($exportRows);
+            });
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->equal('level')->select(UserRole::$level);
+                $filter->like('name');
+            });
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new UserRole(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('name');
+            $show->field('sex');
+            $show->field('age');
+            $show->field('star');
+            $show->field('level');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new UserRole(), function (Form $form) {
+            $form->display('id');
+//            $form->text('user_id');
+            $form->text('name');
+            $form->select('sex')->options(['男孩' => '男孩','女孩' => '女孩']);
+            $form->number('age');
+            $form->text('star');
+            $form->select('level')->options(UserRole::$level);
+            $form->disableViewButton();
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 209 - 0
app/Admin/Controllers/WinnowController.php

xqd
@@ -0,0 +1,209 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Http\Controllers\V1\Ai\AiController;
+use App\Models\TaskList;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+use TencentCloud\Asr\V20190614\Models\Task;
+
+class WinnowController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new TaskList(), function (Grid $grid) {
+            $grid->model()->with('userData')->where('is_handpick',1)->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('userData.name','昵称');
+//            $grid->column('userData.mobile','手机号');
+            $grid->column('userData.avatar','头像')->image('',40,40);
+            $grid->column('is_handpick')->switch();
+            $grid->column('title')->display(function ($initContent) {
+                $maxLength = 10; // 你希望显示的最大字符数
+                $ellipsis = '...'; // 省略号
+
+                // 使用 mb_substr 截取字符串
+                $displayContent = mb_substr($initContent, 0, $maxLength);
+
+                // 如果原始字符串长度超过最大长度,添加省略号
+                if (mb_strlen($initContent) > $maxLength) {
+                    $displayContent .= $ellipsis;
+                }
+
+                return $displayContent;
+            });;
+//            $grid->column('role_id');
+//            $grid->column('content');
+//            $grid->column('image')->image('',80,80);
+//            $grid->column('init_content')->width('300px')->display(function ($initContent) {
+//                $maxLength = 10; // 你希望显示的最大字符数
+//                $ellipsis = '...'; // 省略号
+//
+//                // 使用 mb_substr 截取字符串
+//                $displayContent = mb_substr($initContent, 0, $maxLength);
+//
+//                // 如果原始字符串长度超过最大长度,添加省略号
+//                if (mb_strlen($initContent) > $maxLength) {
+//                    $displayContent .= $ellipsis;
+//                }
+//
+//                return $displayContent;
+//            });
+//            $grid->column('keyword');
+//            $grid->column('sd_image');
+//            $grid->column('desc');
+//            $grid->column('sd_id')->image('',80,80);
+            $grid->column('surplus_diamond');
+//            $grid->column('nickname');
+//            $grid->column('plot');
+            $grid->column('sort')->sortable();
+            $grid->column('pdf_path')->display(function ($pdfPath) {
+//                $downloadUrl = url($pdfPath);
+                if (!empty($pdfPath)){
+                    return "<a target='_blank' href='".$pdfPath."'>下载PDF</a>";
+                }else{
+                    return "还未生成PDF";
+                }
+            });
+            $grid->column('image_path','图片地址')->display(function ($image_path) {
+//                $downloadUrl = url($pdfPath);
+                if (!empty($image_path)){
+                    return "<a target='_blank' href='".$image_path."'>浏览图片</a>";
+                }else{
+                    return "还未生成图片";
+                }
+            });
+//            $grid->column('pinyin_content');
+
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+//            $grid->disableEditButton();
+//            $grid->disableDeleteButton();
+            $grid->disableViewButton();
+//            $grid->disableCreateButton();
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->between('created_at')->datetime();
+                $filter->like('title');
+//                $filter->equal('is_handpick','是否精选')->select([0 => '不是精选',1 => '精选']);
+//                $filter->where('mobile', function ($query) {
+//                    $query->whereHas('userData', function ($query) {
+//                        $query->where('mobile', 'like', "%{$this->input}%");
+//                    });
+//                }, '用户手机号');
+//                $filter->where('name', function ($query) {
+//                    $query->whereHas('userData', function ($query) {
+//                        $query->where('name', 'like', "%{$this->input}%");
+//                    });
+//                }, '用户昵称');
+
+            });
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new TaskList(), function (Show $show) {
+            $show->field('id');
+            $show->field('user_id');
+            $show->field('title');
+            $show->field('role_id');
+            $show->field('content');
+            $show->field('state');
+            $show->field('image');
+            $show->field('init_content');
+            $show->field('keyword');
+            $show->field('sd_image');
+            $show->field('desc');
+            $show->field('sd_id');
+            $show->field('surplus_diamond');
+            $show->field('nickname');
+            $show->field('plot');
+            $show->field('is_handpick');
+            $show->field('sort');
+            $show->field('pinyin_content');
+            $show->field('pdf_path');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new TaskList(), function (Form $form) {
+            $form->display('id');
+            // $form->hidden('user_id')->default(0);
+//            $form->text('title');
+//            $form->text('role_id');
+//            $form->text('content');
+            // $form->hidden('state')->default(6);
+            $form->text('title');
+            // $form->image('image')->disk('oss')->autoUpload()->saving(function ($res) {
+            //     if (strstr($res, 'https')) {
+            //         return $res;
+            //     }else{
+            //         return 'https://zhengda.oss-cn-chengdu.aliyuncs.com/'.$res;
+            //     }
+            // })->required();
+            // $form->image('sd_image')->disk('oss')->autoUpload()->saving(function ($res) {
+            //     if (strstr($res, 'https')) {
+            //         return $res;
+            //     }else{
+            //         return 'zhengda.oss-cn-chengdu.aliyuncs.com/'.$res;
+            //     }
+            // })->required();
+//            $form->text('init_content');
+//            $form->text('keyword');
+//            $form->text('sd_image');
+//            $form->text('desc');
+//            $form->text('sd_id');
+//            $form->text('surplus_diamond');
+//            $form->text('nickname');
+//            $form->text('plot');
+            // $form->hidden('is_handpick')->default(1);
+            $form->number('sort');
+            // $form->textarea('pinyin_content')->saving(function ($item) use(&$form){
+            //     // var_dump($form->sd_image);
+            //     $item = preg_replace('/[^\p{L}\p{N}]/u', '', $item);
+            //     return (new AiController())->extracted($item,1,'zhengda.oss-cn-chengdu.aliyuncs.com/'.$form->sd_image);
+            // });
+            // $form->file('pdf_path','PDF')->disk('oss')->autoUpload()->saving(function ($res) {
+            //     if (strstr($res, 'https')) {
+            //         return $res;
+            //     }else{
+            //         return 'https://zhengda.oss-cn-chengdu.aliyuncs.com/'.$res;
+            //     }
+            // })->required();
+            // $form->image('image_path','图片')->disk('oss')->autoUpload()->saving(function ($res) {
+            //     if (strstr($res, 'https')) {
+            //         return $res;
+            //     }else{
+            //         return 'https://zhengda.oss-cn-chengdu.aliyuncs.com/'.$res;
+            //     }
+            // })->required();
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 116 - 0
app/Admin/Controllers/WithdrawController.php

xqd
@@ -0,0 +1,116 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Models\Withdraw;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Http\Controllers\AdminController;
+
+class WithdrawController extends AdminController
+{
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new Withdraw(), function (Grid $grid) {
+            $grid->model()->with('userData')->orderByDesc('id');
+            $grid->column('id')->sortable();
+            $grid->column('userData.name','昵称')->sortable();
+            $grid->column('userData.avatar','昵称')->image('',40,40);
+            $grid->column('userData.mobile','手机号')->sortable();
+            $grid->column('type')->using(Withdraw::$label)
+                ->dot(
+                    [
+                        2 => 'primary',
+                        1 => 'success',
+                        3 => 'danger',
+                    ],
+                    'danger' // 第二个参数为默认值
+                );
+            $grid->column('bank_name');
+            $grid->column('name');
+            $grid->column('number');
+            $grid->column('state')->using(Withdraw::$state)
+                ->dot(
+                    [
+                        0 => 'primary',
+                        1 => 'success',
+                        2 => 'danger',
+                    ],
+                    'danger' // 第二个参数为默认值
+                );;
+//            $grid->column('user_id');
+            $grid->column('amount');
+            $grid->column('procedure');
+            $grid->column('practical_amount');
+            $grid->column('not_desc');
+            $grid->column('created_at');
+            $grid->column('updated_at')->sortable();
+            $grid->disableDeleteButton();
+            $grid->disableCreateButton();
+            $grid->disableViewButton();
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->equal('state')->select(Withdraw::$state);
+                $filter->between('created_at')->datetime();
+            });
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new Withdraw(), function (Show $show) {
+            $show->field('id');
+            $show->field('type');
+            $show->field('bank_name');
+            $show->field('name');
+            $show->field('number');
+            $show->field('state');
+            $show->field('user_id');
+            $show->field('amount');
+            $show->field('procedure');
+            $show->field('practical_amount');
+            $show->field('not_desc');
+            $show->field('created_at');
+            $show->field('updated_at');
+        });
+    }
+
+    /**
+     * Make a form builder.
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new Withdraw(), function (Form $form) {
+            $state = Withdraw::$state;
+            // unset($state[0]);
+            $form->display('id');
+//            $form->select('type')->options(Withdraw::$label)->disable();
+            $form->select('state')
+                ->when(2, function (Form $form) {
+                    $form->textarea('not_desc');
+                })
+                ->options($state);
+            $form->display('amount');
+            $form->display('procedure');
+            $form->display('practical_amount');
+
+            $form->display('created_at');
+            $form->display('updated_at');
+        });
+    }
+}

+ 150 - 0
app/Admin/Metrics/Consume.php

xqd
@@ -0,0 +1,150 @@
+<?php
+
+namespace App\Admin\Metrics;
+
+use App\Models\UserConsumeRecord;
+use Carbon\Carbon;
+use Dcat\Admin\Widgets\Metrics\Card;
+use Illuminate\Contracts\Support\Renderable;
+use Illuminate\Http\Request;
+
+class Consume extends Card
+{
+    /**
+     * 卡片底部内容.
+     *
+     * @var string|Renderable|\Closure
+     */
+    protected $footer;
+
+    /**
+     * 初始化卡片.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $this->title('消费金币');
+        $this->height(300);
+        $this->dropdown([
+            '1'  => '当日新增消费金币',
+            '7'  => '7天新增消费金币',
+            '30' => '当月新增消费金币',
+            '-1' => '总消费金币',
+        ]);
+    }
+
+    /**
+     * 处理请求.
+     *
+     * @return void
+     */
+    public function handle(Request $request)
+    {
+        switch ($request->get('option')) {
+            case '-1':
+                $this->content($this->getConsume());
+                break;
+            case '7':
+                $startAt = Carbon::now()->subDays(7)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $today   = $this->getConsume($startAt, $endAt);
+                $this->content($today);
+
+                break;
+            case '30':
+                $startAt = Carbon::now()->subDays(30)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $today   = $this->getConsume($startAt, $endAt);
+                $this->content($today);
+
+                break;
+            case '1':
+            default:
+                $startAt = Carbon::now()->subDay()->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $today   = $this->getConsume($startAt, $endAt);
+                $this->content($today);
+                break;
+        }
+    }
+
+    private function getConsume($startAt = '', $endAt = ''): int
+    {
+        $res = UserConsumeRecord::when($startAt, function ($query) use ($startAt) {
+            $query->where('created_at', '>', $startAt);
+        })->when($endAt, function ($query) use ($endAt) {
+            $query->where('created_at', '<=', $endAt);
+        })->where('type', 2)
+            ->sum('change');
+
+        return abs($res);
+    }
+
+    /**
+     * @param int $percent
+     *
+     * @return $this
+     */
+    public function up($percent)
+    {
+        return $this->footer(
+            "<i class=\"feather icon-trending-up text-success\"></i> {$percent}% 增加"
+        );
+    }
+
+    /**
+     * @param int $percent
+     *
+     * @return $this
+     */
+    public function down($percent)
+    {
+        return $this->footer(
+            "<i class=\"feather icon-trending-down text-danger\"></i> {$percent}% 减少"
+        );
+    }
+
+    /**
+     * 设置卡片底部内容.
+     *
+     * @param string|Renderable|\Closure $footer
+     *
+     * @return $this
+     */
+    public function footer($footer)
+    {
+        $this->footer = $footer;
+
+        return $this;
+    }
+
+    /**
+     * 渲染卡片内容.
+     *
+     * @return string
+     */
+    public function renderContent()
+    {
+        $content = parent::renderContent();
+
+        return <<<HTML
+<div class="d-flex justify-content-between align-items-center mt-1" style="margin-bottom: 2px">
+    <h2 class="ml-1 font-lg-1">{$content}</h2>
+</div>
+<div class="ml-1 mt-1 font-weight-bold text-80">
+    {$this->renderFooter()}
+</div>
+HTML;
+    }
+
+    /**
+     * 渲染卡片底部内容.
+     *
+     * @return string
+     */
+    public function renderFooter()
+    {
+        return $this->toString($this->footer);
+    }
+}

+ 111 - 0
app/Admin/Metrics/Episodes.php

xqd
@@ -0,0 +1,111 @@
+<?php
+
+namespace App\Admin\Metrics;
+
+use App\Models\Episode;
+use App\Models\EpisodesList;
+use Dcat\Admin\Admin;
+use Dcat\Admin\Widgets\Metrics\Donut;
+
+class Episodes extends Donut
+{
+    protected $labels = ['短剧总数量', '剧集总数量'];
+
+    private $episodeCount     = 0;
+    private $episodeListCount = 0;
+
+    /**
+     * 初始化卡片内容.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $color  = Admin::color();
+        $colors = [$color->primary(), $color->alpha('blue2', 0.5)];
+
+        $this->title('短剧/剧集');
+        $this->chartLabels($this->labels);
+        // 设置图表颜色
+        $this->chartColors($colors);
+        $this->episodeCount     = $this->getEpisodeCount();
+        $this->episodeListCount = $this->getEpisodeListCount();
+    }
+
+    private function getEpisodeCount()
+    {
+        return Episode::count();
+    }
+
+    private function getEpisodeListCount()
+    {
+        return EpisodesList::count();
+    }
+
+    /**
+     * 渲染模板
+     *
+     * @return string
+     */
+    public function render()
+    {
+        $this->fill();
+
+        return parent::render();
+    }
+
+    /**
+     * 写入数据.
+     *
+     * @return void
+     */
+    public function fill()
+    {
+        $this->withContent($this->episodeCount, $this->episodeListCount);
+
+        // 图表数据
+        $this->withChart([$this->episodeCount, $this->episodeListCount]);
+    }
+
+    /**
+     * 设置图表数据.
+     *
+     * @return $this
+     */
+    public function withChart(array $data)
+    {
+        return $this->chart([
+            'series' => $data,
+        ]);
+    }
+
+    /**
+     * 设置卡片头部内容.
+     *
+     * @return $this
+     */
+    protected function withContent($episode, $episodeList)
+    {
+        $blue = Admin::color()->alpha('blue2', 0.5);
+
+        $style      = 'margin-bottom: 8px';
+        $labelWidth = 120;
+
+        return $this->content(
+            <<<HTML
+<div class="d-flex pl-1 pr-1 pt-1" style="{$style}">
+    <div style="width: {$labelWidth}px">
+        <i class="fa fa-circle text-primary"></i> {$this->labels[0]}
+    </div>
+    <div>{$episode}</div>
+</div>
+<div class="d-flex pl-1 pr-1" style="{$style}">
+    <div style="width: {$labelWidth}px">
+        <i class="fa fa-circle" style="color: $blue"></i> {$this->labels[1]}
+    </div>
+    <div>{$episodeList}</div>
+</div>
+HTML
+        );
+    }
+}

+ 95 - 0
app/Admin/Metrics/Examples/NewDevices.php

xqd
@@ -0,0 +1,95 @@
+<?php
+
+namespace App\Admin\Metrics\Examples;
+
+use Dcat\Admin\Admin;
+use Dcat\Admin\Widgets\Metrics\Donut;
+
+class NewDevices extends Donut
+{
+    protected $labels = ['Desktop', 'Mobile'];
+
+    /**
+     * 初始化卡片内容.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $color  = Admin::color();
+        $colors = [$color->primary(), $color->alpha('blue2', 0.5)];
+
+        $this->title('New Devices');
+        $this->subTitle('Last 30 days');
+        $this->chartLabels($this->labels);
+        // 设置图表颜色
+        $this->chartColors($colors);
+    }
+
+    /**
+     * 渲染模板
+     *
+     * @return string
+     */
+    public function render()
+    {
+        $this->fill();
+
+        return parent::render();
+    }
+
+    /**
+     * 写入数据.
+     *
+     * @return void
+     */
+    public function fill()
+    {
+        $this->withContent(44.9, 28.6);
+
+        // 图表数据
+        $this->withChart([44.9, 28.6]);
+    }
+
+    /**
+     * 设置图表数据.
+     *
+     * @return $this
+     */
+    public function withChart(array $data)
+    {
+        return $this->chart([
+            'series' => $data,
+        ]);
+    }
+
+    /**
+     * 设置卡片头部内容.
+     *
+     * @return $this
+     */
+    protected function withContent($desktop, $mobile)
+    {
+        $blue = Admin::color()->alpha('blue2', 0.5);
+
+        $style      = 'margin-bottom: 8px';
+        $labelWidth = 120;
+
+        return $this->content(
+            <<<HTML
+<div class="d-flex pl-1 pr-1 pt-1" style="{$style}">
+    <div style="width: {$labelWidth}px">
+        <i class="fa fa-circle text-primary"></i> {$this->labels[0]}
+    </div>
+    <div>{$desktop}</div>
+</div>
+<div class="d-flex pl-1 pr-1" style="{$style}">
+    <div style="width: {$labelWidth}px">
+        <i class="fa fa-circle" style="color: $blue"></i> {$this->labels[1]}
+    </div>
+    <div>{$mobile}</div>
+</div>
+HTML
+        );
+    }
+}

+ 104 - 0
app/Admin/Metrics/Examples/NewUsers.php

xqd
@@ -0,0 +1,104 @@
+<?php
+
+namespace App\Admin\Metrics\Examples;
+
+use Dcat\Admin\Widgets\Metrics\Line;
+use Illuminate\Http\Request;
+
+class NewUsers extends Line
+{
+    /**
+     * 初始化卡片内容.
+     *
+     * @return void
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $this->title('New Users');
+        $this->dropdown([
+            '7'   => 'Last 7 Days',
+            '28'  => 'Last 28 Days',
+            '30'  => 'Last Month',
+            '365' => 'Last Year',
+        ]);
+    }
+
+    /**
+     * 处理请求
+     *
+     * @return mixed|void
+     */
+    public function handle(Request $request)
+    {
+        $generator = function ($len, $min = 10, $max = 300) {
+            for ($i = 0; $i <= $len; $i++) {
+                yield mt_rand($min, $max);
+            }
+        };
+
+        switch ($request->get('option')) {
+            case '365':
+                // 卡片内容
+                $this->withContent(mt_rand(1000, 5000) . 'k');
+                // 图表数据
+                $this->withChart(collect($generator(30))->toArray());
+                break;
+            case '30':
+                // 卡片内容
+                $this->withContent(mt_rand(400, 1000) . 'k');
+                // 图表数据
+                $this->withChart(collect($generator(30))->toArray());
+                break;
+            case '28':
+                // 卡片内容
+                $this->withContent(mt_rand(400, 1000) . 'k');
+                // 图表数据
+                $this->withChart(collect($generator(28))->toArray());
+                break;
+            case '7':
+            default:
+                // 卡片内容
+                $this->withContent('89.2k');
+                // 图表数据
+                $this->withChart([28, 40, 36, 52, 38, 60, 55]);
+        }
+    }
+
+    /**
+     * 设置图表数据.
+     *
+     * @return $this
+     */
+    public function withChart(array $data)
+    {
+        return $this->chart([
+            'series' => [
+                [
+                    'name' => $this->title,
+                    'data' => $data,
+                ],
+            ],
+        ]);
+    }
+
+    /**
+     * 设置卡片内容.
+     *
+     * @param string $content
+     *
+     * @return $this
+     */
+    public function withContent($content)
+    {
+        return $this->content(
+            <<<HTML
+<div class="d-flex justify-content-between align-items-center mt-1" style="margin-bottom: 2px">
+    <h2 class="ml-1 font-lg-1">{$content}</h2>
+    <span class="mb-0 mr-1 text-80">{$this->title}</span>
+</div>
+HTML
+        );
+    }
+}

+ 110 - 0
app/Admin/Metrics/Examples/ProductOrders.php

xqd
@@ -0,0 +1,110 @@
+<?php
+
+namespace App\Admin\Metrics\Examples;
+
+use Dcat\Admin\Widgets\Metrics\Round;
+use Illuminate\Http\Request;
+
+class ProductOrders extends Round
+{
+    /**
+     * 初始化卡片内容.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $this->title('Product Orders');
+        $this->chartLabels(['Finished', 'Pending', 'Rejected']);
+        $this->dropdown([
+            '7'   => 'Last 7 Days',
+            '28'  => 'Last 28 Days',
+            '30'  => 'Last Month',
+            '365' => 'Last Year',
+        ]);
+    }
+
+    /**
+     * 处理请求
+     *
+     * @return mixed|void
+     */
+    public function handle(Request $request)
+    {
+        switch ($request->get('option')) {
+            case '365':
+            case '30':
+            case '28':
+            case '7':
+            default:
+                // 卡片内容
+                $this->withContent(23043, 14658, 4758);
+
+                // 图表数据
+                $this->withChart([70, 52, 26]);
+
+                // 总数
+                $this->chartTotal('Total', 344);
+        }
+    }
+
+    /**
+     * 设置图表数据.
+     *
+     * @return $this
+     */
+    public function withChart(array $data)
+    {
+        return $this->chart([
+            'series' => $data,
+        ]);
+    }
+
+    /**
+     * 卡片内容.
+     *
+     * @param int $finished
+     * @param int $pending
+     * @param int $rejected
+     *
+     * @return $this
+     */
+    public function withContent($finished, $pending, $rejected)
+    {
+        return $this->content(
+            <<<HTML
+<div class="col-12 d-flex flex-column flex-wrap text-center" style="max-width: 220px">
+    <div class="chart-info d-flex justify-content-between mb-1 mt-2" >
+          <div class="series-info d-flex align-items-center">
+              <i class="fa fa-circle-o text-bold-700 text-primary"></i>
+              <span class="text-bold-600 ml-50">Finished</span>
+          </div>
+          <div class="product-result">
+              <span>{$finished}</span>
+          </div>
+    </div>
+
+    <div class="chart-info d-flex justify-content-between mb-1">
+          <div class="series-info d-flex align-items-center">
+              <i class="fa fa-circle-o text-bold-700 text-warning"></i>
+              <span class="text-bold-600 ml-50">Pending</span>
+          </div>
+          <div class="product-result">
+              <span>{$pending}</span>
+          </div>
+    </div>
+
+     <div class="chart-info d-flex justify-content-between mb-1">
+          <div class="series-info d-flex align-items-center">
+              <i class="fa fa-circle-o text-bold-700 text-danger"></i>
+              <span class="text-bold-600 ml-50">Rejected</span>
+          </div>
+          <div class="product-result">
+              <span>{$rejected}</span>
+          </div>
+    </div>
+</div>
+HTML
+        );
+    }
+}

+ 113 - 0
app/Admin/Metrics/Examples/Sessions.php

xqd
@@ -0,0 +1,113 @@
+<?php
+
+namespace App\Admin\Metrics\Examples;
+
+use Dcat\Admin\Admin;
+use Dcat\Admin\Widgets\Metrics\Bar;
+use Illuminate\Http\Request;
+
+class Sessions extends Bar
+{
+    /**
+     * 初始化卡片内容.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $color = Admin::color();
+
+        $dark35 = $color->dark35();
+
+        // 卡片内容宽度
+        $this->contentWidth(5, 7);
+        // 标题
+        $this->title('Avg Sessions');
+        // 设置下拉选项
+        $this->dropdown([
+            '7'   => 'Last 7 Days',
+            '28'  => 'Last 28 Days',
+            '30'  => 'Last Month',
+            '365' => 'Last Year',
+        ]);
+        // 设置图表颜色
+        $this->chartColors([
+            $dark35,
+            $dark35,
+            $color->primary(),
+            $dark35,
+            $dark35,
+            $dark35,
+        ]);
+    }
+
+    /**
+     * 处理请求
+     *
+     * @return mixed|void
+     */
+    public function handle(Request $request)
+    {
+        switch ($request->get('option')) {
+            case '7':
+            default:
+                // 卡片内容
+                $this->withContent('2.7k', '+5.2%');
+
+                // 图表数据
+                $this->withChart([
+                    [
+                        'name' => 'Sessions',
+                        'data' => [75, 125, 225, 175, 125, 75, 25],
+                    ],
+                ]);
+        }
+    }
+
+    /**
+     * 设置图表数据.
+     *
+     * @return $this
+     */
+    public function withChart(array $data)
+    {
+        return $this->chart([
+            'series' => $data,
+        ]);
+    }
+
+    /**
+     * 设置卡片内容.
+     *
+     * @param string $title
+     * @param string $value
+     * @param string $style
+     *
+     * @return $this
+     */
+    public function withContent($title, $value, $style = 'success')
+    {
+        // 根据选项显示
+        $label = strtolower(
+            $this->dropdown[request()->option] ?? 'last 7 days'
+        );
+
+        $minHeight = '183px';
+
+        return $this->content(
+            <<<HTML
+<div class="d-flex p-1 flex-column justify-content-between" style="padding-top: 0;width: 100%;height: 100%;min-height: {$minHeight}">
+    <div class="text-left">
+        <h1 class="font-lg-2 mt-2 mb-0">{$title}</h1>
+        <h5 class="font-medium-2" style="margin-top: 10px;">
+            <span class="text-{$style}">{$value} </span>
+            <span>vs {$label}</span>
+        </h5>
+    </div>
+
+    <a href="#" class="btn btn-primary shadow waves-effect waves-light">View Details <i class="feather icon-chevrons-right"></i></a>
+</div>
+HTML
+        );
+    }
+}

+ 112 - 0
app/Admin/Metrics/Examples/Tickets.php

xqd
@@ -0,0 +1,112 @@
+<?php
+
+namespace App\Admin\Metrics\Examples;
+
+use Dcat\Admin\Widgets\Metrics\RadialBar;
+use Illuminate\Http\Request;
+
+class Tickets extends RadialBar
+{
+    /**
+     * 初始化卡片内容.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $this->title('Tickets');
+        $this->height(400);
+        $this->chartHeight(300);
+        $this->chartLabels('Completed Tickets');
+        $this->dropdown([
+            '7'   => 'Last 7 Days',
+            '28'  => 'Last 28 Days',
+            '30'  => 'Last Month',
+            '365' => 'Last Year',
+        ]);
+    }
+
+    /**
+     * 处理请求
+     *
+     * @return mixed|void
+     */
+    public function handle(Request $request)
+    {
+        switch ($request->get('option')) {
+            case '365':
+            case '30':
+            case '28':
+            case '7':
+            default:
+                // 卡片内容
+                $this->withContent(162);
+                // 卡片底部
+                $this->withFooter(29, 63, '1d');
+                // 图表数据
+                $this->withChart(83);
+        }
+    }
+
+    /**
+     * 设置图表数据.
+     *
+     * @return $this
+     */
+    public function withChart(int $data)
+    {
+        return $this->chart([
+            'series' => [$data],
+        ]);
+    }
+
+    /**
+     * 卡片内容.
+     *
+     * @param string $content
+     *
+     * @return $this
+     */
+    public function withContent($content)
+    {
+        return $this->content(
+            <<<HTML
+<div class="d-flex flex-column flex-wrap text-center">
+    <h1 class="font-lg-2 mt-2 mb-0">{$content}</h1>
+    <small>Tickets</small>
+</div>
+HTML
+        );
+    }
+
+    /**
+     * 卡片底部内容.
+     *
+     * @param string $new
+     * @param string $open
+     * @param string $response
+     *
+     * @return $this
+     */
+    public function withFooter($new, $open, $response)
+    {
+        return $this->footer(
+            <<<HTML
+<div class="d-flex justify-content-between p-1" style="padding-top: 0!important;">
+    <div class="text-center">
+        <p>New Tickets</p>
+        <span class="font-lg-1">{$new}</span>
+    </div>
+    <div class="text-center">
+        <p>Open Tickets</p>
+        <span class="font-lg-1">{$open}</span>
+    </div>
+    <div class="text-center">
+        <p>Response Time</p>
+        <span class="font-lg-1">{$response}</span>
+    </div>
+</div>
+HTML
+        );
+    }
+}

+ 127 - 0
app/Admin/Metrics/Examples/TotalUsers.php

xqd
@@ -0,0 +1,127 @@
+<?php
+
+namespace App\Admin\Metrics\Examples;
+
+use Dcat\Admin\Widgets\Metrics\Card;
+use Illuminate\Contracts\Support\Renderable;
+use Illuminate\Http\Request;
+
+class TotalUsers extends Card
+{
+    /**
+     * 卡片底部内容.
+     *
+     * @var string|Renderable|\Closure
+     */
+    protected $footer;
+
+    /**
+     * 初始化卡片.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $this->title('Total Users');
+        $this->dropdown([
+            '7'   => 'Last 7 Days',
+            '28'  => 'Last 28 Days',
+            '30'  => 'Last Month',
+            '365' => 'Last Year',
+        ]);
+    }
+
+    /**
+     * 处理请求.
+     *
+     * @return void
+     */
+    public function handle(Request $request)
+    {
+        switch ($request->get('option')) {
+            case '365':
+                $this->content(mt_rand(600, 1500));
+                $this->down(mt_rand(1, 30));
+                break;
+            case '30':
+                $this->content(mt_rand(170, 250));
+                $this->up(mt_rand(12, 50));
+                break;
+            case '28':
+                $this->content(mt_rand(155, 200));
+                $this->up(mt_rand(5, 50));
+                break;
+            case '7':
+            default:
+                $this->content(143);
+                $this->up(15);
+        }
+    }
+
+    /**
+     * @param int $percent
+     *
+     * @return $this
+     */
+    public function up($percent)
+    {
+        return $this->footer(
+            "<i class=\"feather icon-trending-up text-success\"></i> {$percent}% Increase"
+        );
+    }
+
+    /**
+     * @param int $percent
+     *
+     * @return $this
+     */
+    public function down($percent)
+    {
+        return $this->footer(
+            "<i class=\"feather icon-trending-down text-danger\"></i> {$percent}% Decrease"
+        );
+    }
+
+    /**
+     * 设置卡片底部内容.
+     *
+     * @param string|Renderable|\Closure $footer
+     *
+     * @return $this
+     */
+    public function footer($footer)
+    {
+        $this->footer = $footer;
+
+        return $this;
+    }
+
+    /**
+     * 渲染卡片内容.
+     *
+     * @return string
+     */
+    public function renderContent()
+    {
+        $content = parent::renderContent();
+
+        return <<<HTML
+<div class="d-flex justify-content-between align-items-center mt-1" style="margin-bottom: 2px">
+    <h2 class="ml-1 font-lg-1">{$content}</h2>
+</div>
+<div class="ml-1 mt-1 font-weight-bold text-80">
+    {$this->renderFooter()}
+</div>
+HTML;
+    }
+
+    /**
+     * 渲染卡片底部内容.
+     *
+     * @return string
+     */
+    public function renderFooter()
+    {
+        return $this->toString($this->footer);
+    }
+}

+ 171 - 0
app/Admin/Metrics/Recharge.php

xqd
@@ -0,0 +1,171 @@
+<?php
+
+namespace App\Admin\Metrics;
+
+use App\Models\UserRechargeRecord;
+use App\Models\UserVipRecord;
+use Carbon\Carbon;
+use Dcat\Admin\Admin;
+use Dcat\Admin\Widgets\Metrics\Round;
+use Illuminate\Database\Query\Builder;
+use Illuminate\Http\Request;
+
+class Recharge extends Round
+{
+    protected $labels = ['VIP充值金额', '金币充值金额'];
+
+    private $vipRecharge   = 0;
+    private $goldRecharge  = 0;
+    private $totalRecharge = 0;
+
+    /**
+     * 初始化卡片内容.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $color  = Admin::color();
+        $colors = [$color->primary(), $color->alpha('blue2', 0.5)];
+        $this->height(300);
+        $this->chartHeight(300);
+        $this->chartRadialBarSize(1);
+
+        $this->title('VIP充值/金币充值金额');
+        $this->chartLabels($this->labels);
+        // 设置图表颜色
+        $this->chartColors($colors);
+
+        $this->dropdown([
+            '1'  => '当日新增充值金额',
+            '7'  => '7天新增充值金额',
+            '30' => '当月新增充值金额',
+            '-1' => '总充值金额',
+        ]);
+    }
+
+    public function handle(Request $request)
+    {
+        switch ($request->get('option')) {
+            case '-1':
+                $this->getVipRecharge();
+                $this->getGoldRecharge();
+                break;
+            case '7':
+                $startAt = Carbon::now()->subDays(7)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $this->getVipRecharge($startAt, $endAt);
+                $this->getGoldRecharge($startAt, $endAt);
+                break;
+            case '30':
+                $startAt = Carbon::now()->subDays(30)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $this->getVipRecharge($startAt, $endAt);
+                $this->getGoldRecharge($startAt, $endAt);
+                break;
+            case '1':
+            default:
+                $startAt = Carbon::now()->toDateString() . ' 00:00:0';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $this->getVipRecharge($startAt, $endAt);
+                $this->getGoldRecharge($startAt, $endAt);
+                break;
+        }
+        $this->totalRecharge = $this->vipRecharge + $this->goldRecharge;
+        $this->withContent();
+
+        // 图表数据
+        $vipPercent  = $this->totalRecharge ? $this->vipRecharge  / $this->totalRecharge : 0;
+        $goldPercent = $this->totalRecharge ? $this->goldRecharge / $this->totalRecharge : 0;
+        $data        = [
+            \round($vipPercent * 100, 2),
+            \round($goldPercent * 100, 2),
+        ];
+        $this->withChart($data);
+        $this->chartTotal('总充值金额', $this->totalRecharge);
+    }
+
+    private function getVipRecharge($statAt = '', $endAt = '')
+    {
+        $this->vipRecharge = UserVipRecord::withSum('pay', 'order_fee')
+            ->whereHas('pay', function ($query) use ($statAt, $endAt) {
+                /* @var Builder $query */
+                $query->where('status', 1)
+                ->when($statAt, function ($query) use ($statAt) {
+                    /* @var Builder $query */
+                    $query->where('created_at', '>', $statAt);
+                })->when($statAt, function ($query) use ($endAt) {
+                    /* @var Builder $query */
+                    $query->where('created_at', '<=', $endAt);
+                });
+            })
+            ->get()
+            ->sum('pay_sum_order_fee');
+    }
+
+    private function getGoldRecharge($statAt = '', $endAt = '')
+    {
+        $this->goldRecharge = UserRechargeRecord::withSum('pay', 'order_fee')
+            ->whereHas('pay', function ($query) use ($statAt, $endAt) {
+                /* @var Builder $query */
+                $query->where('status', 1)
+                    ->when($statAt, function ($query) use ($statAt) {
+                        /* @var Builder $query */
+                        $query->where('created_at', '>', $statAt);
+                    })->when($statAt, function ($query) use ($endAt) {
+                        /* @var Builder $query */
+                        $query->where('created_at', '<=', $endAt);
+                    });
+            })
+            ->get()
+            ->sum('pay_sum_order_fee');
+    }
+
+    /**
+     * 设置图表数据.
+     *
+     * @return $this
+     */
+    public function withChart($data): Recharge
+    {
+        return $this->chart([
+            'series' => $data,
+        ]);
+    }
+
+    /**
+     * 设置卡片头部内容.
+     *
+     * @return $this
+     */
+    protected function withContent(): Recharge
+    {
+        $blue = Admin::color()->alpha('blue2', 0.5);
+
+        $style      = 'margin-bottom: 8px';
+        $labelWidth = 120;
+
+        return $this->content(
+            <<<HTML
+<div class="d-flex pl-1 pr-1 pt-1" style="{$style}">
+    <div style="width: {$labelWidth}px">
+        <i class="fa fa-circle text-success"></i> 总充值金额
+    </div>
+    <div>{$this->totalRecharge}</div>
+</div>
+<div class="d-flex pl-1 pr-1" style="{$style}">
+    <div style="width: {$labelWidth}px">
+        <i class="fa fa-circle text-primary"></i> {$this->labels[0]}
+    </div>
+    <div>{$this->vipRecharge}</div>
+</div>
+<div class="d-flex pl-1 pr-1" style="{$style}">
+    <div style="width: {$labelWidth}px">
+        <i class="fa fa-circle" style="color: $blue"></i> {$this->labels[1]}
+    </div>
+    <div>{$this->goldRecharge}</div>
+</div>
+HTML
+        );
+    }
+}

+ 169 - 0
app/Admin/Metrics/TotalMember.php

xqd
@@ -0,0 +1,169 @@
+<?php
+
+namespace App\Admin\Metrics;
+
+use App\Models\UserInfo;
+use Carbon\Carbon;
+use Dcat\Admin\Widgets\Metrics\Card;
+use Illuminate\Contracts\Support\Renderable;
+use Illuminate\Http\Request;
+
+class TotalMember extends Card
+{
+    /**
+     * 卡片底部内容.
+     *
+     * @var string|Renderable|\Closure
+     */
+    protected $footer;
+
+    /**
+     * 初始化卡片.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $this->title('会员');
+        $this->dropdown([
+            '1'  => '当日新增会员',
+            '7'  => '7天新增会员',
+            '30' => '当月新增会员',
+            '-1' => '总会员',
+        ]);
+    }
+
+    /**
+     * 处理请求.
+     *
+     * @return void
+     */
+    public function handle(Request $request)
+    {
+        switch ($request->get('option')) {
+            case '-1':
+                $this->content($this->getUsers());
+                $this->up(100);
+                break;
+            case '7':
+                $startAt = Carbon::now()->subDays(7)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $today   = $this->getUsers($startAt, $endAt);
+                $this->content($today);
+
+                // 计算上7天
+                $startAt = Carbon::now()->subDays(14)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->subDays(7)->toDateString() . ' 23:59:59';
+                $this->calcPercent($today, $startAt, $endAt);
+                break;
+            case '30':
+                $startAt = Carbon::now()->subDays(30)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $today   = $this->getUsers($startAt, $endAt);
+                $this->content($today);
+
+                // 计算上个月
+                $startAt = Carbon::now()->subDays(60)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->subDays(30)->toDateString() . ' 23:59:59';
+                $this->calcPercent($today, $startAt, $endAt);
+                break;
+            case '1':
+            default:
+                $startAt = Carbon::now()->toDateString() . ' 00:00:00';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $today   = $this->getUsers($startAt, $endAt);
+                $this->content($today);
+                // 计算前天
+                $startAt = Carbon::now()->subDays()->toDateString() . ' 00:00:00';
+                $endAt   = Carbon::now()->subDay()->toDateString() . ' 23:59:59';
+                $this->calcPercent($today, $startAt, $endAt);
+                break;
+        }
+    }
+
+    private function calcPercent($today, $startAt, $endAt)
+    {
+        $yesterday = $this->getUsers($startAt, $endAt);
+        // 百分比
+        $percent = $yesterday ? ($today - $yesterday) / $yesterday : 0;
+        $percent = round($percent, 2) * 100;
+        $percent > 0 ? $this->up($percent) : $this->down($percent < 0 ? -$percent : $percent);
+    }
+
+    private function getUsers($startAt = '', $endAt = ''): int
+    {
+        return UserInfo::where('is_vip', 1)
+            ->when($startAt, function ($query) use ($startAt) {
+                $query->where('created_at', '>', $startAt);
+            })->when($endAt, function ($query) use ($endAt) {
+                $query->where('created_at', '<=', $endAt);
+            })->count();
+    }
+
+    /**
+     * @param int $percent
+     *
+     * @return $this
+     */
+    public function up($percent)
+    {
+        return $this->footer(
+            "<i class=\"feather icon-trending-up text-success\"></i> {$percent}% 增加"
+        );
+    }
+
+    /**
+     * @param int $percent
+     *
+     * @return $this
+     */
+    public function down($percent)
+    {
+        return $this->footer(
+            "<i class=\"feather icon-trending-down text-danger\"></i> {$percent}% 减少"
+        );
+    }
+
+    /**
+     * 设置卡片底部内容.
+     *
+     * @param string|Renderable|\Closure $footer
+     *
+     * @return $this
+     */
+    public function footer($footer)
+    {
+        $this->footer = $footer;
+
+        return $this;
+    }
+
+    /**
+     * 渲染卡片内容.
+     *
+     * @return string
+     */
+    public function renderContent()
+    {
+        $content = parent::renderContent();
+
+        return <<<HTML
+<div class="d-flex justify-content-between align-items-center mt-1" style="margin-bottom: 2px">
+    <h2 class="ml-1 font-lg-1">{$content}</h2>
+</div>
+<div class="ml-1 mt-1 font-weight-bold text-80">
+    {$this->renderFooter()}
+</div>
+HTML;
+    }
+
+    /**
+     * 渲染卡片底部内容.
+     *
+     * @return string
+     */
+    public function renderFooter()
+    {
+        return $this->toString($this->footer);
+    }
+}

+ 168 - 0
app/Admin/Metrics/TotalUsers.php

xqd
@@ -0,0 +1,168 @@
+<?php
+
+namespace App\Admin\Metrics;
+
+use App\Models\User;
+use Carbon\Carbon;
+use Dcat\Admin\Widgets\Metrics\Card;
+use Illuminate\Contracts\Support\Renderable;
+use Illuminate\Http\Request;
+
+class TotalUsers extends Card
+{
+    /**
+     * 卡片底部内容.
+     *
+     * @var string|Renderable|\Closure
+     */
+    protected $footer;
+
+    /**
+     * 初始化卡片.
+     */
+    protected function init()
+    {
+        parent::init();
+
+        $this->title('用户');
+        $this->dropdown([
+            '1'  => '当日新增用户',
+            '7'  => '7天新增用户',
+            '30' => '当月新增用户',
+            '-1' => '总用户',
+        ]);
+    }
+
+    /**
+     * 处理请求.
+     *
+     * @return void
+     */
+    public function handle(Request $request)
+    {
+        switch ($request->get('option')) {
+            case '-1':
+                $this->content($this->getUsers());
+                $this->up(100);
+                break;
+            case '7':
+                $startAt = Carbon::now()->subDays(7)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $today   = $this->getUsers($startAt, $endAt);
+                $this->content($today);
+
+                // 计算上7天
+                $startAt = Carbon::now()->subDays(14)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->subDays(7)->toDateString() . ' 23:59:59';
+                $this->calcPercent($today, $startAt, $endAt);
+                break;
+            case '30':
+                $startAt = Carbon::now()->subDays(30)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $today   = $this->getUsers($startAt, $endAt);
+                $this->content($today);
+
+                // 计算上个月
+                $startAt = Carbon::now()->subDays(60)->toDateString() . ' 23:59:59';
+                $endAt   = Carbon::now()->subDays(30)->toDateString() . ' 23:59:59';
+                $this->calcPercent($today, $startAt, $endAt);
+                break;
+            case '1':
+            default:
+                $startAt = Carbon::now()->toDateString() . ' 00:00:00';
+                $endAt   = Carbon::now()->toDateString() . ' 23:59:59';
+                $today   = $this->getUsers($startAt, $endAt);
+                $this->content($today);
+                // 计算昨天
+                $startAt = Carbon::now()->subDays()->toDateString() . ' 00:00:00';
+                $endAt   = Carbon::now()->subDay()->toDateString() . ' 23:59:59';
+                $this->calcPercent($today, $startAt, $endAt);
+                break;
+        }
+    }
+
+    private function calcPercent($today, $startAt, $endAt)
+    {
+        $yesterday = $this->getUsers($startAt, $endAt);
+        // 百分比
+        $percent = $yesterday ? ($today - $yesterday) / $yesterday : 0;
+        $percent = round($percent, 2) * 100;
+        $percent > 0 ? $this->up($percent) : $this->down($percent < 0 ? -$percent : $percent);
+    }
+
+    private function getUsers($startAt = '', $endAt = ''): int
+    {
+        return User::when($startAt, function ($query) use ($startAt) {
+            $query->where('created_at', '>', $startAt);
+        })->when($endAt, function ($query) use ($endAt) {
+            $query->where('created_at', '<=', $endAt);
+        })->count();
+    }
+
+    /**
+     * @param int $percent
+     *
+     * @return $this
+     */
+    public function up($percent)
+    {
+        return $this->footer(
+            "<i class=\"feather icon-trending-up text-success\"></i> {$percent}% 增加"
+        );
+    }
+
+    /**
+     * @param int $percent
+     *
+     * @return $this
+     */
+    public function down($percent)
+    {
+        return $this->footer(
+            "<i class=\"feather icon-trending-down text-danger\"></i> {$percent}% 减少"
+        );
+    }
+
+    /**
+     * 设置卡片底部内容.
+     *
+     * @param string|Renderable|\Closure $footer
+     *
+     * @return $this
+     */
+    public function footer($footer)
+    {
+        $this->footer = $footer;
+
+        return $this;
+    }
+
+    /**
+     * 渲染卡片内容.
+     *
+     * @return string
+     */
+    public function renderContent()
+    {
+        $content = parent::renderContent();
+
+        return <<<HTML
+<div class="d-flex justify-content-between align-items-center mt-1" style="margin-bottom: 2px">
+    <h2 class="ml-1 font-lg-1">{$content}</h2>
+</div>
+<div class="ml-1 mt-1 font-weight-bold text-80">
+    {$this->renderFooter()}
+</div>
+HTML;
+    }
+
+    /**
+     * 渲染卡片底部内容.
+     *
+     * @return string
+     */
+    public function renderFooter()
+    {
+        return $this->toString($this->footer);
+    }
+}

+ 23 - 0
app/Admin/bootstrap.php

xqd
@@ -0,0 +1,23 @@
+<?php
+
+use Dcat\Admin\Admin;
+use Dcat\Admin\Form;
+
+/*
+ * Dcat-admin - admin builder based on Laravel.
+ * @author jqh <https://github.com/jqhph>
+ *
+ * Bootstraper for Admin.
+ *
+ * Here you can remove builtin form field:
+ *
+ * extend custom field:
+ * Dcat\Admin\Form::extend('php', PHPEditor::class);
+ * Dcat\Admin\Grid\Column::extend('php', PHPEditor::class);
+ * Dcat\Admin\Grid\Filter::extend('php', PHPEditor::class);
+ *
+ * Or require js and css assets:
+ * Admin::css('/packages/prettydocs/css/styles.css');
+ * Admin::js('/packages/prettydocs/js/main.js');
+ *
+ */

+ 134 - 0
app/Admin/routes.php

xqd
@@ -0,0 +1,134 @@
+<?php
+
+use Illuminate\Routing\Router;
+use Illuminate\Support\Facades\Route;
+use Dcat\Admin\Admin;
+
+Admin::routes();
+
+Route::group([
+    'prefix'     => config('admin.route.prefix'),
+    'namespace'  => config('admin.route.namespace'),
+    'middleware' => config('admin.route.middleware'),
+], function (Router $router) {
+    $router->get('/', 'HomeController@index');
+
+    //  程序管理
+    $router->group([
+        'namespace' => 'Program',
+        'prefix'    => 'program',
+    ], function ($router) {
+        // 程序管理 --banner
+        $router->resource('/banner', 'BannerController');
+        // 程序管理 --导航
+        $router->resource('/navbar', 'NavBarController');
+        // 程序管理 --标签
+        $router->resource('/tabbar', 'TabbarController');
+        // 程序管理 --首页栏目
+        $router->resource('/home_column', 'HomeColumnController');
+    });
+
+    //  短剧管理
+    $router->group([
+        'namespace' => 'Episode',
+        'prefix'    => 'episodes',
+    ], function ($router) {
+        // 短剧管理 --短剧列表
+        /* @var Route $router */
+        $router->resource('/index', 'EpisodeController');
+        // 短剧管理 --短剧分类
+        $router->resource('/category', 'EpisodesCategoryController');
+        // 短剧批量上传
+        $router->resource('/batch/{id}/upload', 'EpisodesBatchUploadController');
+        // $router->put('batch', 'SettingController@setting');
+    });
+    // 短剧管理 --短剧列表
+    $router->resource('episodes/{episodes}/lists', 'Episode\\EpisodesListController')->shallow();
+
+    //  订单
+    $router->group([
+        'namespace' => 'Order',
+        'prefix'    => 'order',
+    ], function ($router) {
+        // 订单管理 --金币消费记录
+        // $router->resource('/consume','UserConsumeRecordController');
+        // 订单管理 --充值记录
+        $router->resource('/recharge', 'UserRechargeRecordController');
+        // 订单管理 --用户短剧消费记录
+        $router->resource('/episodes', 'UserEpisodesRecordController');
+        // 消费记录
+        $router->resource('/consume', 'UserConsumeRecordController');
+    });
+
+    // 用户列表
+    $router->resource('/users', 'UserController');
+    $router->resource('/PaymentConfig', 'PaymentConfigController');
+    $router->resource('/order', 'OrderController');
+    $router->resource('/withdraw', 'WithdrawController');
+    // VIP购买记录
+    $router->resource('/vip/records', 'UserVipRecordController');
+    $router->resource('/Giveaway', 'GiveawayController');
+    $router->resource('/Recommendations', 'RecommendationsController');
+    $router->resource('/taskList', 'TaskListController');
+    $router->resource('/banner', 'BannerController');
+    $router->resource('/protocol', 'ProtocolController');
+    $router->resource('/service', 'ServiceController');
+    $router->resource('/promotion_background', 'PromotionController');
+    $router->resource('/share', 'ShareController');
+    $router->resource('/aiLvCount', 'AiCountController');
+    $router->resource('/Other', 'OtherController');
+    $router->resource('/apply', 'ApplyController');
+    $router->resource('/ShareA', 'ShareAController');
+    $router->resource('/shareList', 'ShareUserController');
+    $router->resource('/Winnow', 'WinnowController');
+    $router->resource('/keyword', 'KeywordController');
+        $router->resource('/UsersRole', 'UsersRoleController');
+            $router->resource('/ShareData', 'ShareDataController');
+    //  营销
+    $router->group([
+        'namespace' => 'Market',
+        'prefix'    => 'market',
+    ], function ($router) {
+        // --会员 充值套餐
+        $router->resource('/member/combo', 'VipComboController');
+        // --金币 充值设置
+        $router->resource('/recharge/setting', 'RechargeComboController');
+        // --金币 充值记录
+        $router->resource('/recharge/records', 'UserRechargeRecordController');
+    });
+    // 营销 --会员 权限设置
+    $router->resource('/market/member/role', 'Setting\\SettingController');
+    // 营销 --金币 基础设置
+    $router->resource('/market/setting/gold', 'Setting\\SettingController');
+    // 营销 --签到
+    $router->resource('/market/setting/sign', 'Setting\\SettingController');
+
+    //  设置
+    $router->group([
+        'namespace' => 'Setting',
+        'prefix'    => 'setting',
+    ], function ($router) {
+        // --基础设置
+        $router->resource('/base/basic', 'SettingController');
+        // --消息设置
+        $router->resource('/base/tips', 'SettingController');
+        // --订单设置
+        $router->resource('/pay', 'PayConfigController');
+        // --支付设置
+        $router->resource('/pay', 'PayConfigController');
+    });
+
+    //  分销
+    $router->group([
+        'namespace' => 'Share',
+        'prefix'    => 'share',
+    ], function ($router) {
+        // --设置
+        $router->resource('/setting', 'ShareConfigController');
+        $router->post('/setting/save', 'ShareConfigController@save');
+        // --分销商
+        $router->resource('/users', 'UserController');
+        // --体现管理
+        $router->resource('/withdraw', 'UserWithdrawController');
+    });
+});

+ 12 - 0
app/Auth.php

xqd
@@ -0,0 +1,12 @@
+<?php
+
+namespace App;
+
+class Auth
+{
+    public static $user;
+    public static $userId;
+
+    public static $admin;
+    public static $adminId;
+}

+ 19 - 0
app/Casts/DefaultAvatar.php

xqd
@@ -0,0 +1,19 @@
+<?php
+
+namespace App\Casts;
+
+use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
+
+class DefaultAvatar implements CastsAttributes
+{
+    public function get($model, string $key, $value, array $attributes)
+    {
+        return $value
+            ?: 'https://fourtiao.oss-cn-beijing.aliyuncs.com/zhangsiye/images/6b40343b27263be34cf3212bf44f74c3.png';
+    }
+
+    public function set($model, string $key, $value, array $attributes)
+    {
+        return $value;
+    }
+}

+ 36 - 0
app/Casts/HttpToHttps.php

xqd
@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Casts;
+
+use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
+
+class HttpToHttps implements CastsAttributes
+{
+    public function get($model, string $key, $value, array $attributes)
+    {
+        return static::replace($value);
+    }
+
+    public function set($model, string $key, $value, array $attributes)
+    {
+        return static::replace($value);
+    }
+
+    private static function replace($value): string
+    {
+        if (!$value) {
+            return $value;
+        }
+
+        $value = str_replace('http://', 'https://', $value);
+        $value = str_replace('-internal', '', $value);
+
+        $arr = explode('/', $value);
+        array_shift($arr);
+        array_shift($arr);
+        array_shift($arr);
+        $baseUrl = env('ALI_OSS_URL', 'https://zhangsiyecdn.9026.com/');
+
+        return $baseUrl . implode('/', $arr);
+    }
+}

+ 41 - 0
app/Console/Commands/AnJuKePicker.php

xqd
@@ -0,0 +1,41 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+
+class AnJuKePicker extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'picker:anjuke';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     */
+    public function handle()
+    {
+        $picker = new \App\Http\Controllers\Service\AnJuKePicker();
+        $picker->startPick();
+    }
+}

+ 195 - 0
app/Console/Commands/DataSeeder.php

xqd
@@ -0,0 +1,195 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Model\AccountInfo;
+use App\Model\BaseArea;
+use App\Model\WithdrawInfo;
+use App\Model\DeliverInfo;
+use App\Model\DeviceInfo;
+use App\Model\RechargeInfo;
+use App\Model\TransportInfo;
+use App\Model\UserInfo;
+use Illuminate\Console\Command;
+
+/**
+ * 数据填充器
+ * Class DataSeeder.
+ */
+class DataSeeder extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'data:seeder';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     */
+    public function handle()
+    {
+        // 用户表数据填充300条
+
+        $faker_zh = \Faker\Factory::create('zh_CN');
+        $faker_en = \Faker\Factory::create();
+
+        $cities = BaseArea::where('pid', '510117')->with(['communities' => function ($query) {
+            $query->select(['area_id', 'id']);
+        }])->get(['id'])->toArray();
+        $area_ids        = [];
+        $communities_ids = [];
+        $city_max        = 0;
+        $com_max         = 0;
+        foreach ($cities as $city) {
+            $area_ids[] = $city['id'];
+            $city_max++;
+            foreach ($city['communities'] as $community) {
+                $communities_ids[] = $community['id'];
+                $com_max++;
+            }
+        }
+
+        $us              = UserInfo::all(['id', 'role'])->toArray();
+        $user_common     = [];
+        $user_agent      = [];
+        $user_agent_user = [];
+        foreach ($us as $u) {
+            if (UserInfo::Role_Common == $u['role']) {
+                $user_common[] = $u['id'];
+            }
+            if (UserInfo::Role_Agent == $u['role']) {
+                $user_agent[] = $u['id'];
+            }
+            if (UserInfo::Role_Agent_User == $u['role']) {
+                $user_agent_user[] = $u['id'];
+            }
+        }
+
+        $us = DeviceInfo::all(['id'])->toArray();
+
+        $device_ids = [];
+        $device_max = 0;
+        foreach ($us as $u) {
+            $device_max++;
+            $device_ids[] = $u['id'];
+        }
+
+        for ($i = 0; $i < 2000; $i++) {
+            echo "第{$i}个投放记录开始创建\r\n";
+            $tempRow                = [];
+            $tempRow['device_id']   = $device_ids[rand(0, $device_max - 1)];
+            $tempRow['user_id']     = $user_common[rand(0, count($user_common) - 1)];
+            $tempRow['no']          = strtoupper(uniqid());
+            $tempRow['weight']      = rand(1, 99999);
+            $tempRow['money']       = rand(0, 1000);
+            $tempRow['finished_at'] = date('Y-m-d H:i:s');
+            $tempRow['status']      = rand(1, 3);
+            $tempRow['created_at']  = date('Y-m-d H:i:s', strtotime('-' . rand(1, 365) . ' days'));
+            DeliverInfo::create($tempRow);
+        }
+
+        for ($i = 0; $i < 2000; $i++) {
+            echo "第{$i}个收运记录开始创建\r\n";
+            $tempRow               = [];
+            $tempRow['device_id']  = $device_ids[rand(0, $device_max - 1)];
+            $tempRow['user_id']    = $user_agent_user[rand(0, count($user_agent_user) - 1)];
+            $tempRow['no']         = strtoupper(uniqid());
+            $tempRow['weight']     = rand(1, 999999);
+            $tempRow['money']      = rand(0, 1000);
+            $tempRow['created_at'] = date('Y-m-d H:i:s', strtotime('-' . rand(1, 365) . ' days'));
+            $tempRow['status']     = rand(1, 2);
+            TransportInfo::create($tempRow);
+        }
+
+        $banks = [
+            '渤海银行',
+            '广发银行',
+            '国家开发银行',
+            '恒丰银行',
+            '华夏银行',
+            '交通银行',
+            '平安银行',
+            '上海浦东发展银行',
+            '兴业银行',
+            '招商银行',
+            '浙商银行',
+            '中国工商银行',
+            '中国光大银行',
+            '中国建设银行',
+            '中国民生银行',
+            '中国农业银行',
+            '中国银行',
+            '中国邮政储蓄银行',
+            '中信银行',
+        ];
+
+        for ($i = 0; $i < 2000; $i++) {
+            echo "第{$i}个提现记录开始创建\r\n";
+            $tempRow                = [];
+            $tempRow['user_id']     = $user_common[rand(0, count($user_common) - 1)];
+            $tempRow['order_no']    = strtoupper(uniqid());
+            $tempRow['money']       = rand(0, 3000);
+            $tempRow['poundage']    = rand(0, 200);
+            $tempRow['pay_money']   = $tempRow['money'] - $tempRow['poundage'];
+            $tempRow['paid_at']     = date('Y-m-d H:i:s');
+            $tempRow['bank_card']   = $faker_zh->phoneNumber;
+            $tempRow['bank_name']   = $banks[rand(0, count($banks) - 1)];
+            $tempRow['name']        = $faker_zh->name;
+            $tempRow['mobile']      = $faker_zh->phoneNumber;
+            $tempRow['ali_account'] = $faker_zh->phoneNumber;
+            $tempRow['ali_name']    = $faker_zh->name;
+            $tempRow['status']      = rand(0, 1);
+            $tempRow['type']        = rand(0, 2);
+            $tempRow['created_at']  = date('Y-m-d H:i:s', strtotime('-' . rand(1, 365) . ' days'));
+            $tempRow['reason']      = $faker_en->company;
+            WithdrawInfo::create($tempRow);
+        }
+
+        for ($i = 0; $i < 2000; $i++) {
+            echo "第{$i}个充值记录开始创建\r\n";
+            $tempRow               = [];
+            $tempRow['user_id']    = $user_common[rand(0, count($user_common) - 1)];
+            $tempRow['order_no']   = strtoupper(uniqid());
+            $tempRow['money']      = rand(0, 1000);
+            $tempRow['type']       = rand(0, 1);
+            $tempRow['created_at'] = date('Y-m-d H:i:s', strtotime('-' . rand(1, 365) . ' days'));
+
+            $tempRow['finished_at'] = date('Y-m-d H:i:s', strtotime('-' . rand(1, 365) . ' days'));
+            $tempRow['status']      = rand(0, 1);
+            RechargeInfo::create($tempRow);
+        }
+
+        for ($i = 0; $i < 2000; $i++) {
+            echo "第{$i}个余额记录开始创建\r\n";
+            $tempRow             = [];
+            $tempRow['user_id']  = $user_common[rand(0, count($user_common) - 1)];
+            $tempRow['method']   = rand(1, 6);
+            $s                   = [-1, 1];
+            $tempRow['admin_id'] = $s[rand(0, 1)];
+            $tempRow['balance']  = rand(10, 1000);
+            $tempRow['money']    = rand(10, 1000);
+            $tempRow['detail']   = $faker_en->text;
+
+            AccountInfo::create($tempRow);
+        }
+    }
+}

+ 41 - 0
app/Console/Commands/DongFangDiPicker.php

xqd
@@ -0,0 +1,41 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+
+class DongFangDiPicker extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'picker:dfd';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '懂房帝';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     */
+    public function handle()
+    {
+        $dfd = new \App\Http\Controllers\Service\DongFangDiPicker();
+        $dfd->startPick();
+    }
+}

+ 72 - 0
app/Console/Commands/KuaishowPush.php

xqd
@@ -0,0 +1,72 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Helper\Kuaishou;
+use App\Helper\UniPlatform\Kuaishou\KuaishouAPI;
+use App\Models\Pay;
+use App\Models\PayConfig;
+use Illuminate\Console\Command;
+
+class KuaishowPush extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'kuaishow:push';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '推送快手订单';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        $app    = $this->getKuishouFactory();
+        $orders = Pay::with(['user.info'])->whereHas('user.info', function ($query) {
+            $query->where('platform', 2);
+        })->where('status', 1)
+            ->where('is_settle', 0)
+            ->get();
+
+        /* @var Pay $order */
+        foreach ($orders as $order) {
+            try {
+                echo '推送订单号-->',$order->pay_id;
+                $res = $app->pushOrder($order->user->open_id, $order->pay_id, $order->created_at);
+                echo "\t推送结果-->\033[32m",json_encode($res, JSON_UNESCAPED_UNICODE),"\033[0m",PHP_EOL;
+            } catch (\Exception $exception) {
+                echo "\t推送结果异常-->\033[31m",$exception->getMessage(),"\033[0m",PHP_EOL;
+            }
+        }
+    }
+
+    protected function getKuishouFactory(): Kuaishou
+    {
+        $setting = PayConfig::first();
+
+        return (new Kuaishou(app(KuaishouAPI::class)))->factory([
+            'app_id'     => $setting->kuaishou_app_id,
+            'app_secret' => $setting->kuaishou_app_secret,
+        ]);
+    }
+}

+ 64 - 0
app/Console/Commands/UserRepair.php

xqd
@@ -0,0 +1,64 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Model\AccountInfo;
+use App\Model\DeliverInfo;
+use App\Model\RechargeInfo;
+use App\Model\UserInfo;
+use Illuminate\Console\Command;
+
+class UserRepair extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'user:reset';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '测试用户数据重置';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     */
+    public function handle()
+    {
+        // TODO 10465 宣传员展示不删除
+        $ids = [9997, 10025, 10318, 10340, 10024, 10572, 10517, 10690, 10538, 10533, 10044, 10679, 10347, 10142, 10190, 10329, 11036, 11460, 10004, 10045, 10446, 12000, 12031];
+        foreach ($ids as $id) {
+            $user = UserInfo::where(['id' => $id])->first();
+            if (empty($user)) {
+                continue;
+            }
+            $user_arr = [];
+            // echo '用户id:'.$id.'  角色'.$user->role.PHP_EOL;
+            if (!in_array($user->role, $user_arr)) {
+                $user_arr[] = $user->role;
+            }
+            DeliverInfo::where(['user_id' => $id])->delete();
+            AccountInfo::where(['user_id' => $id])->delete();
+            RechargeInfo::where(['user_id' => $id])->delete();
+            $data['account'] = 0;
+            if (2 == $user->role) {
+                $data['agent_account'] = 0;
+            }
+            UserInfo::where(['id' => $id])->update($data);
+        }
+    }
+}

+ 164 - 0
app/Console/Commands/importMap.php

xqd
@@ -0,0 +1,164 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Model\BaseArea;
+use App\Model\BaseAreaNew;
+use Illuminate\Console\Command;
+use Overtrue\Pinyin\Pinyin;
+
+class importMap extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'import:map';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     */
+    public function handle()
+    {
+        //    $data = json_decode(file_get_contents(public_path('docs/maps.txt')),true);
+
+        $total = [];
+
+        //        foreach ($data['plist']['dict']['key'] as $value){
+        //            $prefix = substr($value,0,6);
+        //            $name = str_replace($prefix,'',$value);
+        //            $total[substr($prefix,0,2)] = ['pid'=>$prefix,'name'=>$name];
+        //        }
+        //
+        //
+        //
+        //
+        //
+        //
+        //     foreach ($data['plist']['dict']['dict'] as $datum){
+        //             foreach ($datum['key'] as $vv){
+        //                     $g2_prefix = substr($vv,0,6);
+        //                     $g2_prefix_half = substr($vv,0,2);
+        //                     $name = str_replace($g2_prefix,'',$vv);
+        //                     $total[substr($g2_prefix,0,4)] = ['pid'=>$g2_prefix,'name'=>$name];
+        //         }
+        //     }
+        //
+        //
+        //        foreach ($data['plist']['dict']['dict'] as $datum){
+        //            foreach ($datum['array'] as $vv){
+        //                foreach ($vv['string'] as $vvv){
+        //
+        //                    $g2_prefix = substr($vvv,0,6);
+        //                    $g2_prefix_half = substr($vvv,0,2);
+        //                    $g2_prefix_3 = substr($vvv,0,4);
+        //                    $name = str_replace($g2_prefix,'',$vvv);
+        //                    $total[substr($g2_prefix,0,5)] = ['pid'=>$g2_prefix,'name'=>$name];
+        //
+        //                }
+        //            }
+        //        }
+        //
+        //
+        //
+        //
+        //
+        //
+        //
+        //        $res = BaseAreaNew::find($g2_prefix);
+        //        if ($res)
+        //            continue;
+        //        $id = BaseAreaNew::insert([
+        //            'name'=>$name,
+        //            'pid'=>$total[$g2_prefix_3]['pid'],
+        //            'id'=>$g2_prefix,
+        //            'short_name'=>$name,
+        //            'grade'=>3,
+        //            'city_code'=>0,
+        //            'zip_code'=>0,
+        //            'merger_name'=>$total[$g2_prefix_half]['name'].','.$total[$g2_prefix_3]['name'].','.$name,
+        //            'lng'=>0,
+        //            'lat'=>0,
+        //            'pinyin'=>'',
+        //        ]);
+        //
+        //
+        //     $dd = scandir(public_path('docs/town'));
+        //
+        //     $arr = array_map(function ($v){
+        //         return str_replace('.json','',$v);
+        //     },$dd);
+        //     unset($dd);
+        //     $ds = BaseAreaNew::whereIn('id',$arr)->get(['id','name','merger_name']);
+        //
+        //
+        //     foreach ($ds as $d){
+        //         $dds = json_decode(file_get_contents(public_path('docs/town/'.$d->id.'.json')),true);
+        //         foreach ($dds as $k =>$v){
+        //
+        //             $res = BaseAreaNew::find($k);
+        //        if ($res)
+        //            continue;
+        //        $id = BaseAreaNew::insert([
+        //            'name'=>$v,
+        //            'pid'=>$d->id,
+        //            'id'=>$k,
+        //            'short_name'=>$v,
+        //            'grade'=>4,
+        //            'city_code'=>0,
+        //            'zip_code'=>0,
+        //            'merger_name'=>$d->merger_name.','.$v,
+        //            'lng'=>0,
+        //            'lat'=>0,
+        //            'pinyin'=>'',
+        //        ]);
+        //
+        //        echo "{$v}:{$k} ³É¹¦µ¼ÈëÁË£¡\r\n";
+        //
+        //         }
+
+        $i    = 0;
+        $maps = BaseArea::where('relation', '')->get(['pid', 'id']);
+
+        foreach ($maps as $map) {
+            $i++;
+            echo "Deal:{$i}\r\n";
+            $relation = ['1'];
+            $pid      = $map->pid;
+            if (1 == $pid) {
+                $map->update(['relation' => implode(',', $relation)]);
+            }
+
+            $relation[] = $map->id;
+            while (1 != $pid) {
+                $area = BaseArea::find($pid);
+                if ($area) {
+                    $relation[] = $pid;
+                    $pid        = $area->pid;
+                }
+            }
+            $str = implode(',', $relation);
+
+            $map->update(['relation' => $str]);
+            echo "{$str}\r\n";
+        }
+    }
+}

+ 43 - 0
app/Console/Kernel.php

xqd
@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Console;
+
+use App\Jobs\ByteDanceSettlementJob;
+use App\Jobs\KuaishouSettlementJob;
+use Illuminate\Console\Scheduling\Schedule;
+use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
+
+class Kernel extends ConsoleKernel
+{
+    /**
+     * The Artisan commands provided by your application.
+     *
+     * @var array
+     */
+    protected $commands = [
+    ];
+
+    /**
+     * Define the application's command schedule.
+     *
+     * @return void
+     */
+    protected function schedule(Schedule $schedule)
+    {
+        // $schedule->command('inspire')->hourly();
+        $schedule->job(ByteDanceSettlementJob::class)->dailyAt('00:00');
+        $schedule->job(KuaishouSettlementJob::class)->dailyAt('00:05');
+    }
+
+    /**
+     * Register the commands for the application.
+     *
+     * @return void
+     */
+    protected function commands()
+    {
+        $this->load(__DIR__ . '/Commands');
+
+        require base_path('routes/console.php');
+    }
+}

+ 7 - 0
app/Exceptions/ApiException.php

xqd
@@ -0,0 +1,7 @@
+<?php
+
+namespace App\Exceptions;
+
+class ApiException extends \Exception
+{
+}

+ 7 - 0
app/Exceptions/Exceptions/ApiException.php

xqd
@@ -0,0 +1,7 @@
+<?php
+
+namespace App\Exceptions;
+
+class ApiException extends \Exception
+{
+}

+ 25 - 0
app/Exceptions/Exceptions/ApiHandler.php

xqd
@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Exceptions;
+
+use Dingo\Api\Exception\Handler as DingoHandler;
+use Illuminate\Validation\ValidationException;
+
+class ApiHandler extends DingoHandler
+{
+    public function handle($exception)
+    {
+        if ($exception instanceof ApiException) {
+            $msg = $exception->getMessage();
+            $out = json_decode($msg, true);
+
+            return response()->json($out);
+        }
+
+        if ($exception instanceof ValidationException) {
+            return response()->json(['code' => 1, 'msg' => array_values($exception->errors())[0][0], 'data' => []]);
+        }
+
+        return response()->json(['code' => 1, 'msg' => $exception->getMessage(), 'data' => []]);
+    }
+}

+ 60 - 0
app/Exceptions/Exceptions/Handler.php

xqd
@@ -0,0 +1,60 @@
+<?php
+
+namespace App\Exceptions;
+
+use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+use Illuminate\Validation\ValidationException;
+use Exception;
+
+class Handler extends ExceptionHandler
+{
+    /**
+     * A list of the exception types that are not reported.
+     *
+     * @var array
+     */
+    protected $dontReport = [
+    ];
+
+    /**
+     * A list of the inputs that are never flashed for validation exceptions.
+     *
+     * @var array
+     */
+    protected $dontFlash = [
+        'current_password',
+        'password',
+        'password_confirmation',
+    ];
+
+    /**
+     * Register the exception handling callbacks for the application.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        $this->reportable(function (\Throwable $e) {
+        });
+    }
+
+    public function render($request, \Throwable $e)
+    {
+        if ($e instanceof ValidationException) {
+            $out = [
+                'code' => 1,
+                'msg'  => array_values($e->errors())[0][0],
+                'data' => null,
+            ];
+
+            return response()->json($out);
+        }
+        $out = [
+            'code' => 1,
+            'msg'  => $e->getMessage(),
+            'data' => null,
+        ];
+
+        return response()->json($out);
+    }
+}

+ 38 - 0
app/Exceptions/Handler.php

xqd
@@ -0,0 +1,38 @@
+<?php
+
+namespace App\Exceptions;
+
+use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+
+class Handler extends ExceptionHandler
+{
+    /**
+     * A list of the exception types that are not reported.
+     *
+     * @var array
+     */
+    protected $dontReport = [
+    ];
+
+    /**
+     * A list of the inputs that are never flashed for validation exceptions.
+     *
+     * @var array
+     */
+    protected $dontFlash = [
+        'current_password',
+        'password',
+        'password_confirmation',
+    ];
+
+    /**
+     * Register the exception handling callbacks for the application.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        $this->reportable(function (\Throwable $e) {
+        });
+    }
+}

+ 165 - 0
app/Helper/AttachmentHelper.php

xqd
@@ -0,0 +1,165 @@
+<?php
+
+namespace App\Helper;
+
+use FFMpeg;
+use Illuminate\Http\Request;
+use Illuminate\Http\UploadedFile;
+use OSS\OssClient;
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use App\Models\BaseAttachment;
+
+trait AttachmentHelper
+{
+    public function __construct()
+    {
+        // 在 .env 文件里配置下面可测试文件上传OSS
+        // ALI_OSS_ACCESS_ID=LTAI5tHMYxyoEkmGqQZjbpmk
+        // ALI_OSS_ACCESS_KEY=Fqj8J1JH0yyRpLvOjNFj5usjvfvHns
+        // ALI_OSS_BUCKET=zhengda
+        // ALI_OSS_ENDPOINT=oss-cn-chengdu.aliyuncs.com
+
+        $this->fileDriver = env('FILESYSTEM_DRIVER');
+        $this->bucket     = env('ALI_OSS_BUCKET');
+        $this->accessId   = env('ALI_OSS_ACCESS_ID');
+        $this->accessKey  = env('ALI_OSS_ACCESS_KEY');
+        $this->endPoint   = env('ALI_OSS_ENDPOINT');
+        $this->ossClient  = new OssClient($this->accessId, $this->accessKey, $this->endPoint);
+    }
+
+    /**
+     * @param string    $tag
+     * @param float|int $size
+     *
+     * @return array|mixed
+     *
+     * @throws \Exception
+     */
+    public function uploadAttachment(Request $request, $key, $tag = 'files', $size = 200 * 1024 * 1024, array $mimeType = [])
+    {
+        if ($request->hasFile($key)) {
+            $files = $request->file($key);
+            if (null === $files) {
+                throw new \Exception(trans('api.ATTACHMENT_FILE_NULL'));
+            }
+            if ($files instanceof UploadedFile) {
+                $files = [$files];
+            }
+            $result = [];
+            foreach ($files as $idx => $file) {
+                if (!$file->isValid()) {
+                    throw new \Exception(trans('api.ATTACHMENT_TYPE_ERROR'));
+                    continue;
+                }
+                $fileSize = $file->getSize();
+                if ($fileSize > $size) {
+                    throw new \Exception(trans('api.ATTACHMENT_SIZE_EXCEEDED'));
+                    continue;
+                }
+                $fileMimeType = $file->getMimeType();
+                if (!empty($mimeType) && !in_array($fileMimeType, $mimeType)) {
+                    throw new \Exception(trans('api.ATTACHMENT_TYPE_ERROR'));
+                    continue;
+                }
+                $clientName   = $file->getClientOriginalName();
+                $md5          = md5($clientName . time());
+                $md5_filename = $md5 . '.' . $file->getClientOriginalExtension();
+
+                try {
+                    // 本地上传
+                    if ('local' == env('FILESYSTEM_DRIVER')) {
+                        $rel_path = '/upload/' . $tag . '/' . date('Ymd');
+                        $path     = public_path() . $rel_path;
+                        if (!file_exists($path)) {
+                            if (!@mkdir($path, 0755, true)) {
+                                throw new \Exception(trans('api.ATTACHMENT_MAKE_FOLDER_ERROR'));
+                            }
+                        }
+                        $file->move($path, $md5_filename);
+                        $realPath = $path . '/' . $md5_filename;
+                        $urlPath  = $rel_path . '/' . $md5_filename;
+                        if ('video/mp4' == $fileMimeType || 'video/quicktime' == $fileMimeType) {
+                            $ffmpeg = FFMpeg\FFMpeg::create([
+                                'ffmpeg.binaries'  => '/usr/bin/ffmpeg',
+                                'ffprobe.binaries' => '/usr/bin/ffprobe',
+                            ]);
+                            $video = $ffmpeg->open($realPath);
+                            $vpath = public_path() . '/upload/vpic/';
+                            if (!file_exists($vpath)) {
+                                if (!@mkdir($vpath, 0755, true)) {
+                                    return trans('api.ATTACHMENT_MAKE_FOLDER_ERROR');
+                                }
+                            }
+                            $pic = $vpath . $md5 . '.jpg';
+                            $video->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(1))->save($pic);
+                        }
+                        $result[$idx] = 'http://' . $_SERVER['HTTP_HOST'] . $urlPath;
+                    } elseif ('oss' == env('FILESYSTEM_DRIVER')) {
+                        $tmppath      = $file->getRealPath(); // 获取上传图片的临时地址
+                        $fileName     = date('YmdHis') . rand(10000, 99999) . '.' . $file->getClientOriginalExtension(); // 生成文件名
+                        $pathName     = $tag . '/' . date('Y-m') . '/' . $fileName;
+                        $upResult     = $this->ossClient->uploadFile($this->bucket, $pathName, $tmppath, ['ContentType' => $file->getClientMimeType()]); // 上传图片到阿里云OSS
+                        $url          = $upResult['info']['url'];
+                        $realPath     = $url;
+                        $urlPath      = $url;
+                        $result[$idx] = $url;
+                    }
+                    // 将上传的图片存入到数据表作为记录
+                    $attachment            = new BaseAttachment();
+                    $attachment->name      = $clientName;
+                    $attachment->md5       = $md5;
+                    $attachment->path      = $realPath;
+                    $attachment->url       = $urlPath;
+                    $attachment->size      = $fileSize;
+                    $attachment->file_type = $fileMimeType;
+                    if (!$attachment->save()) {
+                        @unlink($realPath);
+
+                        throw new \Exception(trans('api.ATTACHMENT_SAVE_ERROR'));
+                    }
+                } catch (FileException $e) {
+                    throw new \Exception(trans('api.ATTACHMENT_UPLOAD_INVALID'));
+                }
+            }
+            if (1 == count($result)) {
+                return array_shift($result);
+            }
+
+            return $result;
+        }
+
+        throw new \Exception(trans('api.ATTACHMENT_UPLOAD_INVALID'));
+    }
+
+    /**
+     * 删除附件.
+     *
+     * @param $picUrl 图片地址
+     *
+     * @return int 错误码or 0(成功)
+     */
+    public function deleteAttachment($picUrl)
+    {
+        if ('local' == $this->fileDriver) {
+            $attachment = BaseAttachment::where(['path' => $picUrl])->first();
+            if (!$attachment) {
+                return false;
+            }
+            if (file_exists($attachment->path)) {
+                if (@unlink($attachment->path)) {
+                    if (!$attachment->delete()) {
+                        return false;
+                    }
+                } else {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        } elseif ('oss' == $this->fileDriver) {
+            $this->ossClient->deleteObject($this->bucket, $picUrl);
+        }
+
+        return true;
+    }
+}

+ 276 - 0
app/Helper/ByteDance.php

xqd
@@ -0,0 +1,276 @@
+<?php
+
+namespace App\Helper;
+
+use App\Helper\UniPlatform\BaseAPI;
+use App\Helper\UniPlatform\BaseUniPlatform;
+use Carbon\Carbon;
+use GuzzleHttp\Client;
+use GuzzleHttp\Exception\GuzzleException;
+
+/**
+ * Class ByteDance.
+ *
+ * @property string $appId
+ * @property string $slat
+ * @property string $secret
+ * @property string $token
+ * @property string $accessTokenFile
+ * @property string $accessToken
+ * @property string $noticeUrl
+ * @property string $validTimestamp
+ */
+class ByteDance extends BaseUniPlatform
+{
+    public function __construct(BaseAPI $api)
+    {
+        $this->API = $api;
+    }
+
+    /**
+     * 获取 access token.
+     *
+     * @throws \Exception
+     */
+    protected function getAccessToken(): string
+    {
+        $res = $this->post($this->API::ACCESS_TOKEN, [
+            'grant_type' => 'client_credential',
+            'appid'      => $this->appId,
+            'secret'     => $this->secret,
+        ]);
+        if (!empty($res['err_no'])) {
+            throw new \Exception('获取access token 错误');
+        }
+
+        file_put_contents($this->accessTokenFile, json_encode([
+            'access_token' => $res['access_token'],
+            'expires_at'   => $res['expiresAt'],
+        ]));
+
+        return $res['access_token'];
+    }
+
+    /**
+     * @return array|mixed
+     *
+     * @throws \Exception
+     */
+    public function createOrder($outOrderNo, $totalAmount, $openId): array
+    {
+        $data = [
+            'app_id'       => $this->appId,
+            'out_order_no' => $outOrderNo,
+            'total_amount' => intval($totalAmount * 100),
+            'subject'      => '订单号:' . $outOrderNo,
+            'body'         => '抖音担保支付',
+            'valid_time'   => $this->validTimestamp,
+            'sign'         => $this->secret,
+            // 'notify_url'   => $notifyUrl, // 可以不设置 使用小程序后台设置的回调
+        ];
+        $data         = array_filter($data);
+        $data['sign'] = $this->getSign($data);
+
+        return $this->post(
+            $this->API::CREATE_ORDER,
+            $data
+        );
+    }
+
+    /**
+     * 结算.
+     *
+     * @return array|mixed|string[]
+     *
+     * @throws \Exception
+     */
+    public function settle($outOrderNo)
+    {
+        $data = [
+            'app_id'        => $this->appId,
+            'out_settle_no' => $outOrderNo,
+            'out_order_no'  => $outOrderNo,
+            'settle_desc'   => '主动结算',
+        ];
+        $data         = array_filter($data);
+        $data['sign'] = $this->getSign($data);
+
+        return $this->post(
+            $this->API::SETTLE,
+            $data
+        );
+    }
+
+    /**
+     * @return string
+     */
+    public function getSign(array $data)
+    {
+        $rList = [];
+        foreach ($data as $k => $v) {
+            if ('other_settle_params' == $k || 'app_id' == $k || 'sign' == $k || 'thirdparty_id' == $k) {
+                continue;
+            }
+            $value = trim(strval($v));
+            $len   = strlen($value);
+            if ($len > 1 && '"' == substr($value, 0, 1) && '"' == substr($value, $len, $len - 1)) {
+                $value = substr($value, 1, $len - 1);
+            }
+            $value = trim($value);
+            if ('' == $value || 'null' == $value) {
+                continue;
+            }
+            array_push($rList, $value);
+        }
+        array_push($rList, $this->slat);
+        sort($rList, SORT_STRING);
+
+        return md5(implode('&', $rList));
+    }
+
+    /**
+     * @return string
+     */
+    public function getNotifySign(array $data)
+    {
+        $filtered = [];
+        foreach ($data as $key => $value) {
+            if (in_array($key, ['msg_signature', 'type'])) {
+                continue;
+            }
+            $value = trim(strval($value));
+            $len   = strlen($value);
+            if ($len > 1 && '"' == substr($value, 0, 1) && '"' == substr($value, $len, $len - 1)) {
+                $value = substr($value, 1, $len - 1);
+            }
+            $filtered[] = is_string($value)
+                    ? trim($value)
+                    : $value;
+        }
+        $filtered[] = trim($this->token);
+        sort($filtered, SORT_STRING);
+
+        return sha1(trim(implode('', $filtered)));
+    }
+
+    public function pushOrder($openid, $orderId, $goods, $status)
+    {
+        $data = [
+            'access_token' => $this->accessToken,
+            'open_id'      => $openid,
+            'order_type'   => 0, // 0:普通小程序订单(非 POI 订单),
+            'order_status' => 1,
+            'app_name'     => 'douyin',
+            'update_time'  => (int) Carbon::now()->getPreciseTimestamp(3),
+            'order_detail' => json_encode([
+                'order_id'    => $orderId,
+                'create_time' => (int) Carbon::now()->getPreciseTimestamp(3),
+                'status'      => $status, // 已支付 待支付
+                'amount'      => 1,
+                'total_price' => $goods['price'] * 100,
+                'detail_url'  => 'pages/my/consume',
+                'item_list'   => [
+                    [
+                        'item_code' => (string) $goods['id'], // 商品ID,
+                        'img'       => 'https://zhengda.oss-cn-chengdu.aliyuncs.com/zhangsiye/images/26474488be1b83e2fcb0b9475508a9bb.png',
+                        'title'     => $goods['title'],
+                        'price'     => $goods['price'] * 100,
+                    ],
+                ],
+            ], JSON_UNESCAPED_UNICODE),
+        ];
+
+        return $this->post($this->API::ORDER_PUSH, $data);
+    }
+
+    /**
+     * @param string $code
+     *
+     * @return array|mixed
+     *
+     * @throws \Exception
+     */
+    public function login($code = ''): array
+    {
+        return $this->post($this->API::LOGIN, [
+            'appid'  => $this->appId,
+            'secret' => $this->secret,
+            'code'   => $code,
+        ]);
+    }
+
+    /**
+     * @return array|mixed
+     *
+     * @throws \Exception
+     */
+    public function generateQrcode()
+    {
+        $userId = \user()->id;
+
+        return $this->post($this->API::CREATE_QRCODE, [
+            'appname'      => 'douyin',
+            'access_token' => $this->accessToken,
+            'path'         => urlencode('pages/index/index?' . "user_id=$userId"),
+            'width'        => 600,
+        ]);
+    }
+
+    /**
+     * 接口请求
+     *
+     * @param string $uri
+     * @param array  $data
+     *
+     * @return array|mixed
+     *
+     * @throws \Exception
+     */
+    protected function post($uri = '', $data = []): array
+    {
+        try {
+            $client = new Client();
+            $res    = $client->post($uri, [
+                'verify'  => false,
+                'headers' => ['Content-Type' => 'application/json'],
+                'body'    => json_encode($data),
+            ]);
+
+            $stringBody = (string) $res->getBody();
+            $res        = json_decode($stringBody, true);
+            // 生成二维码接口是直接放回 buffer的
+            if (empty($res) && $uri == $this->API::CREATE_QRCODE) {
+                return [$stringBody];
+            }
+
+            if (isset($res['err_no']) && !empty($res['err_no'])) {
+                throw new \Exception("请求字节跳动API接口错误,错误码:{$res['err_no']},错误信息:{$res['err_tips']}");
+            }
+
+            if (isset($res['err_code']) && !empty($res['err_code'])) {
+                throw new \Exception("请求字节跳动API接口错误,错误码:{$res['err_msg']},错误信息:{$res['err_msg']}");
+            }
+
+            return $res['data'] ?? $res;
+        } catch (GuzzleException $e) {
+            \Log::error($e->getMessage());
+
+            throw new \Exception($e->getMessage());
+        }
+    }
+
+    protected function setAccessFileDir(): void
+    {
+        $this->accessTokenDir = storage_path('app/bytedance');
+    }
+
+    protected function setAccessFilePath(): void
+    {
+        $this->accessTokenFile = storage_path('app/bytedance/bytedance_access_token.json');
+    }
+
+    protected function setNoticeUrl(): void
+    {
+        $this->noticeUrl = env('APP_URL') . '/api/pay/bytedance/notify';
+    }
+}

+ 243 - 0
app/Helper/Kuaishou.php

xqd
@@ -0,0 +1,243 @@
+<?php
+
+namespace App\Helper;
+
+use App\Helper\UniPlatform\BaseAPI;
+use App\Helper\UniPlatform\BaseUniPlatform;
+use Carbon\Carbon;
+use GuzzleHttp\Client;
+use GuzzleHttp\Exception\GuzzleException;
+
+/**
+ * Class Kuaishou.
+ *
+ * @property string $appId
+ * @property string $slat
+ * @property string $secret
+ * @property string $token
+ * @property string $accessTokenFile
+ * @property string $accessToken
+ * @property string $noticeUrl
+ * @property string $validTimestamp
+ */
+class Kuaishou extends BaseUniPlatform
+{
+    public function __construct(BaseAPI $api)
+    {
+        $this->API = $api;
+    }
+
+    /**
+     * @param string $code
+     *
+     * @return array|mixed
+     *
+     * @throws \Exception
+     */
+    public function login($code = ''): array
+    {
+        return $this->post($this->API::LOGIN, [
+            'app_id'     => $this->appId,
+            'app_secret' => $this->secret,
+            'js_code'    => $code,
+        ]);
+    }
+
+    public function createOrder($outOrderNo, $totalAmount, $openId): array
+    {
+        $data = [
+            'app_id'       => $this->appId,
+            'out_order_no' => $outOrderNo,
+            'open_id'      => $openId,
+            'total_amount' => intval($totalAmount * 100),
+            'subject'      => '订单号:' . $outOrderNo,
+            'detail'       => '快手担保支付',
+            'type'         => 1233, // @url https://mp.kuaishou.com/docs/operate/platformAgreement/epayServiceCharge.html
+            'expire_time'  => $this->validTimestamp,
+            'notify_url'   => $this->noticeUrl,
+        ];
+        $data['sign'] = $this->getSign($data);
+        $url          = $this->API::CREATE_ORDER . '?' . http_build_query([
+            'app_id'       => $this->appId,
+            'access_token' => $this->accessToken,
+        ]);
+        $res = $this->post(
+            $url,
+            $data,
+            'json'
+        );
+
+        return [
+            'order_id'    => $res['order_info']['order_no'],
+            'order_token' => $res['order_info']['order_info_token'],
+        ];
+    }
+
+    public function settle($orderNo, $amount)
+    {
+        $url = $this->API::SETTLE . '?' . http_build_query([
+            'app_id'       => $this->appId,
+            'access_token' => $this->accessToken,
+        ]);
+        $data = [
+            'app_id'        => $this->appId,
+            'out_order_no'  => $orderNo,
+            'out_settle_no' => $orderNo,
+            'reason'        => '主动结算',
+            'notify_url'    => env('APP_URL') . '/api/pay/kuaishou/settle',
+            'settle_amount' => $amount,
+        ];
+        $data['sign'] = $this->getSign($data);
+
+        return $this->post(
+            $url,
+            $data,
+            'json'
+        );
+    }
+
+    public function pushOrder($openid, $orderId, $createAt): array
+    {
+        $data = [
+            'app_id'               => $this->appId,
+            'out_order_no'         => $orderId,
+            'out_biz_order_no'     => $orderId,
+            'open_id'              => $openid,
+            'order_create_time'    => (int) Carbon::parse($createAt)->getPreciseTimestamp(3),
+            'order_status'         => 11, // 支付成功 虚拟
+            'order_path'           => 'pages/my/consume',
+            'product_cover_img_id' => '5acfa29b90c8234ff41ede600cad6a9b715f38871eaf5973',
+        ];
+        $url = $this->API::ORDER_PUSH . '?' . http_build_query([
+            'app_id'       => $this->appId,
+            'access_token' => $this->accessToken,
+        ]);
+
+        return $this->post($url, $data, 'json');
+    }
+
+    public function upload()
+    {
+        $url = 'https://open.kuaishou.com/openapi/mp/developer/file/img/uploadWithUrl?' . http_build_query([
+            'app_id'       => $this->appId,
+            'access_token' => $this->accessToken,
+            'url'          => 'http://fourtiao.oss-cn-beijing.aliyuncs.com/zhangsiye/images/664b34c5afb8cb56d4a3cec398e64948.png',
+        ]);
+
+        return $this->post(
+            $url,
+        );
+    }
+
+    /**
+     * @return string
+     */
+    public function getSign(array $data)
+    {
+        $filterArray = ['sign', 'access_token'];
+        $rList       = [];
+        foreach ($data as $k => $v) {
+            if (in_array($k, $filterArray)) {
+                continue;
+            }
+            $value = trim(strval($v));
+            $len   = strlen($value);
+            if ($len > 1 && '"' == substr($value, 0, 1) && '"' == substr($value, $len, $len - 1)) {
+                $value = substr($value, 1, $len - 1);
+            }
+            $value = trim($value);
+            if ('' == $value || 'null' == $value) {
+                continue;
+            }
+            array_push($rList, "$k=$value");
+        }
+        sort($rList, SORT_STRING);
+        $str = implode('&', $rList);
+        $str .= $this->secret;
+
+        return md5($str);
+    }
+
+    protected function getAccessToken(): string
+    {
+        $res = $this->post($this->API::ACCESS_TOKEN, [
+            'app_id'     => $this->appId,
+            'app_secret' => $this->secret,
+            'grant_type' => 'client_credentials',
+        ]);
+        if (!isset($res['result']) || 1 != $res['result']) {
+            throw new \Exception('获取access token 错误');
+        }
+
+        file_put_contents($this->accessTokenFile, json_encode([
+            'access_token' => $res['access_token'],
+            'expires_at'   => Carbon::now()->timestamp + $res['expires_in'],
+        ]));
+
+        return $res['access_token'];
+    }
+
+    protected function setAccessFileDir(): void
+    {
+        $this->accessTokenDir = storage_path('app/kuaishou');
+    }
+
+    protected function setAccessFilePath(): void
+    {
+        $this->accessTokenFile = storage_path('app/kuaishou/kuaishou_access_token.json');
+    }
+
+    public function getNotifySign(array $data)
+    {
+        $req = file_get_contents('php://input');
+        $str = $req . $this->secret;
+
+        return md5($str);
+    }
+
+    /**
+     * @param string $uri
+     * @param array  $data
+     * @param string $type
+     *
+     * @throws \Exception
+     */
+    protected function post($uri = '', $data = [], $type = 'urlencoded'): array
+    {
+        try {
+            $client = new Client();
+            if ('urlencoded' == $type) {
+                $url     = $uri . '?' . http_build_query($data);
+                $options = [
+                    'verify'  => false,
+                    'headers' => ['Content-Type' => 'x-www-form-urlencoded'],
+                ];
+            } else {
+                $url     = $uri;
+                $options = [
+                    'verify'  => false,
+                    'headers' => ['Content-Type' => 'application/json'],
+                    'body'    => json_encode($data),
+                ];
+            }
+            $res        = $client->post($url, $options);
+            $stringBody = (string) $res->getBody();
+            $res        = json_decode($stringBody, true);
+
+            if (!isset($res['result']) || 1 != $res['result']) {
+                throw new \Exception("请求快手API接口错误,错误码:{$res['result']},错误信息:{$res['error_msg']}");
+            }
+
+            return $res;
+        } catch (GuzzleException $e) {
+            \Log::error($e->getMessage());
+
+            throw new \Exception($e->getMessage());
+        }
+    }
+
+    protected function setNoticeUrl(): void
+    {
+        $this->noticeUrl = env('APP_URL') . '/api/pay/kuaishou/notify';
+    }
+}

+ 35 - 0
app/Helper/SerialNumber.php

xqd
@@ -0,0 +1,35 @@
+<?php
+
+namespace App\Helper;
+
+class SerialNumber
+{
+    /**
+     * 订单号生存规则.
+     *
+     * @param int $bus_type 业务类型
+     * @param int $user_id  用户ID
+     *
+     * @return string 订单号 20位数
+     */
+    public static function createOrderId($user_id = 0, $bus_type = 1)
+    {
+        $userId = substr($user_id, -7);
+        $userId = str_pad($userId, 7, '0', STR_PAD_LEFT);
+        $u1     = substr($userId, 0, 2);
+        $u2     = substr($userId, 2, 2);
+        $u3     = substr($userId, 4, 2);
+        $u4     = substr($userId, 6, 1);
+        unset($userId);
+        $d             = date('ymd');
+        $timestamp     = microtime(true) - strtotime(date('Y-m-d'));
+        list($t1, $t2) = explode('.', $timestamp);
+        unset($timestamp);
+        $t1 = str_pad($t1, 5, '0', STR_PAD_LEFT);
+        $t2 = intval(substr($t2, 0, 1));
+        $b  = intval($bus_type);
+        unset($busType);
+
+        return "{$b}{$u1}{$d}{$u2}{$t1}{$u3}{$t2}{$u4}";
+    }
+}

+ 13 - 0
app/Helper/UniPlatform/BaseAPI.php

xqd
@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Helper\UniPlatform;
+
+abstract class BaseAPI
+{
+    public const ACCESS_TOKEN  = '';
+    public const LOGIN         = '';
+    public const CREATE_ORDER  = '';
+    public const ORDER_PUSH    = '';
+    public const CREATE_QRCODE = '';
+    public const SETTLE        = '';
+}

+ 139 - 0
app/Helper/UniPlatform/BaseUniPlatform.php

xqd
@@ -0,0 +1,139 @@
+<?php
+
+namespace App\Helper\UniPlatform;
+
+/**
+ * Class ByteDance.
+ *
+ * @property string  $appId
+ * @property string  $slat
+ * @property string  $secret
+ * @property string  $token
+ * @property string  $accessTokenDir
+ * @property string  $accessTokenFile
+ * @property string  $accessToken
+ * @property string  $noticeUrl
+ * @property string  $validTimestamp
+ * @property BaseAPI $API
+ */
+abstract class BaseUniPlatform
+{
+    protected $appId;
+    protected $mchId;
+    protected $mchKey;
+    protected $secret;
+    protected $slat;
+    protected $token;
+
+    protected $accessTokenDir;
+    protected $accessTokenFile;
+    protected $accessToken;
+    protected $noticeUrl;
+    // 订单过期时间(秒);
+    protected $validTimestamp = 24 * 60 * 60;
+
+    protected $API;
+
+    /**
+     * @return $this
+     */
+    public function factory(array $config = [])
+    {
+        $this->appId  = $config['app_id'];
+        $this->secret = $config['app_secret'];
+        $this->slat   = $config['slat']    ?? null;
+        $this->token  = $config['token']   ?? null;
+        $this->mchId  = $config['mch_id']  ?? null;
+        $this->mchKey = $config['mch_key'] ?? null;
+
+        $this->setAccessFileDir();
+        $this->setAccessFilePath();
+        $this->setNoticeUrl();
+        $this->accessToken = $this->checkAccessToken();
+
+        return $this;
+    }
+
+    /**
+     * @throws \Exception
+     */
+    public function decryptData(string $sessionKey, string $iv, string $encrypted): array
+    {
+        $decrypted = openssl_decrypt(
+            base64_decode($encrypted, true),
+            'AES-128-CBC',
+            base64_decode($sessionKey),
+            OPENSSL_RAW_DATA,
+            base64_decode($iv)
+        );
+        $decrypted = json_decode($decrypted, true);
+        if (empty($decrypted)) {
+            throw new \Exception('解密数据错误');
+        }
+
+        return $decrypted;
+    }
+
+    /**
+     * 校验access token 是否过期
+     */
+    protected function checkAccessToken()
+    {
+        try {
+            $dir = $this->accessTokenDir;
+            if (!is_dir($dir)) {
+                mkdir($dir, 0755);
+            }
+            if (!is_file($this->accessTokenFile)) {
+                touch($this->accessTokenFile);
+            }
+
+            $accessToken = file_get_contents($this->accessTokenFile);
+            $accessToken = $accessToken ? json_decode($accessToken, true) : null;
+            // 改为 还有5分钟过期就刷新,不然会出现没有过期但是抖音会返回过期的信息
+            if (empty($accessToken) || $accessToken['expires_at'] < time() + 300) {
+                $accessToken = $this->getAccessToken();
+            } else {
+                $accessToken = $accessToken['access_token'];
+            }
+
+            return $accessToken;
+        } catch (\Exception $e) {
+        }
+    }
+
+    /**
+     * 登陆.
+     */
+    abstract protected function login($code): array;
+
+    /**
+     * 接口请求
+     *
+     * @param string $uri
+     * @param array  $data
+     */
+    abstract protected function post($uri = '', $data = []): array;
+
+    /**
+     * 获取Access token.
+     */
+    abstract protected function getAccessToken(): string;
+
+    /**
+     * 创建支付订单.
+     */
+    abstract public function createOrder($outOrderNo, $totalAmount, $openId): array;
+
+    /**
+     * 设置 access token 目录.
+     */
+    abstract protected function setAccessFileDir(): void;
+
+    /**
+     * 设置 access token路径.
+     */
+    abstract protected function setAccessFilePath(): void;
+
+    abstract protected function setNoticeUrl(): void;
+}

+ 52 - 0
app/Helper/UniPlatform/Bytedance/ByteDanceAPI.php

xqd
@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Helper\UniPlatform\Bytedance;
+
+use App\Helper\UniPlatform\BaseAPI;
+
+define('BASE_URL', 'https://developer.toutiao.com/api/apps/v2');
+define('PAY_URL', 'https://developer.toutiao.com/api/apps');
+final class ByteDanceAPI extends BaseAPI
+{
+    /**
+     * 获取 ACCESS_TOKEN.
+     *
+     * @url https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/interface-request-credential/get-access-token
+     */
+    public const ACCESS_TOKEN = BASE_URL . '/token';
+
+    /**
+     * 登陆.
+     *
+     * @url https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/log-in/code-2-session
+     */
+    public const LOGIN = BASE_URL . '/jscode2session';
+
+    /**
+     * 支付下单.
+     *
+     * @url https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/server/ecpay/introduction
+     */
+    public const CREATE_ORDER = PAY_URL . '/ecpay/v1/create_order';
+
+    /**
+     * 订单同步.
+     *
+     * @url https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/ecpay/order/order-sync/
+     */
+    public const ORDER_PUSH = PAY_URL . '/order/v2/push';
+
+    /**
+     * 生成二维码
+     *
+     * @url https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/qr-code/create-qr-code
+     */
+    public const CREATE_QRCODE = PAY_URL . '/qrcode';
+
+    /**
+     * 结算.
+     *
+     * @url https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/ecpay/settlements/settlement/
+     */
+    public const SETTLE = PAY_URL . '/ecpay/v1/settle';
+}

+ 79 - 0
app/Helper/UniPlatform/Bytedance/Payment.php

xqd
@@ -0,0 +1,79 @@
+<?php
+
+namespace App\Helper\UniPlatform\Bytedance;
+
+use App\Helper\ByteDance;
+use App\Models\Pay;
+
+/**
+ * Class Payment.
+ *
+ * @property ByteDance $app
+ */
+class Payment
+{
+    private $app;
+
+    private $fail;
+
+    public function __construct(ByteDance $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * 支付通知.
+     *
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function payNotify(\Closure $closure)
+    {
+        try {
+            call_user_func($closure, $this->getNoticeData(), [$this, 'fail']);
+        } catch (\Exception $e) {
+            $this->fail($e->getMessage());
+        }
+
+        return $this->toResponse();
+    }
+
+    public function fail($msg)
+    {
+        $this->fail = $msg;
+    }
+
+    /**
+     * 获取支付通知数据.
+     *
+     * @throws \Exception
+     */
+    private function getNoticeData()
+    {
+        $notify = \request()->all();
+        // $notify = '{"msg":"{\"appid\":\"tt5b312d8cc40f46b701\",\"cp_orderno\":\"10022082800824490007\",\"cp_extra\":\"\",\"way\":\"2\",\"channel_no\":\"2022082822001477591433078541\",\"channel_gateway_no\":\"\",\"payment_order_no\":\"PCP2022082822540531221069106962\",\"out_channel_order_no\":\"2022082822001477591433078541\",\"total_amount\":1,\"status\":\"SUCCESS\",\"seller_uid\":\"71227862181355706950\",\"extra\":\"\",\"item_id\":\"\",\"paid_at\":1661698458,\"message\":\"\",\"order_id\":\"7136939355201063205\",\"trade_item_list\":null,\"ec_pay_trade_no\":\"NEP2022082822540409483061846962\"}","msg_signature":"804a60e48936b14a739230cef21fe6204427732e","nonce":"78","timestamp":"1661698459","type":"payment"}';
+        // 获取订单信息
+        // $notify = json_decode($notify,true);
+        \Log::info('抖音支付回调==>' . json_encode($notify, JSON_UNESCAPED_SLASHES));
+
+        if ($notify['msg_signature'] !== $this->app->getNotifySign($notify)) {
+            throw new \Exception('签名验证错误');
+        }
+
+        return json_decode($notify['msg'], true);
+    }
+
+    /**
+     * 返回数据.
+     *
+     * @return \Illuminate\Http\JsonResponse
+     */
+    private function toResponse()
+    {
+        $data = [
+            'err_no'   => is_null($this->fail) ? 0 : 1,
+            'err_tips' => is_null($this->fail) ? 'success' : $this->fail,
+        ];
+
+        return response()->json($data);
+    }
+}

+ 53 - 0
app/Helper/UniPlatform/Kuaishou/KuaishouAPI.php

xqd
@@ -0,0 +1,53 @@
+<?php
+
+namespace App\Helper\UniPlatform\Kuaishou;
+
+use App\Helper\UniPlatform\BaseAPI;
+
+define('BASE_URL', 'https://open.kuaishou.com/oauth2');
+define('PAY_URL', 'https://open.kuaishou.com/openapi/mp/developer');
+
+final class KuaishouAPI extends BaseAPI
+{
+    /**
+     * 获取 ACCESS_TOKEN.
+     *
+     * @url https://mp.kuaishou.com/docs/develop/server/getAccessToken.html
+     */
+    public const ACCESS_TOKEN = BASE_URL . '/access_token';
+
+    /**
+     * 登陆.
+     *
+     * @url https://mp.kuaishou.com/docs/develop/server/code2Session.html
+     */
+    public const LOGIN = BASE_URL . '/mp/code2session';
+
+    /**
+     * 支付下单.
+     *
+     * @url https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html
+     */
+    public const CREATE_ORDER = PAY_URL . '/epay/create_order';
+
+    /**
+     * 生成二维码
+     *
+     * @url https://developer.open-douyin.com/docs/resource/zh-CN/mini-app/develop/server/qr-code/create-qr-code
+     */
+    public const CREATE_QRCODE = '';
+
+    /**
+     * 同步订单.
+     *
+     * @url https://mp.kuaishou.com/docs/develop/server/order/push.html
+     */
+    public const ORDER_PUSH = PAY_URL . '/order/v1/report';
+
+    /**
+     * 结算.
+     *
+     * @url https://mp.kuaishou.com/docs/develop/server/epay/interfaceDefinition.html#_3%E3%80%81%E6%94%AF%E4%BB%98%E7%BB%93%E7%AE%97
+     */
+    public const SETTLE = PAY_URL . '/epay/settle';
+}

+ 82 - 0
app/Helper/UniPlatform/Kuaishou/Payment.php

xqd
@@ -0,0 +1,82 @@
+<?php
+
+namespace App\Helper\UniPlatform\Kuaishou;
+
+use App\Helper\Kuaishou;
+
+/**
+ * Class Payment.
+ *
+ * @property Kuaishou $app
+ */
+class Payment
+{
+    private $app;
+
+    private $messageId = '';
+
+    private $fail;
+
+    public function __construct(Kuaishou $app)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * 支付通知.
+     *
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function payNotify(\Closure $closure)
+    {
+        try {
+            call_user_func($closure, $this->getNoticeData(), [$this, 'fail']);
+        } catch (\Exception $e) {
+            $this->fail($e->getMessage());
+        }
+
+        return $this->toResponse();
+    }
+
+    public function fail($msg)
+    {
+        $this->fail = $msg;
+    }
+
+    /**
+     * 获取支付通知数据.
+     *
+     * @throws \Exception
+     */
+    private function getNoticeData()
+    {
+        $notify   = \request()->all();
+        $kwaisign = \request()->header('kwaisign');
+
+        $this->messageId = $notify['message_id'];
+        // 获取订单信息
+        $notify = array_merge($notify, ['kwaisign' => $kwaisign]);
+        \Log::info('快手支付回调==>' . json_encode($notify, JSON_UNESCAPED_SLASHES));
+        if ($kwaisign !== $this->app->getNotifySign($notify)) {
+            throw new \Exception('签名验证错误');
+        }
+
+        return $notify['data'];
+    }
+
+    /**
+     * 返回数据.
+     *
+     * @return \Illuminate\Http\JsonResponse
+     */
+    private function toResponse()
+    {
+        $data = [
+            'result'     => is_null($this->fail) ? 1 : 0,
+            'message_id' => $this->messageId,
+            'fail'       => $this->fail,
+        ];
+
+        return response()->json($data);
+    }
+}

+ 13 - 0
app/Helper/UniPlatform/Wechat/WechatAPI.php

xqd
@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Helper\UniPlatform\Wechat;
+
+use App\Helper\UniPlatform\BaseAPI;
+
+final class WechatAPI extends BaseAPI
+{
+    public const ACCESS_TOKEN  = '';
+    public const LOGIN         = '';
+    public const CREATE_ORDER  = '';
+    public const CREATE_QRCODE = '';
+}

+ 90 - 0
app/Helper/Wechat.php

xqd
@@ -0,0 +1,90 @@
+<?php
+/**
+ * Created by PhpStorm
+ * DateTime: 2022/10/28 22:50.
+ *
+ * @description
+ */
+
+namespace App\Helper;
+
+use App\Helper\UniPlatform\BaseUniPlatform;
+use EasyWeChat\Factory;
+use EasyWeChat\MiniProgram\Application;
+
+class Wechat extends BaseUniPlatform
+{
+    public function mini(): Application
+    {
+        return Factory::miniProgram([
+            'app_id'        => $this->appId,
+            'secret'        => $this->secret,
+            'response_type' => 'array',
+        ]);
+    }
+
+    public function payment(): \EasyWeChat\Payment\Application
+    {
+        return Factory::payment([
+            'sandbox'    => false,
+            'app_id'     => $this->appId,
+            'mch_id'     => $this->mchId,
+            'key'        => $this->mchKey,
+            'cert_path'  => env('WECHAT_PAYMENT_CERT_PATH', 'path/to/cert/apiclient_cert.pem'),    // XXX: 绝对路径!!!!
+            'key_path'   => env('WECHAT_PAYMENT_KEY_PATH', 'path/to/cert/apiclient_key.pem'),      // XXX: 绝对路径!!!!
+            'notify_url' => $this->noticeUrl,
+        ]);
+    }
+
+    /**
+     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
+     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
+     * @throws \GuzzleHttp\Exception\GuzzleException
+     * @throws \Exception
+     */
+    public function createOrder($outOrderNo, $totalAmount, $openId): array
+    {
+        $app     = $this->payment();
+        $payment = $app->order->unify([
+            'body'         => '订单号:' . $outOrderNo,
+            'out_trade_no' => $outOrderNo,
+            'total_fee'    => $totalAmount * 100, // 分
+            'notify_url'   => $this->noticeUrl, // 支付结果通知网址,如果不设置则会使用配置里的默认地址
+            'trade_type'   => 'JSAPI', // 请对应换成你的支付方式对应的值类型
+            'openid'       => $openId,
+        ]);
+        if ('SUCCESS' != $payment['return_code'] || 'SUCCESS' != $payment['result_code']) {
+            throw new \Exception($payment['return_msg']);
+        }
+
+        return [
+            'order_id'    => $payment['prepay_id'],
+            'order_token' => '',
+        ];
+    }
+
+    protected function setNoticeUrl(): void
+    {
+        $this->noticeUrl = env('APP_URL') . '/api/pay/wechat/notify';
+    }
+
+    protected function login($code): array
+    {
+    }
+
+    protected function post($uri = '', $data = []): array
+    {
+    }
+
+    protected function getAccessToken(): string
+    {
+    }
+
+    protected function setAccessFileDir(): void
+    {
+    }
+
+    protected function setAccessFilePath(): void
+    {
+    }
+}

+ 374 - 0
app/Helper/function.php

xqd
@@ -0,0 +1,374 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: pijh
+ * Date: 2017/8/20
+ * Time: 00:33.
+ */
+
+use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\Storage;
+
+if (!function_exists('user_admin_config')) {
+    function user_admin_config($key = null, $value = null)
+    {
+        $session = session();
+
+        if (!$config = $session->get('admin.config')) {
+            $config = config('admin');
+
+            $config['lang'] = config('app.locale');
+        }
+
+        if (is_array($key)) {
+            // 保存
+            foreach ($key as $k => $v) {
+                Arr::set($config, $k, $v);
+            }
+
+            $session->put('admin.config', $config);
+
+            return;
+        }
+
+        if (null === $key) {
+            return $config;
+        }
+
+        return Arr::get($config, $key, $value);
+    }
+}
+
+// 生成随机码
+function create_invite_code()
+{
+    $code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    $rand = $code[rand(0, 25)]
+        . strtoupper(dechex(date('m')))
+        . date('d')
+        . substr(time(), -5)
+        . substr(microtime(), 2, 5)
+        . sprintf('%02d', rand(0, 99));
+    for (
+        $a = md5($rand, true),
+        $s = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+        $d = '',
+        $f = 0;
+        $f < 6;
+        $g = ord($a[$f]),
+        $d .= $s[($g ^ ord($a[$f + 8])) - $g & 0x1F],
+        $f++
+    ) {
+    }
+
+    return $d;
+}
+
+/**
+ * 时间格式化(时间戳).
+ *
+ * @return false|string
+ */
+function uc_time_ago($ptime)
+{
+    date_default_timezone_set('PRC');
+    $etime = time() - $ptime;
+    switch ($etime) {
+        case $etime <= 60:
+            $msg = '刚刚';
+            break;
+        case $etime > 60 && $etime <= 60 * 60:
+            $msg = floor($etime / 60) . '分钟前';
+            break;
+        case $etime > 60 * 60 && $etime <= 24 * 60 * 60:
+            $msg = date('Ymd', $ptime) == date('Ymd', time()) ? '今天 ' . date('H:i', $ptime) : '昨天 ' . date('H:i', $ptime);
+            break;
+        case $etime > 24 * 60 * 60 && $etime <= 2 * 24 * 60 * 60:
+            $msg = date('Ymd', $ptime) + 1 == date('Ymd', time()) ? '昨天 ' . date('H:i', $ptime) : '前天 ' . date('H:i', $ptime);
+            break;
+        case $etime > 2 * 24 * 60 * 60 && $etime <= 12 * 30 * 24 * 60 * 60:
+            $msg = date('Y', $ptime) == date('Y', time()) ? date('m-d H:i', $ptime) : date('Y-m-d H:i', $ptime);
+            break;
+        default:
+            $msg = date('Y-m-d H:i', $ptime);
+    }
+
+    return $msg;
+}
+
+/**
+ * 获取IP地址归属地.
+ *
+ * @return string
+ */
+function get_ip_address($ip)
+{
+    if ('127.0.0.1' == $ip) {
+        return 'Localhost';
+    }
+    $url = 'http://ip.taobao.com/service/getIpInfo.php?ip=' . $ip;
+    $ch  = curl_init($url);
+    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 获取数据返回
+    $location = curl_exec($ch);
+    $location = json_decode($location, true);
+    curl_close($ch);
+
+    if (false != $location && 0 === $location['code']) {
+        return $location['data']['region'] . $location['data']['city'] . $location['data']['county'] . '・' . $location['data']['isp'];
+    }
+
+    return 'unknown';
+}
+
+/**
+ * 递归查询获取分类树结构.
+ *
+ * @param int    $pid
+ * @param int    $level
+ * @param array  $tree
+ * @param string $pidField
+ * @param string $showField
+ *
+ * @return array
+ */
+function get_tree_list(&$data, $pid = 0, $level = 0, &$tree = [], $pidField = 'pid', $showField = 'name')
+{
+    foreach ($data as $key => &$value) {
+        if ($value[$pidField] == $pid) {
+            $value['level']                       = $level;
+            $value['level'] && $value[$showField] = '&nbsp;' . $value[$showField];
+            $value[$showField]                    = str_repeat('ㅡ', $value['level']) . $value[$showField];
+            $tree[]                               = $value;
+            unset($data[$key]);
+            get_tree_list($data, $value['id'], $level + 1, $tree);
+        }
+    }
+    unset($value);
+
+    return $tree;
+}
+
+/**
+ * 递归查询获取分类树结构带child.
+ *
+ * @param int    $pid
+ * @param int    $level
+ * @param string $pidField
+ *
+ * @return array
+ */
+function get_tree_list_with_child(&$data, $pid = 0, $level = 0, $pidField = 'pid')
+{
+    $tree = [];
+    foreach ($data as $key => &$value) {
+        if ($value[$pidField] == $pid) {
+            $value['level'] = $level;
+            $value['child'] = get_tree_list_with_child($data, $value['id'], $level + 1);
+            $tree[]         = $value;
+            unset($data[$key]);
+        }
+    }
+    unset($value);
+
+    return $tree;
+}
+
+/**
+ * 打印sql语句,在sql语句之前调用.
+ */
+function dump_sql()
+{
+    \DB::listen(function ($query) {
+        $bindings = $query->bindings;
+        $i        = 0;
+        $rawSql   = preg_replace_callback('/\?/', function ($matches) use ($bindings, &$i) {
+            $item = isset($bindings[$i]) ? $bindings[$i] : $matches[0];
+            $i++;
+
+            return 'string' == gettype($item) ? "'$item'" : $item;
+        }, $query->sql);
+        echo $rawSql . "\n<br /><br />\n";
+    });
+}
+
+function create_guid($namespace = null)
+{
+    static $guid = '';
+    $uid         = uniqid('', true);
+
+    $data = $namespace;
+    $data .= $_SERVER['REQUEST_TIME'];     // 请求那一刻的时间戳
+    $data .= $_SERVER['HTTP_USER_AGENT'];  // 获取访问者在用什么操作系统
+    $data .= $_SERVER['SERVER_ADDR'];      // 服务器IP
+    $data .= $_SERVER['SERVER_PORT'];      // 端口号
+    $data .= $_SERVER['REMOTE_ADDR'];      // 远程IP
+    $data .= $_SERVER['REMOTE_PORT'];      // 端口信息
+
+    $hash = strtoupper(hash('ripemd128', $uid . $guid . md5($data)));
+    $guid = substr($hash, 0, 8);
+
+    return $guid;
+}
+
+function create_order_number()
+{
+    return date('Ymd') . str_pad(mt_rand(1, 999999), 6, '0', STR_PAD_LEFT);
+}
+
+/**
+ * curl 请求
+ *
+ * @param null $header
+ * @param null $data
+ */
+function curlRequest($url, $header = null, $data = null)
+{
+    $ch = curl_init();
+    curl_setopt($ch, CURLOPT_URL, $url);
+    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+    curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
+    curl_setopt($ch, CURLOPT_HEADER, 1);
+    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+    if ($data) {
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+    }
+    if ($header) {
+        curl_setopt($ch, CURLOPT_HEADER, $header);
+    }
+    $ret = curl_exec($ch);
+    curl_close($ch);
+
+    return $ret;
+}
+
+/**
+ * 数字金额转换成中文大写金额的函数.
+ *
+ * @return string
+ */
+function cny($num)
+{
+    $c1  = '零壹贰叁肆伍陆柒捌玖';
+    $c2  = '分角元拾佰仟万拾佰仟亿';
+    $num = round($num, 2);
+    $num = $num * 100;
+    if (strlen($num) > 10) {
+        return '数据太长,没有这么大的钱吧,检查下';
+    }
+    $i = 0;
+    $c = '';
+    while (1) {
+        if (0 == $i) {
+            $n = substr($num, strlen($num) - 1, 1);
+        } else {
+            $n = $num % 10;
+        }
+        $p1 = substr($c1, 3 * $n, 3);
+        $p2 = substr($c2, 3 * $i, 3);
+        if ('0' != $n || ('0' == $n && ('亿' == $p2 || '万' == $p2 || '元' == $p2))) {
+            $c = $p1 . $p2 . $c;
+        } else {
+            $c = $p1 . $c;
+        }
+        $i   = $i + 1;
+        $num = $num / 10;
+        $num = (int) $num;
+        if (0 == $num) {
+            break;
+        }
+    }
+    $j    = 0;
+    $slen = strlen($c);
+    while ($j < $slen) {
+        $m = substr($c, $j, 6);
+        if ('零元' == $m || '零万' == $m || '零亿' == $m || '零零' == $m) {
+            $left  = substr($c, 0, $j);
+            $right = substr($c, $j + 3);
+            $c     = $left . $right;
+            $j     = $j    - 3;
+            $slen  = $slen - 3;
+        }
+        $j = $j + 3;
+    }
+
+    if ('零' == substr($c, strlen($c) - 3, 3)) {
+        $c = substr($c, 0, strlen($c) - 3);
+    }
+    if (empty($c)) {
+        return '零元整';
+    }
+
+    return $c . '整';
+}
+
+if (!function_exists('valid_url')) {
+    /**
+     * 路径助手函数.
+     *
+     * @param null                   $disk_name
+     * @param false                  $temp
+     * @param DateTimeInterface|null $expiration
+     *
+     * @return string|null
+     */
+    function valid_url($url, $disk_name = null, $temp = false, $expiration = null)
+    {
+        if (is_null($url)) {
+            return null;
+        }
+
+        if (filter_var($url, FILTER_VALIDATE_URL)) {
+            return $url;
+        }
+
+        if ($temp) {
+            $expiration = $expiration ?: now()->addMinutes(30);
+
+            return Storage::disk($disk_name)->temporaryUrl($url, $expiration);
+        }
+
+        return Storage::disk($disk_name)->url($url);
+    }
+}
+
+if (!function_exists('user')) {
+    /**
+     * 获取用户登录信息.
+     *
+     * @return \Illuminate\Contracts\Auth\Authenticatable|\App\Models\User
+     */
+    function user()
+    {
+        return auth('api')->user();
+    }
+}
+
+if (!function_exists('save2Oss')) {
+    /**
+     * @create       JianJia.Zhou<z656123456@gmail.com>
+     *
+     * @param string $type
+     *
+     * @throws Exception
+     */
+    function save2Oss(string $path, $type = 'images'): string
+    {
+        try {
+            $endpoint  = 'http://' . env('ALI_OSS_BUCKET') . '.' . env('ALI_OSS_ENDPOINT');
+            $ossClient = new \OSS\OssClient(env('ALI_OSS_ACCESS_ID'), env('ALI_OSS_ACCESS_SECRET'), $endpoint, true);
+            $filename  = explode('/', $path);
+            $filename  = array_pop($filename);
+            $object    = $type . '/' . $filename;
+            $ossClient->uploadFile(env('ALI_OSS_BUCKET'), $object, $path);
+            unlink($path);
+
+            return $endpoint . '/' . $object;
+        } catch (\OSS\Core\OssException $e) {
+            //            dd($e->getMessage());
+            throw new Exception($e->getMessage());
+        }
+    }
+}

Some files were not shown because too many files changed in this diff