Commit 6c672055 by lujunyi

初始化框架

parents
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4
APP_NAME=tongzhitang
APP_ENV=local
APP_KEY=base64:X9/EwE4Bb65OLiZ54TizbwXD0cceNHFPTLNvP1xJQrY=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=mohe_tzt
DB_USERNAME=root
DB_PASSWORD=root
BROADCAST_DRIVER=log
CACHE_DRIVER=redis
FILESYSTEM_DISK=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=12000
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=123456
REDIS_PORT=6379
ADMIN_HTTPS=false
ADMIN_HELPERS_ENABLE=true
* text=auto
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore
/.phpunit.cache
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.env.production
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.fleet
/.idea
/.vscode
/public/uploads
.DS_Store
## 版本要求
- centos
- php8.1及以上
- mysql5.7及以上
## 初始化安装
```
1、安装composer
2、安装php依赖包
composer install
3、复制 .env.example 为 .env
4、执行生成app_key
php artisan key:generate
5、配置数据库,修改.env文件
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=dcat-admin
DB_USERNAME=root
DB_PASSWORD=
6、配置好后执行命令更新数据库表
php artisan migrate
7、覆盖菜单表、角色表数据
php artisan db:seed --class=AdminTablesSeeder
第一次安装执行 php artisan db:seed --class=InitSeeder
8、创建后台超级管理员账号
php artisan admin:create-user
9、启动服务
php artisan serve
10、访问
http://127.0.0.1:8000
用刚刚创建的账号密码进行登录
11、启用扩展
进入系统,点击“系统>>扩展”,点击列表中的“更新”,再点击“启用”
```
## 单独更新数据库
```
php artisan migrate
```
## 覆盖菜单表、角色表数据
```
php artisan db:seed --class=AdminTablesSeeder
```
## 后台账号创建
```
php artisan admin:create-user
```
## 启动服务
```
php artisan serve
然后浏览器直接访问即可
```
## 备份菜单,权限,账号等数据
```
php artisan admin:export-seed
```
## 备份数据库表,如地区表,多个表,用逗号隔开
```
php artisan iseed 表名 --force
常用
php artisan iseed street
```
## dcat-admin开发文档
https://learnku.com/docs/dcat-admin/2.x
## laravel 9.x开发文档
https://learnku.com/docs/laravel/9.x
## 路由缓存
php artisan api:cache
如果更新了路由需要连续执行下面命令
php artisan route:clear
php artisan api:cache
## 缓存配置
php artisan config:cache
如果更新了配置需要连续执行下面命令
php artisan config:clear
php artisan config:cache
## 优化 Composer 自动加载
composer dump-autoload -o
## 更新model文件的字段注释
php artisan ide-helper:models "App\Models\Shop\model名称"
## 格式化代码
composer fix-style
\ No newline at end of file
<?php
namespace App\Admin\Controllers;
use Dcat\Admin\Form;
use Dcat\Admin\Grid;
use Dcat\Admin\Http\Controllers\UserController;
use Dcat\Admin\Http\Repositories\Administrator;
use Dcat\Admin\Models\Administrator as AdministratorModel;
use Dcat\Admin\Show;
use Dcat\Admin\Widgets\Tree;
class AdminUserController extends UserController
{
protected function grid()
{
return Grid::make(Administrator::with(['roles']), function (Grid $grid) {
$grid->model()->orderBy('id', 'desc');
$grid->column('id', 'ID')->sortable();
$grid->column('username');
$grid->column('name');
$grid->column('is_block', '黑名单')->switch('red');
if (config('admin.permission.enable')) {
$grid->column('roles')->pluck('name')->label('primary', 3);
$permissionModel = config('admin.database.permissions_model');
$roleModel = config('admin.database.roles_model');
$nodes = (new $permissionModel())->allNodes();
$grid->column('permissions')
->if(function () {
return ! $this->roles->isEmpty();
})
->showTreeInDialog(function (Grid\Displayers\DialogTree $tree) use (&$nodes, $roleModel) {
$tree->nodes($nodes);
foreach (array_column($this->roles->toArray(), 'slug') as $slug) {
if ($roleModel::isAdministrator($slug)) {
$tree->checkAll();
}
}
})
->else()
->display('');
}
$grid->column('created_at');
$grid->column('updated_at')->sortable();
$grid->quickSearch(['id', 'name', 'username']);
$grid->showQuickEditButton();
$grid->enableDialogCreate();
$grid->showColumnSelector();
$grid->disableEditButton();
$grid->actions(function (Grid\Displayers\Actions $actions) {
if ($actions->getKey() == AdministratorModel::DEFAULT_ID) {
$actions->disableDelete();
}
});
});
}
protected function detail($id)
{
return Show::make($id, Administrator::with(['roles']), function (Show $show) {
$show->field('id');
$show->field('username');
$show->field('name');
$show->field('is_block', '黑名单');
$show->field('avatar', __('admin.avatar'))->image();
if (config('admin.permission.enable')) {
$show->field('roles')->as(function ($roles) {
if (! $roles) {
return;
}
return collect($roles)->pluck('name');
})->label();
$show->field('permissions')->unescape()->as(function () {
$roles = $this->roles->toArray();
$permissionModel = config('admin.database.permissions_model');
$roleModel = config('admin.database.roles_model');
$permissionModel = new $permissionModel();
$nodes = $permissionModel->allNodes();
$tree = Tree::make($nodes);
$isAdministrator = false;
foreach (array_column($roles, 'slug') as $slug) {
if ($roleModel::isAdministrator($slug)) {
$tree->checkAll();
$isAdministrator = true;
}
}
if (! $isAdministrator) {
$keyName = $permissionModel->getKeyName();
$tree->check(
$roleModel::getPermissionId(array_column($roles, $keyName))->flatten()
);
}
return $tree->render();
});
}
$show->field('created_at');
$show->field('updated_at');
});
}
public function form()
{
return Form::make(Administrator::with(['roles']), function (Form $form) {
$userTable = config('admin.database.users_table');
$connection = config('admin.database.connection');
$id = $form->getKey();
$form->display('id', 'ID');
$form->text('username', trans('admin.username'))
->required()
->creationRules(['required', "unique:{$connection}.{$userTable}"])
->updateRules(['required', "unique:{$connection}.{$userTable},username,$id"]);
$form->text('name', trans('admin.name'))->required();
$form->image('avatar', trans('admin.avatar'))->autoUpload();
if ($id) {
$form->password('password', trans('admin.password'))
->minLength(5)
->maxLength(20)
->customFormat(function () {
return '';
});
} else {
$form->password('password', trans('admin.password'))
->required()
->minLength(5)
->maxLength(20);
}
$form->password('password_confirmation', trans('admin.password_confirmation'))->same('password');
$form->ignore(['password_confirmation']);
$form->switch('is_block', '黑名单')->red();
if (config('admin.permission.enable')) {
$form->multipleSelect('roles', trans('admin.roles'))
->options(function () {
$roleModel = config('admin.database.roles_model');
return $roleModel::all()->pluck('name', 'id');
})
->customFormat(function ($v) {
return array_column($v, 'id');
});
}
$form->display('created_at', trans('admin.created_at'));
$form->display('updated_at', trans('admin.updated_at'));
if ($id == AdministratorModel::DEFAULT_ID) {
$form->disableDeleteButton();
}
})->saving(function (Form $form) {
if ($form->password && $form->model()->get('password') != $form->password) {
$form->password = bcrypt($form->password);
}
if (! $form->password) {
$form->deleteInput('password');
}
});
}
}
<?php
namespace App\Admin\Controllers;
use App\Models\AdminUsers;
use Dcat\Admin\Http\Controllers\AuthController as BaseAuthController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class AuthController extends BaseAuthController
{
protected $view = 'admin.login';
/**
* Handle a login request.
*
* @return mixed
*/
public function postLogin(Request $request)
{
$credentials = $request->only([$this->username(), 'password']);
$remember = (bool) $request->input('remember', false);
/** @var \Illuminate\Validation\Validator $validator */
$validator = Validator::make($credentials, [
$this->username() => 'required',
'password' => 'required',
]);
if ($validator->fails()) {
return $this->validationErrorsResponse($validator);
}
$user = AdminUsers::where(['username' => $credentials['username']])->first();
if (isset($user->is_block) && $user->is_block) {
return $this->validationErrorsResponse([
$this->username() => '账号异常,请联系管理员!',
]);
}
if ($this->guard()->attempt($credentials, $remember)) {
return $this->sendLoginResponse($request);
}
return $this->validationErrorsResponse([
$this->username() => $this->getFailedLoginMessage(),
]);
}
}
<?php
namespace App\Admin\Controllers\Common;
use App\Admin\Repositories\Common\ImportLogRepository;
use App\Common\Util;
use App\Models\Common\ImportLogModel;
use Dcat\Admin\Grid;
use Dcat\Admin\Http\Controllers\AdminController;
class ImportLogController extends AdminController
{
/**
* Make a grid builder.
*/
protected function grid(): Grid
{
return Grid::make(new ImportLogRepository(['operator']), function (Grid $grid) {
$grid->model()->orderByDesc('id');
$grid->column('id');
$grid->column('operator.name', '操作账号');
$grid->column('item', '导入项')->using(ImportLogModel::IMPORT_ITEM_MAP);
$grid->column('process_status')->using(ImportLogModel::PROCESS_STATUS_MAP)->dot(ImportLogModel::PROCESS_STATUS_COLOR);
$grid->column('file_name');
$grid->column('file_size')->display(function ($v) {
return byte_2_human($v);
});
$grid->column('succeed');
$grid->column('failed');
$grid->column('created_at');
$grid->column('updated_at');
$grid->actions(function (Grid\Displayers\Actions $actions) {
// if ($actions->row->failed > 0) {
// $actions->append(new ImportErrorExportAction('错误信息'));
// }
});
$grid->filter(function (Grid\Filter $filter) {
$filter->equal('id');
});
// 行按钮控制
$grid->disableRowSelector(); // 禁用行选择器
$grid->disableDeleteButton(); // 禁用删除按钮
$grid->disableEditButton(); // 禁用编辑按钮
$grid->disableViewButton(); // 禁用详情按钮
// 工具栏按钮控制
$grid->disableBatchActions(); // 禁用批量操作
$grid->disableBatchDelete(); // 禁用批量删除
$grid->disableCreateButton(); // 禁用创建按钮
});
}
/**
* Make a show builder.
*
* @param mixed $id
* @return Show
*/
protected function detail(int $id)
{
return Util::noAuth();
}
/**
* Make a form builder.
*
* @return Form
*/
protected function form()
{
return Util::noAuth();
}
}
<?php
namespace App\Admin\Controllers;
use App\Admin\Metrics\Examples;
use App\Http\Controllers\Controller;
use Dcat\Admin\Http\Controllers\Dashboard;
use Dcat\Admin\Layout\Column;
use Dcat\Admin\Layout\Content;
use Dcat\Admin\Layout\Row;
class HomeController extends Controller
{
public function index(Content $content)
{
return $content
->header('Dashboard')
->description('Description...')
->body(function (Row $row) {
$row->column(6, function (Column $column) {
$column->row(Dashboard::title());
$column->row(new Examples\Tickets());
});
$row->column(6, function (Column $column) {
$column->row(function (Row $row) {
$row->column(6, new Examples\NewUsers());
$row->column(6, new Examples\NewDevices());
});
$column->row(new Examples\Sessions());
$column->row(new Examples\ProductOrders());
});
});
}
}
<?php
namespace App\Admin\Extensions\Form;
use Dcat\Admin\Form\Field;
class JsonEditor extends Field
{
protected $view = 'admin.json-editor';
protected static $css = [
'vendor/jsoneditor-6.2.1/dist/jsoneditor.min.css',
];
protected static $js = [
'vendor/jsoneditor-6.2.1/dist/jsoneditor.min.js',
];
public function render()
{
$json = old($this->column, $this->value());
if (empty($json)) {
$json = '{}';
}
if (! is_string($json)) {
$json = json_encode($json);
} else {
$json = json_encode(json_decode($json)); //兼容json里有类似</p>格式,首次初始化显示会丢失的问题
}
$this->value = $json;
$options = json_encode(config('admin.extension.json-editor.config'));
if (empty($options)) {
$options = '{}';
}
$this->script = <<<EOT
// 生成editor
var container = document.getElementById('jsonEditor');
const options = {
mode: 'tree',
modes: ["code", "form", "text", "tree"],
onChangeJSON(json) {
$('input[id=json_input]').val(JSON.stringify(json))
},
onChangeText(text) {
$('input[id=json_input]').val(text)
},
history: false
}
window['editor_json'] = new JSONEditor(container, options);
// 赋值json
var json = {$json};
window['editor_json'].set(json);
EOT;
return parent::render();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\BatchActions;
use Dcat\Admin\Grid\BatchAction;
use Illuminate\Http\Request;
class CopyBatchAction extends BatchAction
{
// 模型类名
protected $model;
// 复制弹窗标题
protected $copyTitle;
// 不需要复制的字段
protected $except;
public function __construct(?string $model = null, string $copyTitle = '', ?array $except = null)
{
$this->model = $model;
$this->copyTitle = $copyTitle;
$this->except = $except;
}
/**
* 标题
*
* @return string
*/
public function title()
{
return "<i class='fa fa-copy'></i>&nbsp;批量复制&nbsp;";
}
// 确认弹窗信息
public function confirm()
{
return "您确定要批量复制{$this->copyTitle}吗?";
}
// 处理请求
public function handle(Request $request)
{
// 获取选中ID数组
$keys = $this->getKey();
// 获取 parameters 方法传递的参数
$model = $request->get('model');
$except = $request->get('except');
// 复制数据
$newIds = [];
foreach ($keys as $primaryId) {
$qb = $model::find($primaryId)->replicate($except);
$qb->save();
$newIds[] = $qb->id;
}
// 返回响应结果并刷新页面
$newIdsString = implode(',', $newIds);
return $this->response()->alert(true)->success('批量复制成功。新ID:')->detail($newIdsString)->refresh();
}
/**
* 设置要POST到接口的数据
*
* @return array
*/
public function parameters()
{
return [
// 把模型类名传递到接口
'model' => $this->model,
// 不需要复制的字段
'except' => $this->except,
];
}
}
<?php
namespace App\Admin\Extensions\ToolBar\BatchActions;
use Dcat\Admin\Grid\BatchAction;
use Illuminate\Http\Request;
class CopyIdsAction extends BatchAction
{
/**
* 标题
*
* @return string
*/
public function title()
{
return "<i class='fa fa-copy'></i>&nbsp;获取选中ID&nbsp;";
}
// 确认弹窗信息
public function confirm()
{
return '已复制到粘贴板!';
}
// 处理请求
public function handle(Request $request) {}
/**
* 设置动作发起请求前的回调函数,返回false可以中断请求.
*
* @return string
*/
public function actionScript()
{
$warning = __('No data selected!');
return <<<JS
function (data, target, action) {
var key = {$this->getSelectedKeysScript()}
if (key.length === 0) {
Dcat.warning('{$warning}');
return false;
}
// 设置主键为复选框选中的行ID数组
action.options.key = key;
var keyStr = key.join(',');
const input = document.createElement('textarea');
document.body.appendChild(input);
input.value = keyStr;
input.select();
if (document.execCommand('copy')) {
document.execCommand('copy');
Dcat.success('复制成功');
}
input.remove();
}
JS;
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms;
use App\Models\Shop\ShopSku;
use App\Service\Shop\ShopMerchantService;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Alert;
use Dcat\Admin\Widgets\Form;
class CopySkuBatchForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$info = '1、不选择小程序则还复制到SKU对应小程序<br />';
$this->html(Alert::make($info, '注意事项')->warning());
$this->disableResetButton();
$this->textarea('sku_ids', '已勾选SKUID')->attribute('id', 'batch-ids'); //批量选择的行的id通过隐藏元素 提交时一并传递过去
$appMap = ShopMerchantService::getAppCodeIdNameMap();
$this->select('code_id', __('小程序'))->placeholder('请选择复制到哪个小程序')->options($appMap)->help('如果不选择小程序,则还是复制到当前小程序');
$this->text('original_text', '替换前')->placeholder('多个被替换关键词用竖杠 | 隔开。例如:洁柔|维达')->help('例如将SKU标题中品牌名称替换成另一个品牌名称,不填则不替换。');
$this->text('modified_text', '替换后')->placeholder('填写需要被替换成的关键词')->help('如果不填,则是将文字替换为空');
}
public function handle(array $input)
{
$skuIds = $input['sku_ids'];
$skuIdsArray = clean_ids($skuIds, true);
if (! $skuIdsArray) {
return $this->response()->error('请选择至少一个SKU')->refresh();
}
$codeId = $input['code_id'] ?? '';
$originalText = explode('|', $input['original_text']);
$modifiedText = $input['modified_text'] ?? '';
// 复制数据
$newIds = [];
foreach ($skuIdsArray as $primaryId) {
$qb = ShopSku::find($primaryId)->replicate(['online_status', 'sale_num']);
if ($codeId) {
$qb->code_id = $codeId;
}
if ($originalText) {
$qb->title = str_replace($originalText, $modifiedText, $qb->title);
}
$qb->save();
$newIds[] = $qb->id;
}
// 返回响应结果并刷新页面
$newIdsString = implode(',', $newIds);
return $this->response()->alert(true)->success('批量复制成功。新ID:')->detail($newIdsString)->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms;
use App\Common\Util;
use App\Models\Group\GroupMessageRecordModel;
use App\Models\Group\GroupModel;
use App\Service\Alipay\AlipayGroupService;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class SendGroupMsgForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$this->textarea('group_ids', '已勾选群组ID')->attribute('id', 'batch-ids')->help('如果未指定群组ID,则发送所有群组!');
$this->text('title', '消息名称')->default('粉丝福利')->help('只供商家区分消息,消费者看不到~')->required();
$this->radio('msg_type', '消息类型')
->when(GroupMessageRecordModel::MSG_TYPE_TEXT, function (Form $form) {
$form->textarea('text_content', '消息内容')->rules('required_if:msg_type,1')->setLabelClass(['asterisk']);
})
->when(GroupMessageRecordModel::MSG_TYPE_IMAGE, function (Form $form) {
$form->image('image_image', '消息图片')->accept('jpg,png')->uniqueName()->autoUpload()->retainable()->removable(false)->rules('required_if:msg_type,2')->setLabelClass(['asterisk'])->help('支持jpg、png格式,大小1Mb以内');
$form->number('image_width', '图片宽度')->rules('required_if:msg_type,2')->setLabelClass(['asterisk'])->help('图片宽,请必须传图片真实的宽,否则客户端会按照填的宽高进行剪裁');
$form->number('image_height', '图片高度')->rules('required_if:msg_type,2')->setLabelClass(['asterisk'])->help('图片高度,请必须传图片真实的高度,否则客户端会按照填的宽高进行剪裁');
})
->when(GroupMessageRecordModel::MSG_TYPE_LINK, function (Form $form) {
$form->text('link_title', '消息标题')->maxLength(23)->rules('required_if:msg_type,3')->setLabelClass(['asterisk'])->help('消息标题,c侧消息展示的标题');
$form->text('link_desc', '消息描述')->maxLength(64)->rules('required_if:msg_type,3')->setLabelClass(['asterisk']);
$form->text('link_url', '消息跳转地址')->maxLength(1024)->rules('required_if:msg_type,3')->setLabelClass(['asterisk'])->help('消息跳转地址,输入alipays:// 或者 https://的链接。例如:alipays://platformapi/startapp?appId=xxx&url=xxx');
$form->image('link_image', '消息图片')->accept('jpg,png')->uniqueName()->autoUpload()->retainable()->removable(false)->rules('required_if:msg_type,3')->setLabelClass(['asterisk'])->help('支持jpg、png格式,图片宽高1:1,大小1Mb以内');
})
->when(GroupMessageRecordModel::MSG_TYPE_APP, function (Form $form) {
$form->text('app_title', '群消息标题')->maxLength(28)->rules('required_if:msg_type,4')->setLabelClass(['asterisk'])->help('推送的消息文案标题(参考:好物分享来咯!)');
$form->text('app_desc', '群消息描述')->maxLength(34)->rules('required_if:msg_type,4')->setLabelClass(['asterisk'])->help('对推送商品的简单介绍(参考:美妆好物应有尽有,快戳进来看看呀)');
$form->text('app_url', '群消息跳转链接')->maxLength(512)->rules('required_if:msg_type,4')->setLabelClass(['asterisk'])->help('自定义链接或小程序页面链接(自定义链接请输入http,https或alipays开头的链接),例如:alipays://platformapi/startapp?appId=2021***&page=pages%2Findex%2Findex');
$form->text('app_tiny_app_id', '应用id')->maxLength(16)->rules('required_if:msg_type,4')->setLabelClass(['asterisk'])->help('小程序appId。用于入群欢迎语在群里展示');
$form->image('app_image', '消息图片')->accept('jpg,png')->uniqueName()->autoUpload()->retainable()->removable(false)->rules('required_if:msg_type,4')->setLabelClass(['asterisk'])->help('支持jpg、png格式,图片尺寸:202*160,大小1M以内');
})
->options(GroupMessageRecordModel::MSG_TYPE_MAP)
->default(GroupMessageRecordModel::MSG_TYPE_TEXT)
->required();
$this->switch('at_all', '是否@所有人')->help('同一群组每周仅允许发送一条@所有人的消息!请慎重勾选');
}
public function handle(array $input)
{
$title = $input['title'];
$msgType = $input['msg_type'];
$groupIds = $input['group_ids'];
$atAll = $input['at_all'];
$groupIdsArray = clean_ids($groupIds, true);
$qb = GroupModel::query();
if ($groupIdsArray) {
$qb->whereIn('id', $groupIdsArray);
}
$groups = $qb->get()->toArray();
foreach ($groups as $v) {
try {
$channel = $v['channel'];
$msgData = [];
switch ($msgType) {
case GroupMessageRecordModel::MSG_TYPE_TEXT:
$textContent = $input['text_content'];
$msgData = [
'msg_type' => 'TEXT',
'text_msg_content' => [
'content' => $textContent,
],
];
break;
case GroupMessageRecordModel::MSG_TYPE_IMAGE:
$imageWidth = $input['image_width'];
$imageHeight = $input['image_height'];
$imageImage = $input['image_image'];
$imageImageFullUrl = Util::getImgUrl($imageImage);
$service = new AlipayGroupService($channel, false);
$imageImageId = $service->uploadImage($imageImageFullUrl);
$msgData = [
'msg_type' => 'IMAGE',
'image_msg_content' => [
'image_id' => $imageImageId,
'width' => $imageWidth,
'height' => $imageHeight,
],
];
break;
case GroupMessageRecordModel::MSG_TYPE_LINK:
$linkTitle = $input['link_title'];
$linkDesc = $input['link_desc'];
$linkUrl = $input['link_url'];
$linkImage = $input['link_image'];
$linkImageFullUrl = Util::getImgUrl($linkImage);
$service = new AlipayGroupService($channel, false);
$linkImageId = $service->uploadImage($linkImageFullUrl);
$msgData = [
'msg_type' => 'LINK',
'link_msg_content' => [
'title' => $linkTitle,
'desc' => $linkDesc,
'url' => $linkUrl,
'image_id' => $linkImageId,
],
];
break;
case GroupMessageRecordModel::MSG_TYPE_APP:
$appTitle = $input['app_title'];
$appDesc = $input['app_desc'];
$appUrl = $input['app_url'];
$appTinyAppId = $input['app_tiny_app_id'];
$appImage = $input['app_image'];
$appImageFullUrl = Util::getImgUrl($appImage);
$service = new AlipayGroupService($channel, false);
$appImageId = $service->uploadImage($appImageFullUrl);
$msgData = [
'msg_type' => 'APP',
'tiny_app_msg_content' => [
'title' => $appTitle,
'desc' => $appDesc,
'url' => $appUrl,
'tiny_app_id' => $appTinyAppId,
'image_id' => $appImageId,
],
];
break;
}
// 插入记录
$groupMsgRecordModel = new GroupMessageRecordModel;
$groupMsgRecordModel->code_id = $v['code_id'];
$groupMsgRecordModel->channel = $channel;
$groupMsgRecordModel->title = $title;
$groupMsgRecordModel->msg_type = $msgType;
$groupMsgRecordModel->msg_data = $msgData;
$groupMsgRecordModel->group_id = $v['id'];
$groupMsgRecordModel->at_all = $atAll ?? false;
$groupMsgRecordModel->send_status = GroupMessageRecordModel::SEND_STATUS_WAIT;
$groupMsgRecordModel->err_msg = '';
$groupMsgRecordModel->save();
// 发送消息
$service = new AlipayGroupService($channel, false);
$result = $service->groupMsgSend([$v['alipay_group_id']], $title, $msgData, $atAll);
if ($result) {
$groupMsgRecordModel->send_status = GroupMessageRecordModel::SEND_STATUS_SUCCESS;
$groupMsgRecordModel->err_msg = '';
$groupMsgRecordModel->save();
}
} catch (\Exception $e) {
$groupMsgRecordModel->send_status = GroupMessageRecordModel::SEND_STATUS_FAIL;
$groupMsgRecordModel->err_msg = $e->getMessage() ? $e->getMessage() : '未知错误';
$groupMsgRecordModel->save();
}
}
return $this->response()->alert(true)->success('操作完成')->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use App\Service\Common\CommonAppConfigService;
use App\Service\Shop\ShopPointService;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class AddPointForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$this->textarea('user_ids', '用户ID|支付宝UID')->placeholder('如:2200010106,2088702902958383')->help('用户ID或者支付宝UID,逗号等隔开,可以混填,自动识别')->required(); //批量选择的行的id通过隐藏元素 提交时一并传递过去
$appMap = CommonAppConfigService::getBrandCodeNameMap();
$this->select('channel', '出资小程序')->placeholder('请选择用哪个小程序赠送积分')->options($appMap)->required();
$this->number('point', '赠送积分')->min(1)->max(1000)->default(100)->help('单个循环赠送积分不能超过1000');
$this->number('count', '赠送次数')->min(1)->max(20)->default(1)->help('最多循环20次');
}
public function handle(array $input)
{
$userIds = $input['user_ids'];
$point = $input['point'];
$channel = $input['channel'];
$count = $input['count'];
if ($point > 1000 || $point < 1) {
return $this->response()->error('每次赠送积分需在1到1000分之间');
}
if ($count > 20 || $count < 1) {
return $this->response()->error('循环赠送次数不能超过20');
}
$userIds = clean_ids($userIds, true);
if (! $userIds) {
return $this->response()->error('至少填写一个用户ID|支付宝UID');
}
for ($i = 1; $i <= $count; $i++) {
foreach ($userIds as $userId) {
ShopPointService::customSendPoint($channel, $userId, $point);
}
}
return $this->response()->alert(true)->success('赠送积分成功')->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use App\Models\Common\SysConfig;
use App\Models\Shop\ShopProduct;
use App\Service\System\SystemConfigService;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Alert;
use Dcat\Admin\Widgets\Form;
class AddSpecProductForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$info = '1、只能选择同一个小程序的商品<br />2、添加完成后去“系统配置”中检查一下是否正确';
$this->html(Alert::make($info, '注意事项')->warning());
$this->disableResetButton();
$this->textarea('product_ids', '准备合成多规格的商品ID')->attribute('id', 'batch-ids'); //批量选择的行的id通过隐藏元素 提交时一并传递过去
}
public function handle(array $input)
{
$productIds = $input['product_ids'];
$productIdsArray = clean_ids($productIds, true);
if (! $productIdsArray) {
return $this->response()->error('请选择至少一个商品')->refresh();
}
$productList = ShopProduct::whereIn('id', $productIdsArray)->select(['channel', 'id', 'sku_spec'])->get()->toArray();
$channels = array_column($productList, 'channel');
$channelCheck = count(array_unique($channels)) == 1;
if (! $channelCheck) {
return $this->response()->error('您所选择的商品来自不同小程序,无法创建多规格');
}
$channel = $channels[0];
$configName = 'shopProductSceneConfig_'.$channel;
$configInfo = SysConfig::where('name', $configName)->first();
if (! $configInfo) {
return $this->response()->error("此小程序还未在“系统配置”中创建多规格商品项【{$configName}】");
}
$configValue = $configInfo->value;
$configValue = json_decode($configValue, true);
ksort($configValue);
$newValue = [];
foreach ($productList as $item) {
$newValue['specList']['a'.$item['id']] = $item['sku_spec'];
}
$configValue[] = $newValue;
$configInfo->value = json_encode($configValue, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$result = $configInfo->save();
if (! $result) {
return $this->response()->error('添加多规格商品配置失败')->refresh();
}
$key = SystemConfigService::$SYSTEM_CONFIG_NAME.$configName;
app('redis')->setex($key, 3600, json_encode($configValue));
return $this->response()->success('添加多规格商品配置失败')->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use App\Models\Shop\ShopAlipayProduct;
use App\Service\Shop\AlipayProductService;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class AlipayProductSyncBatchForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$this->textarea('product_ids', '商品ID (多个ID使用 逗号或空格或tab 连接')->placeholder('如:308043,308042的6位数编码')->attribute('id', 'batch-ids')->required(); //批量选择的行的id通过隐藏元素 提交时一并传递过去
$this->radio('push_type', '推送数据类型')->options(AlipayProductService::OPT_TYPE_MAP)->required();
$this->radio('page_model', '详情模式')->options(ShopAlipayProduct::PAGE_MODEL_MAP)->required()->help('官方[官方插件版详情页],自有[自定义详情页版]');
}
public function handle(array $input)
{
$productIds = $input['product_ids'];
$productIdsArray = clean_ids($productIds, true);
if (! $productIdsArray) {
return $this->response()->error('请选择至少一个商品')->refresh();
}
$pushType = $input['push_type'];
$pageModel = $input['page_model'];
$isAsync = true; // 是否异步推送
if (! in_array($pushType, AlipayProductService::OPT_TYPE_LIST)) {
return $this->response()->error('推送数据类型不正确');
}
$tip = AlipayProductService::OPT_TYPE_MAP[$pushType];
try {
foreach ($productIdsArray as $productId) {
AlipayProductService::pushProductToAlipay($productId, $pushType, $pageModel, $isAsync);
}
} catch (\Exception $e) {
return $this->response()->error("推送[{$tip}]到支付宝商品库失败:".$e->getMessage());
}
return $this->response()->alert(true)->success("推送[{$tip}]到支付宝商品库完成,请稍后查看推送情况")->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class CopyOrderIdsForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$this->textarea('order_ids', '订单号')->attribute('id', 'batch-order-ids');
$this->disableSubmitButton();
$this->disableResetButton();
}
public function handle(array $input) {}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class CopySkuIdsForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$this->textarea('product_ids', '商品ID')->attribute('id', 'batch-ids');
$this->textarea('sku_ids', 'SKUID')->attribute('id', 'batch-sku-ids');
$this->disableSubmitButton();
$this->disableResetButton();
}
public function handle(array $input) {}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use App\Models\Shop\ShopDeliveryImport;
use App\Service\Shop\ShopService;
use Dcat\Admin\Http\JsonResponse;
use Dcat\Admin\Widgets\Form;
use Dcat\EasyExcel\Excel;
use Exception;
class DeliveryImportForm extends Form
{
/**
* 处理表单提交逻辑.
*/
public function handle(array $input): JsonResponse
{
// 表单参数
$file = $input['import_file'];
$remark = $input['remark'] ?? '';
$filePath = storage_path('uploads/'.$file);
try {
$rows = Excel::import($filePath)->first()->toArray();
$context = '';
foreach ($rows as $row) {
$orderId = $row['订单号'] ?? '';
$deliveryCode = $row['快递公司'] ?? '';
$deliveryNo = $row['快递单号'] ?? '';
if (! $orderId || ! $deliveryCode || ! $deliveryNo) {
continue;
}
$rowData = trim($orderId).','.trim($deliveryNo).','.trim($deliveryCode);
$context .= $rowData."\n"; // 拼接数据并加上换行符
}
$context = rtrim($context, "\n");
$deliveryImport = new ShopDeliveryImport();
$deliveryImport->context = $context;
$deliveryImport->remark = $remark;
if ($deliveryImport->save()) {
$id = $deliveryImport->id;
ShopService::deliveryImportNotice($id);
}
$return = $this->response()->success('导入成功')->refresh();
} catch (Exception $e) {
$return = $this->response()->error('导入失败:'.$e->getMessage());
}
return $return;
}
/**
* 构造表单.
*/
public function form()
{
$this->text('remark', '发货备注')->placeholder('方便自己区分');
$this->file('import_file', '文件')
->disk('admin')
->accept('xls,xlsx,csv')
->autoUpload()
->uniqueName()
->required()
->help("导入要求:<br />
<span style='color:red;'>
1、支持xls、xlsx、csv三种格式!<br />
2、表头必须包含【订单号,快递公司,快递单号】这三列,列的顺序无所谓,多余列会自动过滤!<br />
3、“快递公司”填写发货模板第二个sheet中的“快递公司编码”<br />
4、发货信息必须在第一个sheet!
</span>");
$downloadUrl = admin_url('shop-delivery-template-url');
$this->html("<a target='_blank' href='{$downloadUrl}'>下载发货模板文件</a>");
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use App\Models\Shop\ShopModule;
use App\Models\Shop\ShopProduct;
use Dcat\Admin\Admin;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Alert;
use Dcat\Admin\Widgets\Form;
class EditProductForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$info = '1、只修改想要修改的项,不填则不会修改,批量修改要非常小心<br />2、仅修改商品,不修改SKU信息';
$this->html(Alert::make($info, '注意事项')->warning());
$this->disableResetButton();
$this->textarea('product_ids', '已勾选商品ID')->attribute('id', 'batch-ids'); //批量选择的行的id通过隐藏元素 提交时一并传递过去
$this->select('alipay_cat_id_origin', '选择类目')->attribute(['id' => 'alipay_cat_id_origin'])->help('同一个小程序的商品才能选择支付宝类目,不同小程序只能手动在下方填写支付宝类目编号');
$this->text('alipay_cat_id', '支付宝类目')->attribute(['id' => 'alipay_cat_id']);
Admin::script(<<<'JS'
var productIds = $('#batch-ids').val();
$.ajax({
url: '/api/get-alipayitemcat',
method: 'post',
data: {product_ids: productIds},
success: function(data) {
var select = $('#alipay_cat_id_origin');
select.empty(); // 清空select
select.append('<option value="">请选择...</option>');
$.each(data, function(cat_id, cat_name) {
var option = '<option value="' + cat_id + '">' + cat_name + '</option>';
select.append(option); // 添加到select
});
}
});
$('#alipay_cat_id_origin').on('change', function() {
var sceneId = $('#alipay_cat_id_origin').val();
$('#alipay_cat_id').val(sceneId);
});
JS);
$this->text('pre_title', '标题前缀')->width(6);
$this->text('title', __('商品标题'))->width(6);
$this->currency('crossed_price', __('划线价'))->symbol('¥')->width(6);
$this->currency('mini_price', __('小程序价'))->symbol('¥')->width(6);
$this->currency('mini_coup_price', __('券后价'))->symbol('¥')->width(6);
$this->currency('delivery_fee', __('快递费'))->symbol('¥')->width(6);
$this->text('remark', __('备注'));
$this->number('point', '积分');
$this->radio('edit_module', '是否绑定模块')
->when(1, function (Form $form) {
$modMap = (new ShopModule())->getIdMap();
$form->checkbox('module_ids', __('商品绑定模块'))->options($modMap);
})
->options([
1 => '修改',
2 => '全部取消',
])
->default(1)->help('勾选了全部取消,则会取消掉商品中的模块绑定');
$this->radio('edit_selling_tag', '导购标签')
->when(1, function (Form $form) {
$form->checkbox('selling_tag', '导购标签')->options(ShopProduct::SELLING_TAG_MAP);
})
->options([
1 => '修改',
2 => '全部取消',
])
->default(1)->help('勾选了全部取消,则会取消掉商品中的导购标签');
$this->radio('edit_service_commitment', '修改服务承诺')
->when(1, function (Form $form) {
$form->checkbox('service_commitment', '服务承诺')->options(ShopProduct::SERVICE_COMMITMENT_MAP);
})
->options([
1 => '修改',
2 => '全部取消',
])
->default(1)->help('勾选了全部取消,则会取消掉商品中的服务承诺');
$onlineStatus = ShopProduct::ROW_STATUS_ONLINE_MAP;
// if (Admin::user()->isAdministrator()) {
// $onlineStatus = ShopProduct::ROW_STATUS_ONLINE_MAP;
// }
$this->select('row_status', __('小程序上架状态'))->options($onlineStatus)->width(6);
}
public function handle(array $input)
{
$productIds = $input['product_ids'];
$productIdsArray = clean_ids($productIds, true);
if (! $productIdsArray) {
return $this->response()->error('请选择至少一个商品')->refresh();
}
$data = [];
if ($input['alipay_cat_id']) {
$data['alipay_cat_id'] = trim($input['alipay_cat_id']);
}
if ($input['title']) {
$data['title'] = trim($input['title']);
}
if ($input['crossed_price']) {
$data['crossed_price'] = yuan_2_fen($input['crossed_price']);
}
if ($input['mini_price']) {
$data['mini_price'] = yuan_2_fen($input['mini_price']);
}
if ($input['mini_coup_price']) {
$data['mini_coup_price'] = yuan_2_fen($input['mini_coup_price']);
}
if ($input['delivery_fee']) {
$data['delivery_fee'] = yuan_2_fen($input['delivery_fee']);
}
if ($input['remark']) {
$data['remark'] = $input['remark'];
}
if ($input['point']) {
$data['point'] = $input['point'];
}
if ($input['row_status']) {
$data['row_status'] = $input['row_status'];
}
// 模块ID,数组转化成','连接的字符串
if ($input['module_ids'] && is_array($input['module_ids'])) {
$moduleIds = implode(',', $input['module_ids']);
$data['module_ids'] = strtolower($moduleIds);
}
if ($input['edit_module'] == 2) {
$data['module_ids'] = '';
}
// 导购标签ID,数组转化成','连接的字符串
if ($input['selling_tag'] && is_array($input['selling_tag'])) {
$moduleIds = implode(',', $input['selling_tag']);
$data['selling_tag'] = strtolower($moduleIds);
}
if ($input['edit_selling_tag'] == 2) {
$data['selling_tag'] = '';
}
// 服务承诺ID,数组转化成','连接的字符串
if ($input['service_commitment'] && is_array($input['service_commitment'])) {
$moduleIds = implode(',', $input['service_commitment']);
$data['service_commitment'] = strtolower($moduleIds);
}
if ($input['edit_service_commitment'] == 2) {
$data['service_commitment'] = '';
}
if (! $data && ! $input['pre_title']) {
return $this->response()->error('请至少修改一个内容');
}
if ($data) {
$res = ShopProduct::whereIn('id', $productIdsArray)->update($data);
if (! $res) {
return $this->response()->error('批量修改商品失败')->refresh();
}
}
// 为每个产品标题添加前缀
if ($input['pre_title']) {
$prefix = $input['pre_title'];
$products = ShopProduct::whereIn('id', $productIdsArray)->get();
foreach ($products as $product) {
$product->title = $prefix.$product->title;
$product->save();
}
}
return $this->response()->success('批量修改商品成功')->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use App\Models\Shop\ShopProduct;
use App\Models\Shop\ShopSku;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Alert;
use Dcat\Admin\Widgets\Form;
use Illuminate\Support\Facades\DB;
class EditSkuForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$info = '1、只修改想要修改的项,不填则不会修改,批量修改要非常小心<br />';
$this->html(Alert::make($info, '注意事项')->warning());
$this->disableResetButton();
$this->textarea('sku_ids', '已勾选SKUID')->attribute('id', 'batch-ids'); //批量选择的行的id通过隐藏元素 提交时一并传递过去
// $this->select('alipay_cat_id', '支付宝类目')->width(3)->options($cats);
$this->text('title', __('商品标题'))->width(6);
$this->text('remark', __('备注'))->width(6);
$this->currency('settle_price', __('供货价'))->symbol('¥')->width(6);
$this->currency('crossed_price', __('划线价'))->symbol('¥')->width(6);
$this->currency('guide_price', __('建议售价'))->symbol('¥')->width(6);
$this->text('stock', __('库存'))->width(6);
$this->select('online_status', __('上线状态'))->options(ShopSku::ONLINE_STATUS_ONLINE_MAP)->width(3);
}
public function handle(array $input)
{
$skuIds = $input['sku_ids'];
$skuIdsArray = clean_ids($skuIds, true);
if (! $skuIdsArray) {
return $this->response()->error('请选择至少一个SKU')->refresh();
}
$skuData = [];
$productData = [];
if ($input['title']) {
$skuData['title'] = $productData['title'] = trim($input['title']);
}
if ($input['remark']) {
$skuData['remark'] = trim($input['remark']); // 不同步到商品表
}
if ($input['settle_price']) {
$skuData['settle_price'] = $productData['settle_price'] = yuan_2_fen($input['settle_price']);
}
if ($input['crossed_price']) {
$skuData['crossed_price'] = $productData['crossed_price'] = yuan_2_fen($input['crossed_price']);
}
if ($input['guide_price']) {
$skuData['guide_price'] = $productData['guide_price'] = yuan_2_fen($input['guide_price']);
}
if (isset($input['stock']) && ($input['stock'] >= 0)) {
$skuData['stock'] = $productData['stock'] = (int) $input['stock'];
}
if ($input['online_status']) {
$skuData['online_status'] = $productData['sku_status'] = (int) $input['online_status'];
}
if (! $skuData) {
return $this->response()->error('请至少修改一个内容');
}
DB::beginTransaction();
try {
ShopSku::whereIn('id', $skuIdsArray)->update($skuData);
ShopProduct::whereIn('sku_id', $skuIdsArray)->update($productData);
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
return $this->response()->error('批量修改SKU失败!');
}
// 如果修改了库存,需要批量修改redis中对应的库存
if (isset($input['stock']) && ($input['stock'] >= 0)) {
foreach ($skuIdsArray as $skuId) {
$key = "kv:shop:product_stock_$skuId";
app('redis')->del($key);
}
}
return $this->response()->success('批量修改SKU成功')->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use App\Service\Shop\ShopService;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class ManualAddProductForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$this->textarea('sku_ids', 'SKU ID (多个ID使用 逗号或空格或tab 连接')->placeholder('如:56123,56125')->attribute('id', 'batch-ids')->required(); //批量选择的行的id通过隐藏元素 提交时一并传递过去
$this->text('remark', __('批量备注'))->width('400px')->default('');
$this->text('rank', __('排序权重'))->width('200px')->default(10);
}
public function handle(array $input)
{
$skuIds = $input['sku_ids'];
$rank = $input['rank'];
$remark = trim($input['remark']);
if ($rank < 1) {
$rank = 10;
}
$skuIds = clean_ids($skuIds, true);
$errMsg = [];
foreach ($skuIds as $k => $skuId) {
if ($skuId < 10000) {
$errMsg[] = "skuID错误($skuId)";
unset($skuIds[$k]);
}
}
if (! $skuIds) {
return $this->response()->error('skuID不能为空')->refresh();
}
$newProductIds = [];
foreach ($skuIds as $skuId) {
$newProductIds[] = ShopService::fetchAndSaveProductInfo($skuId, $rank, $remark);
}
$newProductIdsString = implode(',', $newProductIds);
return $this->response()->alert(true)->success('添加商品成功。新ID:')->detail($newProductIdsString)->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use App\Service\Common\CommonAppConfigService;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class MiniClearCacheForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$this->select('channel', __('小程序'))->options(CommonAppConfigService::getBrandCodeNameMap())->width(4)->required();
}
public function handle(array $input)
{
$channel = $input['channel'];
if (! $channel) {
return $this->response()->error('不能清空配置(请选择小程序)')->refresh();
}
$res = CommonAppConfigService::clearAppConfCache($channel);
return $this->response()->success("清空配置成功 ($channel) ($res)")->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolBar\Forms\Shop;
use App\Models\Shop\ShopOrderRefund;
use App\Models\Shop\ShopSubOrder;
use App\Service\Shop\ShopService;
use Dcat\Admin\Admin;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class OrderRefundApplyForm extends Form implements LazyRenderable
{
use LazyWidget;
public function form()
{
$this->textarea('orderId', __('订单号'))->placeholder('请添加订单号,多个单号请换行,一行一个')->rows(8)->required();
// $this->text('subOrderId', __('子订单号'))->width('200px')->required();
$this->radio('reasonId', __('退款原因ID'))->options(ShopOrderRefund::REFUND_REASON_ID_MAP)->required();
$this->text('detail', __('退款说明'))->default('');
}
public function handle(array $input)
{
if (! Admin::user()->inRoles(['administrator', 'adminNormal', 'operatorAdmin'])) {
return $this->response()->error('抱歉,您没有权限操作~');
}
$orderId = $input['orderId'];
// $subOid = $input['subOrderId'];
$reasonId = $input['reasonId'];
$detail = $input['detail'];
if (! $orderId) {
return $this->response()->error('订单号不能为空');
}
$orderIds = clean_ids($orderId, true);
if (count($orderIds) <= 0) {
return $this->response()->error('订单号不能为空')->refresh();
}
if ($reasonId < 100) {
return $this->response()->error('退款原因ID不正确');
}
if (! $detail) {
return $this->response()->error('退款说明不能为空');
}
$orderList = (new ShopSubOrder())->getListByOrdIds($orderIds, ['id', 'order_id']);
// 批量申请退款
foreach ($orderList as $v) {
$flag = ShopOrderRefund::where('order_id', $v['order_id'])->exists();
if ($flag) {
continue;
}
try {
ShopService::applyRefund($v['id'], $reasonId, $detail);
} catch (\Exception $e) {
return $this->response()->error("申请错误。订单号[{$v['order_id']}]:".($e->getMessage()));
}
}
return $this->response()->success('退款申请已提交')->refresh();
}
}
<?php
namespace App\Admin\Extensions\ToolRow\Actions;
use Dcat\Admin\Grid\RowAction;
use Illuminate\Http\Request;
class CopyAction extends RowAction
{
protected $model; // 需要复制的model
protected $except; // 不需要复制的字段
public function __construct(?string $model = null, ?array $except = null)
{
$this->model = $model;
$this->except = $except;
}
/**
* 标题
*
* @return string
*/
public function title()
{
return "<i class='fa fa-copy'></i>&nbsp;复制&nbsp;";
}
/**
* 设置确认弹窗信息,如果返回空值,则不会弹出弹窗
*
* 允许返回字符串或数组类型
*
* @return array|string|void
*/
public function confirm()
{
return [
// 确认弹窗 title
'您确定要复制这行数据吗?',
// 确认弹窗 content
$this->row->id,
];
}
/**
* 处理请求
*
*
* @return \Dcat\Admin\Actions\Response
*/
public function handle(Request $request)
{
// 获取当前行ID
$id = $this->getKey();
// 获取 parameters 方法传递的参数
$model = $request->get('model');
$except = $request->get('except');
// 复制数据
$qb = $model::find($id)->replicate($except);
$qb->save();
$newId = $qb->id;
// 返回响应结果并刷新页面
return $this->response()->alert(true)->success('复制成功。新ID:')->detail($newId)->refresh();
}
/**
* 设置要POST到接口的数据
*
* @return array
*/
public function parameters()
{
return [
// 发送当前行 id 字段数据到接口
'id' => $this->row->id,
// 把模型类名传递到接口
'model' => $this->model,
// 不需要复制的字段
'except' => $this->except,
];
}
}
<?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,
]);
}
/**
* 设置卡片头部内容.
*
* @param mixed $desktop
* @param mixed $mobile
* @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
);
}
}
<?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
);
}
}
<?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
);
}
}
<?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
);
}
}
<?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
);
}
}
<?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);
}
}
<?php
namespace App\Admin\Repositories\Common;
use App\Models\Common\ImportLogModel as Model;
use Dcat\Admin\Repositories\EloquentRepository;
class ImportLogRepository extends EloquentRepository
{
/**
* Model.
*
* @var string
*/
protected $eloquentClass = Model::class;
}
<?php
use App\Admin\Extensions\Form\JsonEditor;
use Dcat\Admin\Admin;
use Dcat\Admin\Form;
use Dcat\Admin\Grid;
use Dcat\Admin\Grid\Column;
/**
* 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');
*/
// 重写内部视图文件
app('view')->prependNamespace('admin', resource_path('views/admin'));
// json编辑器
Form::extend('json', JsonEditor::class);
Grid::resolving(function (Grid $grid) {
$grid->withBorder(true); // 默认显示表格边框
});
// Column::resolving(function (Column $column) {
// $column->setAttributes(['style' => 'font-size:smaller']); // 默认显示表格边框
// });
<?php
use Dcat\Admin\Admin;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Route;
Admin::routes();
Route::group([
'prefix' => config('admin.route.prefix'),
'namespace' => config('admin.route.namespace'),
'middleware' => config('admin.route.middleware'),
], function (Router $router) {
$router->resource('auth/users', 'AdminUserController');
$router->get('/', 'HomeController@index');
// 导入日志
$router->resource('/import-log', 'Common\ImportLogController')->names('import-log');
});
<?php
/**
* Created by PhpStorm.
* User: Xiao Mo
* Date: 2020/4/23
* Time: 11:49 上午
*/
namespace App\Common;
use Dcat\Admin\Widgets\Alert;
class Util
{
/**
* 获取图片完整的URL地址
*
* @param string|null $imgUrl 图片URL或者URI
*/
public static function getImgUrl($imgUrl): string
{
if (! $imgUrl) {
return '';
}
if (strpos($imgUrl, 'http') === false) {
$preUrl = substr($imgUrl, 0, 2) == '//' ? 'https:' : ComConst::IMG_URL.'/';
$imgUrl = $preUrl.$imgUrl;
}
return $imgUrl;
}
/**
* 给URL添加https头
*
* @return string
*/
public static function appendHttps(string $url)
{
if ($url && substr($url, 0, 2) == '//') {
$url = 'https:'.$url;
}
return $url;
}
/**
* 格式化价格 (分->元)
*
* @return string
*/
public static function formatPrice($price)
{
return $price ? number_format($price / 100, 2, '.', '') : '';
}
/**
* 获取数组里面指定 key 的子数组(主要用于数据库操作)
*
* @return array
*/
public static function getArrayKV(array $arr, array $keys)
{
$kv = [];
foreach ($keys as $k) {
$kv[$k] = $arr[$k] ?? '';
}
return $kv;
}
/**
* 把数组转化成json字符串
*
* @param array|object $data
*/
public static function array2json($data): string
{
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 获取URL本地图片文件
*
* @return string 如:/data/abc/local_img.jpg
*/
public static function getLocalImageFile(string $imageUrl): string
{
//如果图片没有域名则添加域名
$imageUrl = self::getImgUrl($imageUrl);
if (! $imageUrl) {
return '';
}
//把图片下载到本地
$fileExt = strrchr(strstr($imageUrl, '?', true), '.') ?: '.jpg';
$fileName = date('YmdHis').mt_rand(1100, 9900).$fileExt;
$tempFile = storage_path('uploads/')."/img_{$fileName}";
$fileCont = file_get_contents($imageUrl);
if (! $fileCont) {
Log::error('【下载图片错误】获取图片内容失败', [
'imgFile' => $imageUrl,
'tempFile' => $tempFile,
]);
return '';
}
$isSave = file_put_contents($tempFile, $fileCont);
if (! $isSave) {
Log::error('【下载图片错误】保存图片内容到本地失败', [
'imgFile' => $imageUrl,
'tempFile' => $tempFile,
]);
return '';
}
if (! is_file($tempFile)) {
Log::error('【下载图片错误】临时本地图片不存在', [
'imgFile' => $imageUrl,
'tempFile' => $tempFile,
]);
return '';
}
return $tempFile;
}
/**
* 删除本地文件
*/
public static function deleteFile(string $filePath, ...$moreFile)
{
//array_unshift($moreFile, $filePath);
$moreFile[] = $filePath;
foreach ($moreFile as $file) {
if (is_file($file)) {
unlink($file);
}
}
}
/**
* 返回不支持操作的错误信息 warning.
*/
public static function noAuth(): Alert
{
$alert = Alert::make('此功能不允许通过此操作实现。', '未提供的操作');
$alert->warning();
$alert->icon('feather icon-alert-triangle');
return $alert;
}
}
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*/
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* The list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->reportable(function (Throwable $e) {
//
});
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
}
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's middleware aliases.
*
* Aliases may be used instead of class names to conveniently assign middleware to routes and groups.
*
* @var array<string, class-string|string>
*/
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'watermark' => \App\Http\Middleware\WatermarkMiddleware::class,
];
}
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*/
protected function redirectTo(Request $request): ?string
{
return $request->expectsJson() ? null : route('login');
}
}
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [
//
];
}
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
//
];
}
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts(): array
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}
<?php
namespace App\Http\Middleware;
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
class ValidateSignature extends Middleware
{
/**
* The names of the query string parameters that should be ignored.
*
* @var array<int, string>
*/
protected $except = [
// 'fbclid',
// 'utm_campaign',
// 'utm_content',
// 'utm_medium',
// 'utm_source',
// 'utm_term',
];
}
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [
//
];
}
<?php
namespace App\Http\Middleware;
use Closure;
use Dcat\Admin\Admin;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* 向页面输出 JavaScript 代码来动态添加水印
*/
class WatermarkMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (Admin::user()) {
$username = Admin::user()->username;
$nickname = Admin::user()->name;
$script = "<script>
var username = '{$username}';
var nickname = '{$nickname}';
// 创建水印元素的样式
var watermarkStyle = 'position: fixed; transform: rotate(-30deg); opacity: 0.1; z-index: 1000; font-size: 1em; pointer-events: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; background: transparent;';
// 获取页面宽度和高度
var pageWidth = document.documentElement.scrollWidth;
var pageHeight = document.documentElement.scrollHeight;
var spacing = 300; // 间距为 150px,可以根据需求调整
// 创建水印元素并添加到页面中
for (var x = -pageHeight; x < pageWidth; x += spacing) {
for (var y = -pageWidth; y < pageHeight; y += spacing) {
var watermark = document.createElement('div');
watermark.style.cssText = watermarkStyle + 'top: ' + y + 'px; left: ' + x + 'px;';
// 添加用户名和昵称
var usernameDiv = document.createElement('div');
usernameDiv.innerText = nickname;
var nicknameDiv = document.createElement('div');
nicknameDiv.innerText = username;
watermark.appendChild(usernameDiv);
watermark.appendChild(nicknameDiv);
document.body.appendChild(watermark);
}
}
</script>";
// 将 JavaScript 代码添加到响应内容中
$response = $next($request);
$content = $response->getContent();
$content = str_replace('</body>', $script.'</body>', $content);
$response->setContent($content);
return $response;
}
return $next($request);
}
}
<?php
namespace App\Models;
use Dcat\Admin\Traits\HasDateTimeFormatter;
use Illuminate\Database\Eloquent\Model;
/**
* App\Models\AdminUsers
*
* @property int $id
* @property string $username
* @property string $password
* @property string $name
* @property string|null $avatar
* @property string|null $remember_token
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
*
* @method static \Illuminate\Database\Eloquent\Builder|AdminUsers newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|AdminUsers newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|AdminUsers query()
*
* @mixin \Eloquent
*/
class AdminUsers extends Model
{
use HasDateTimeFormatter;
public function getIdNameMap(): array
{
return self::orderBy('id', 'desc')->pluck('name', 'id')->toArray();
}
}
<?php
namespace App\Models\Common;
use Dcat\Admin\Models\Administrator;
use Dcat\Admin\Traits\HasDateTimeFormatter;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* @property string $item
* @property int $operator
* @property int $id
*/
class ImportLogModel extends Model
{
use HasDateTimeFormatter;
protected $table = 'import_log';
// 导入文件处理状态:状态取值[0=待处理,1=处理中,2=处理成功,3=处理失败]
const PROCESS_STATUS_WAIT = 0;
const PROCESS_STATUS_DOING = 1;
const PROCESS_STATUS_SUCCESS = 2;
const PROCESS_STATUS_FAILED = 3;
// 导入文件处理状态-文字映射
const PROCESS_STATUS_MAP = [
self::PROCESS_STATUS_WAIT => '待处理',
self::PROCESS_STATUS_DOING => '处理中',
self::PROCESS_STATUS_SUCCESS => '处理成功',
self::PROCESS_STATUS_FAILED => '处理失败',
];
// 导入文件处理状态-颜色映射
const PROCESS_STATUS_COLOR = [
self::PROCESS_STATUS_WAIT => 'default',
self::PROCESS_STATUS_DOING => 'warning',
self::PROCESS_STATUS_SUCCESS => 'success',
self::PROCESS_STATUS_FAILED => 'danger',
];
// 导入模块:状态取值[sku=SKU,coupon=优惠券]
const IMPORT_ITEM_SKU = 'sku';
const IMPORT_ITEM_COUPON = 'coupon';
// 导入文件处理状态-文字映射
const IMPORT_ITEM_MAP = [
self::IMPORT_ITEM_SKU => 'SKU',
self::IMPORT_ITEM_COUPON => '优惠券',
];
/**
* 导入日志有一个操作者.
*/
public function operator(): BelongsTo
{
return $this->belongsTo(Administrator::class, 'operator', 'id');
}
}
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}
<?php
namespace App\Providers;
// use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
//
];
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
//
}
}
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}
<?php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{
/**
* The event to listener mappings for the application.
*
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
/**
* Register any events for your application.
*/
public function boot(): void
{
//
}
/**
* Determine if events and listeners should be automatically discovered.
*/
public function shouldDiscoverEvents(): bool
{
return false;
}
}
<?php
namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to your application's "home" route.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
public const HOME = '/home';
/**
* Define your route model bindings, pattern filters, and other route configuration.
*/
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
});
}
}
<?php
if (! function_exists('fen_2_yuan')) {
/**
* 分转换成元
*
* @param string $fen
* @return string
*/
function fen_2_yuan($fen)
{
return $fen ? bcdiv($fen, '100', 2) : '0';
}
}
if (! function_exists('yuan_2_fen')) {
/**
* 元转换成分
*
* @param string $yuan
* @return int
*/
function yuan_2_fen($yuan)
{
$yuan = money_format($yuan); // 首先移除可能存在的千分位逗号
return intval(bcmul($yuan, '100'));
}
}
if (! function_exists('money_format')) {
/**
* 去除金额千分位逗号
*
* @param string $yuan
* @return string
*/
function money_format($yuan)
{
return $yuan ? str_replace(',', '', $yuan) : $yuan;
}
}
if (! function_exists('object_2_array')) {
// 对象转换成数组
function object_2_array($object)
{
return array_map('get_object_vars', $object);
}
}
if (! function_exists('byte_2_human')) {
// 换算成人可读文件大小
function byte_2_human($byte) //传入字节单位
{
$KB = 1024;
$MB = $KB * 1024;
$GB = $MB * 1024;
$TB = $GB * 1024;
if ($byte < $KB) {
return $byte.'B';
} elseif ($byte < $MB) {
//取两位小数四舍五入
return round($byte / $KB, 2).'KB';
} elseif ($byte < $GB) {
return round($byte / $MB, 2).'MB';
} elseif ($byte < $TB) {
return round($byte / $GB, 2).'GB';
} else {
return round($byte / $TB, 2).'TB';
}
}
}
if (! function_exists('clean_ids')) {
/**
* 清理输入字符串,提取数字ID,并返回一个整数数组或以逗号分隔的字符串。
*
* @param string $idsString 原始输入字符串,包含多个ID和不同的分隔符
* @param bool $returnArray 是否返回数组,默认为 false,返回字符串
* @return array|string 返回整数ID数组或以逗号分隔的ID字符串
*/
function clean_ids($idsString, $returnArray = false)
{
// 替换所有非数字字符为英文逗号
$ids = preg_replace('/[\D]+/', ',', trim($idsString));
// 移除字符串开头和结尾的逗号
$ids = trim($ids, ',');
if (! $returnArray) {
// 返回处理后的字符串
return $ids;
}
// 分割字符串为数组,并过滤掉空字符串
$idArr = array_filter(explode(',', $ids), 'strlen');
// 将数组的每个元素转换为整数并去重
$idArr = array_unique($idArr);
return $idArr;
}
}
if (! function_exists('is_sequential')) {
function is_sequential($array)
{
$minCount = min($array);
$maxCount = max($array);
if ($minCount != 1 || $maxCount != count($array)) {
return false;
}
$expectedCounts = range(1, count($array));
if (array_values($array) != $expectedCounts) {
return false;
}
return true;
}
}
#!/usr/bin/env php
<?php
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any of our classes manually. It's great to relax.
|
*/
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
new Symfony\Component\Console\Output\ConsoleOutput
);
/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/
$kernel->terminate($input, $status);
exit($status);
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
|
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.
|
*/
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
/*
|--------------------------------------------------------------------------
| Return The Application
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
|
*/
return $app;
["pre-commit","pre-push","post-merge"]
\ No newline at end of file
{
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"],
"license": "MIT",
"require": {
"php": "^8.1",
"dcat/easy-excel": "^1.1",
"dcat/laravel-admin": "2.*",
"dcat/laravel-wherehasin": "^0.8.0",
"guanguans/dcat-login-captcha": "^1.1",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.10",
"laravel/helpers": "^1.6",
"laravel/sanctum": "^3.3",
"laravel/tinker": "^2.8",
"overtrue/laravel-query-logger": "^3.1",
"predis/predis": "^2.2",
"sparkinzy/dcat-viewer": "^1.0",
"iidestiny/laravel-filesystem-oss": "^3.0"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^2.13",
"brainmaestro/composer-git-hooks": "^3.0@alpha",
"fakerphp/faker": "^1.9.1",
"kitloong/laravel-migrations-generator": "^6.10",
"laravel/pint": "^1.0",
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^7.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^10.1",
"reliese/laravel": "^1.2",
"spatie/laravel-ignition": "^2.0"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
},
"files": [
"app/helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
"cghooks update"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
],
"post-merge": "composer install",
"post-install-cmd": [
"cghooks add --ignore-lock",
"cghooks update"
],
"phpstan": "phpstan analyse --memory-limit=-1",
"check-style": "vendor/bin/pint --test",
"fix-style": "vendor/bin/pint",
"test": "phpunit --colors"
},
"extra": {
"hooks": {
"pre-commit": [
"composer check-style"
],
"pre-push": [
"composer check-style"
],
"config": {
"stop-on-failure": [
"pre-commit",
"pre-push"
]
}
},
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"repositories": [
]
}
This source diff could not be displayed because it is too large. You can view the blob instead.
<?php
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider;
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application. This value is used when the
| framework needs to place the application's name in a notification or
| any other location as required by the application or its packages.
|
*/
'name' => env('APP_NAME', 'Laravel'),
/*
|--------------------------------------------------------------------------
| Application Environment
|--------------------------------------------------------------------------
|
| This value determines the "environment" your application is currently
| running in. This may determine how you prefer to configure various
| services the application utilizes. Set this in your ".env" file.
|
*/
'env' => env('APP_ENV', 'production'),
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => (bool) env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| your application so that it is used when running Artisan tasks.
|
*/
'url' => env('APP_URL', 'http://localhost'),
'asset_url' => env('ASSET_URL'),
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. We have gone
| ahead and set this to a sensible default for you out of the box.
|
*/
'timezone' => 'Asia/Shanghai',
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/
'locale' => 'zh_CN',
/*
|--------------------------------------------------------------------------
| Application Fallback Locale
|--------------------------------------------------------------------------
|
| The fallback locale determines the locale to use when the current one
| is not available. You may change the value to correspond to any of
| the language folders that are provided through your application.
|
*/
'fallback_locale' => 'en',
/*
|--------------------------------------------------------------------------
| Faker Locale
|--------------------------------------------------------------------------
|
| This locale will be used by the Faker PHP library when generating fake
| data for your database seeds. For example, this will be used to get
| localized telephone numbers, street address information and more.
|
*/
'faker_locale' => 'en_US',
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| This key is used by the Illuminate encrypter service and should be set
| to a random, 32 character string, otherwise these encrypted strings
| will not be safe. Please do this before deploying an application!
|
*/
'key' => env('APP_KEY'),
'cipher' => 'AES-256-CBC',
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => 'file',
// 'store' => 'redis',
],
/*
|--------------------------------------------------------------------------
| Autoloaded Service Providers
|--------------------------------------------------------------------------
|
| The service providers listed here will be automatically loaded on the
| request to your application. Feel free to add your own services to
| this array to grant expanded functionality to your applications.
|
*/
'providers' => ServiceProvider::defaultProviders()->merge([
/*
* Package Service Providers...
*/
Iidestiny\LaravelFilesystemOss\OssStorageServiceProvider::class,
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
])->toArray(),
/*
|--------------------------------------------------------------------------
| Class Aliases
|--------------------------------------------------------------------------
|
| This array of class aliases will be registered when this application
| is started. However, feel free to register as many as you wish as
| the aliases are "lazy" loaded so they don't hinder performance.
|
*/
'aliases' => Facade::defaultAliases()->merge([
// 'Example' => App\Facades\Example::class,
])->toArray(),
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expiry time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
| The throttle setting is the number of seconds a user must wait before
| generating more password reset tokens. This prevents the user from
| quickly generating a very large amount of password reset tokens.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "pusher", "ably", "redis", "log", "null"
|
*/
'default' => env('BROADCAST_DRIVER', 'null'),
/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over websockets. Samples of
| each available type of connection are provided inside this array.
|
*/
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
'port' => env('PUSHER_PORT', 443),
'scheme' => env('PUSHER_SCHEME', 'https'),
'encrypted' => true,
'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
],
'client_options' => [
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
],
],
'ably' => [
'driver' => 'ably',
'key' => env('ABLY_KEY'),
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
],
];
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Cache Store
|--------------------------------------------------------------------------
|
| This option controls the default cache connection that gets used while
| using this caching library. This connection is used when another is
| not explicitly specified when executing a given caching function.
|
*/
'default' => env('CACHE_DRIVER', 'file'),
/*
|--------------------------------------------------------------------------
| Cache Stores
|--------------------------------------------------------------------------
|
| Here you may define all of the cache "stores" for your application as
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "apc", "array", "database", "file",
| "memcached", "redis", "dynamodb", "octane", "null"
|
*/
'stores' => [
'apc' => [
'driver' => 'apc',
],
'array' => [
'driver' => 'array',
'serialize' => false,
],
'database' => [
'driver' => 'database',
'table' => 'cache',
'connection' => null,
'lock_connection' => null,
],
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
'lock_path' => storage_path('framework/cache/data'),
],
'memcached' => [
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
],
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
'octane' => [
'driver' => 'octane',
],
],
/*
|--------------------------------------------------------------------------
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| When utilizing the APC, database, memcached, Redis, or DynamoDB cache
| stores there might be other applications using the same cache. For
| that reason, you may prefix every cache key to avoid collisions.
|
*/
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for all database work. Of course
| you may use many connections at once using the Database library.
|
*/
'default' => env('DB_CONNECTION', 'mysql'),
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DATABASE_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix_indexes' => true,
'search_path' => 'public',
'sslmode' => 'prefer',
],
'sqlsrv' => [
'driver' => 'sqlsrv',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '1433'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix_indexes' => true,
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
],
],
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run in the database.
|
*/
'migrations' => 'migrations',
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer body of commands than a typical key-value system
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
*/
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
],
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| Here you may specify the default filesystem disk that should be used
| by the framework. The "local" disk, as well as a variety of cloud
| based disks are available to your application. Just store away!
|
*/
'default' => env('FILESYSTEM_DISK', 'local'),
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Here you may configure as many filesystem "disks" as you wish, and you
| may even configure multiple disks of the same driver. Defaults have
| been set up for each driver as an example of the required values.
|
| Supported Drivers: "local", "ftp", "sftp", "s3"
|
*/
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
'throw' => false,
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
],
],
/*
|--------------------------------------------------------------------------
| Symbolic Links
|--------------------------------------------------------------------------
|
| Here you may configure the symbolic links that will be created when the
| `storage:link` Artisan command is executed. The array keys should be
| the locations of the links and the values should be their targets.
|
*/
'links' => [
public_path('storage') => storage_path('app/public'),
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Hash Driver
|--------------------------------------------------------------------------
|
| This option controls the default hash driver that will be used to hash
| passwords for your application. By default, the bcrypt algorithm is
| used; however, you remain free to modify this option if you wish.
|
| Supported: "bcrypt", "argon", "argon2id"
|
*/
'driver' => 'bcrypt',
/*
|--------------------------------------------------------------------------
| Bcrypt Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Bcrypt algorithm. This will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 12),
'verify' => true,
],
/*
|--------------------------------------------------------------------------
| Argon Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Argon algorithm. These will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'argon' => [
'memory' => 65536,
'threads' => 1,
'time' => 4,
'verify' => true,
],
];
<?php
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
use Monolog\Processor\PsrLogMessageProcessor;
return [
/*
|--------------------------------------------------------------------------
| Default Log Channel
|--------------------------------------------------------------------------
|
| This option defines the default log channel that gets used when writing
| messages to the logs. The name specified in this option should match
| one of the channels defined in the "channels" configuration array.
|
*/
'default' => env('LOG_CHANNEL', 'stack'),
/*
|--------------------------------------------------------------------------
| Deprecations Log Channel
|--------------------------------------------------------------------------
|
| This option controls the log channel that should be used to log warnings
| regarding deprecated PHP and library features. This allows you to get
| your application ready for upcoming major versions of dependencies.
|
*/
'deprecations' => [
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
'trace' => false,
],
/*
|--------------------------------------------------------------------------
| Log Channels
|--------------------------------------------------------------------------
|
| Here you may configure the log channels for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Drivers: "single", "daily", "slack", "syslog",
| "errorlog", "monolog",
| "custom", "stack"
|
*/
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single'],
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => 14,
'replace_placeholders' => true,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'Laravel Log',
'emoji' => ':boom:',
'level' => env('LOG_LEVEL', 'critical'),
'replace_placeholders' => true,
],
'papertrail' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
'handler_with' => [
'host' => env('PAPERTRAIL_URL'),
'port' => env('PAPERTRAIL_PORT'),
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
],
'processors' => [PsrLogMessageProcessor::class],
],
'stderr' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => StreamHandler::class,
'formatter' => env('LOG_STDERR_FORMATTER'),
'with' => [
'stream' => 'php://stderr',
],
'processors' => [PsrLogMessageProcessor::class],
],
'syslog' => [
'driver' => 'syslog',
'level' => env('LOG_LEVEL', 'debug'),
'facility' => LOG_USER,
'replace_placeholders' => true,
],
'errorlog' => [
'driver' => 'errorlog',
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'null' => [
'driver' => 'monolog',
'handler' => NullHandler::class,
],
'emergency' => [
'path' => storage_path('logs/laravel.log'),
],
],
'query' => [
'enabled' => env('LOG_QUERY', env('APP_ENV') === 'local'),
// Only record queries that are slower than the following time
// Unit: milliseconds
'slower_than' => 0,
// Only record queries when the QUERY_LOG_TRIGGER is set in the environment,
// or when the trigger HEADER, GET, POST, or COOKIE variable is set.
'trigger' => env('QUERY_LOG_TRIGGER'),
// Log Channel
'channel' => 'stack',
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Mailer
|--------------------------------------------------------------------------
|
| This option controls the default mailer that is used to send any email
| messages sent by your application. Alternative mailers may be setup
| and used as needed; however, this mailer will be used by default.
|
*/
'default' => env('MAIL_MAILER', 'smtp'),
/*
|--------------------------------------------------------------------------
| Mailer Configurations
|--------------------------------------------------------------------------
|
| Here you may configure all of the mailers used by your application plus
| their respective settings. Several examples have been configured for
| you and you are free to add your own as your application requires.
|
| Laravel supports a variety of mail "transport" drivers to be used while
| sending an e-mail. You will specify which one you are using for your
| mailers below. You are free to add additional mailers as required.
|
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
| "postmark", "log", "array", "failover", "roundrobin"
|
*/
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'url' => env('MAIL_URL'),
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
'port' => env('MAIL_PORT', 587),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'local_domain' => env('MAIL_EHLO_DOMAIN'),
],
'ses' => [
'transport' => 'ses',
],
'postmark' => [
'transport' => 'postmark',
// 'message_stream_id' => null,
// 'client' => [
// 'timeout' => 5,
// ],
],
'mailgun' => [
'transport' => 'mailgun',
// 'client' => [
// 'timeout' => 5,
// ],
],
'sendmail' => [
'transport' => 'sendmail',
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
],
'log' => [
'transport' => 'log',
'channel' => env('MAIL_LOG_CHANNEL'),
],
'array' => [
'transport' => 'array',
],
'failover' => [
'transport' => 'failover',
'mailers' => [
'smtp',
'log',
],
],
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
],
],
/*
|--------------------------------------------------------------------------
| Global "From" Address
|--------------------------------------------------------------------------
|
| You may wish for all e-mails sent by your application to be sent from
| the same address. Here, you may specify a name and address that is
| used globally for all e-mails that are sent by your application.
|
*/
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
/*
|--------------------------------------------------------------------------
| Markdown Mail Settings
|--------------------------------------------------------------------------
|
| If you are using Markdown based email rendering, you may configure your
| theme and component paths here, allowing you to customize the design
| of the emails. Or, you may simply stick with the Laravel defaults!
|
*/
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],
],
];
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Queue Connection Name
|--------------------------------------------------------------------------
|
| Laravel's queue API supports an assortment of back-ends via a single
| API, giving you convenient access to each back-end using the same
| syntax for every one. Here you may define a default connection.
|
*/
'default' => env('QUEUE_CONNECTION', 'sync'),
/*
|--------------------------------------------------------------------------
| Queue Connections
|--------------------------------------------------------------------------
|
| Here you may configure the connection information for each server that
| is used by your application. A default configuration has been added
| for each back-end shipped with Laravel. You are free to add more.
|
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
*/
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
'after_commit' => false,
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => 'localhost',
'queue' => 'default',
'retry_after' => 90,
'block_for' => 0,
'after_commit' => false,
],
'sqs' => [
'driver' => 'sqs',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
'queue' => env('SQS_QUEUE', 'default'),
'suffix' => env('SQS_SUFFIX'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'after_commit' => false,
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'block_for' => null,
'after_commit' => false,
],
],
/*
|--------------------------------------------------------------------------
| Job Batching
|--------------------------------------------------------------------------
|
| The following options configure the database and table that store job
| batching information. These options can be updated to any database
| connection and table which has been defined by your application.
|
*/
'batching' => [
'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'job_batches',
],
/*
|--------------------------------------------------------------------------
| Failed Queue Jobs
|--------------------------------------------------------------------------
|
| These options configure the behavior of failed queue job logging so you
| can control which database and table are used to store the jobs that
| have failed. You may change them to any database / table you wish.
|
*/
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'failed_jobs',
],
];
This diff is collapsed. Click to expand it.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment