This commit is contained in:
2023-01-11 11:00:43 +08:00
commit ff55141a1e
791 changed files with 177427 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Admin\Actions;
use Encore\Admin\Actions\RowAction;
class LinkCreateAddress extends RowAction
{
public $name = '添加收货地址';
/**
* @return string
*/
public function href(): string
{
return admin_url("mall/addresses/create?user_id=".$this->row->id);
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Admin\Actions;
use Encore\Admin\Actions\RowAction;
class LinkStockOrderDeliver extends RowAction
{
public $name = '发货';
/**
* @return string
*/
public function href(): string
{
return admin_url('mall/stock_orders?user_id='.$this->row->user->id);
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Admin\Actions;
use Encore\Admin\Actions\RowAction;
class LinkVipOrderRefund extends RowAction
{
public $name = '体验官退款';
/**
* @return string
*/
public function href(): string
{
return admin_url("platform/vip_orders?user[username]=".$this->row->user->username);
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace App\Admin\Controllers;
use Encore\Admin\Controllers\AuthController as BaseAuthController;
class AuthController extends BaseAuthController
{
}

View File

@@ -0,0 +1,259 @@
<?php
namespace App\Admin\Controllers;
use App\Http\Controllers\Controller;
use Encore\Admin\Auth\Permission;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Layout\Column;
use Encore\Admin\Layout\Content;
use Encore\Admin\Layout\Row;
use Encore\Admin\Widgets\InfoBox;
use Illuminate\Support\Facades\Artisan;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\OrderItem;
use Modules\User\Models\User;
use Modules\User\Models\UserStock;
class HomeController extends Controller
{
public $content;
/**
* Notes : 数据看板
*
* @Date : 2021/3/10 5:12 下午
* @Author : < Jason.C >
* @param Content $content
* @return Content
*/
public function index(Content $content)
{
$this->content = $content->title('数据看板')->description('Description...');
$admin = Admin::user();
if ($admin->id == 1) {
$this->getUserData();
$this->getUserStockData();
$this->getUserStockOrderData();
} else {
$this->content->row($this->setDivider('您没有权限查看数据'));
}
return $this->content;
}
/**
* Notes: 获取用户数据
*
* @Author: 玄尘
* @Date : 2021/11/17 11:24
*/
public function getUserData()
{
$this->content->row($this->setDivider('用户统计'));
$users = [
'all' => [
'name' => '用户总数',
'color' => 'blue',
'count' => User::count()
],
'ty' => [
'name' => '月卡',
'color' => 'green',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 2);
})->count()
],
'jk' => [
'name' => '季卡用户数',
'color' => 'red',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 3);
})->count()
],
'nk' => [
'name' => '年卡用户数',
'color' => 'yellow',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 4);
})->count(),
],
];
$this->content->row(function (Row $row) use ($users) {
foreach ($users as $user) {
$row->column(2, function (Column $column) use ($user) {
$column->append(new InfoBox(
$user['name'],
'users',
$user['color'],
'/admin/users',
$user['count'],
));
});
}
});
return $this->content;
}
/**
* Notes: 用户水数量
*
* @Author: 玄尘
* @Date: 2022/9/1 13:35
* @return mixed
*/
public function getUserStockData()
{
$this->content->row($this->setDivider('会员水库存数'));
$all = UserStock::query()->sum('stock');
$holds = UserStock::query()->sum('hold');
$sy = bcsub($all, $holds);
$users = [
'all' => [
'name' => '总数',
'color' => 'blue',
'count' => UserStock::query()->sum('stock')
],
'stock' => [
'name' => '已提货数',
'color' => 'green',
'count' => UserStock::query()->sum('hold')
],
'sy' => [
'name' => '待提货数',
'color' => 'green',
'count' => $sy
],
];
$this->content->row(function (Row $row) use ($users) {
foreach ($users as $user) {
$row->column(2, function (Column $column) use ($user) {
$column->append(new InfoBox(
$user['name'],
'goods',
$user['color'],
'/admin/stocks',
$user['count'],
));
});
}
});
return $this->content;
}
/**
* Notes: 提货订单数量
*
* @Author: 玄尘
* @Date: 2022/9/1 13:51
*/
public function getUserStockOrderData()
{
$this->content->row($this->setDivider('会员提货订单'));
$deliver = OrderItem::query()
->whereHas('order', function ($q) {
$q->paid();
})->sum('qty');
$deliverd = OrderItem::query()
->whereHas('order', function ($q) {
$q->whereIn('state', [
Order::STATUS_SIGNED,
Order::STATUS_DELIVERED,
]);
})->sum('qty');
$users = [
'all' => [
'name' => '订单总数',
'color' => 'blue',
'count' => Order::query()->where('type', Order::TYPE_SAMPLE)->count()
],
'deliver' => [
'name' => '待发货',
'color' => 'green',
'count' => Order::query()
->where('type', Order::TYPE_SAMPLE)
->paid()
->count()
],
'deliverd' => [
'name' => '已发货',
'color' => 'green',
'count' => Order::query()
->where('type', Order::TYPE_SAMPLE)
->whereIn('state', [
Order::STATUS_DELIVERED,
])
->count(),
],
'signed' => [
'name' => '已签收',
'color' => 'green',
'count' => Order::query()
->where('type', Order::TYPE_SAMPLE)
->whereIn('state', [
Order::STATUS_SIGNED,
])
->count()
],
];
$this->content->row(function (Row $row) use ($users) {
foreach ($users as $user) {
$row->column(2, function (Column $column) use ($user) {
$column->append(new InfoBox(
$user['name'],
'goods',
$user['color'],
'/admin/stocks',
$user['count'],
));
});
}
});
return $this->content;
}
/**
* Notes : 清理模型缓存
*
* @Date : 2021/6/8 10:51 上午
* @Author : < Jason.C >
* @return string
*/
public function cleanCache(): string
{
Artisan::call('modelCache:clear');
return '缓存清理成功';
}
public function setDivider($title)
{
return <<<HTML
<div style="height: 20px; border-bottom: 1px solid #eee; text-align: center;margin-top: 20px;margin-bottom: 20px;">
<span style="font-size: 18px; padding: 0 10px;">
{$title}
</span>
</div>
HTML;
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Admin\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Bouns;
use Modules\User\Models\Order;
class LeadyController extends Controller
{
public function index()
{
$order = Order::find(1);
Bouns::addBouns($order, $order->price);
dd(111);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Admin\Controllers\Material;
use App\Models\Material;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
class IndexController extends AdminController
{
/**
* Title for current resource.
*
* @var string
*/
protected $title = '物料/素材';
/**
* Make a grid builder.
*
* @return Grid
*/
protected function grid()
{
$grid = new Grid(new Material());
$grid->column('id', '编号');
$grid->column('title', '名称');
$grid->column('地址')->display(function () {
return $this->cover_url;
});
$grid->column('created_at', '创建时间');
return $grid;
}
/**
* Make a form builder.
*
* @return Form
*/
protected function form(): Form
{
$form = new Form(new Material());
$form->text('title', '名称');
$form->image('cover', '图片1')->move('materials/'.date('Y/m/d'));
return $form;
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace App\Admin\Controllers;
use App\Models\Module;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Grid;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Artisan;
use Nwidart\Modules\Facades\Module as ModuleManager;
class ModuleController extends AdminController
{
protected $title = '模块管理';
protected function grid(): Grid
{
$grid = new Grid(new Module());
$grid->disableBatchActions();
$grid->disableFilter();
$grid->disableCreateButton();
$grid->disablePagination();
$grid->disableActions();
$grid->column('name', '模块名称');
$grid->column('alias', '别名');
$grid->column('version', '版本');
$grid->column('author', '作者');
$grid->column('description', '模块简介');
$grid->column('enabled', '状态')->bool();
$grid->column('id', '操作')->display(function () {
if ($this->enabled) {
return sprintf('<a href="%s">%s</a>', route('admin.module.disable', $this->name), '禁用');
} else {
return sprintf('<a href="%s">%s</a>', route('admin.module.enable', $this->name), '启用');
}
});
return $grid;
}
/**
* Notes : 禁用模块
* @Date : 2021/3/11 1:13 下午
* @Author : < Jason.C >
* @param $name
* @return \Illuminate\Http\RedirectResponse
*/
public function disable($name): RedirectResponse
{
try {
$module = ModuleManager::find($name);
$module->disable();
$class = sprintf('\\%s\\%s\\%s', config('modules.namespace'), $module->getName(), $module->getName());
if (class_exists($class)) {
call_user_func([$class, 'uninstall']);
}
admin_success('Success', $name . '模块禁用成功');
} catch (\Exception $exception) {
admin_error('Error', $exception->getMessage());
}
return back();
}
/**
* Notes : 启用模块
* @Date : 2021/3/11 1:13 下午
* @Author : < Jason.C >
* @param $name
* @return \Illuminate\Http\RedirectResponse
*/
public function enable($name): RedirectResponse
{
try {
$module = ModuleManager::find($name);
$module->enable();
$class = sprintf('\\%s\\%s\\%s', config('modules.namespace'), $module->getName(), $module->getName());
if (class_exists($class)) {
call_user_func([$class, 'install']);
}
admin_success('Success', $name . '模块启用成功');
} catch (\Exception $exception) {
admin_error('Error', $exception->getMessage());
}
return back();
}
}

View File

@@ -0,0 +1,291 @@
<?php
namespace App\Admin\Controllers\Platform;
use Encore\Admin\Layout\Column;
use Encore\Admin\Layout\Content;
use Encore\Admin\Layout\Row;
use Encore\Admin\Widgets\InfoBox;
use Illuminate\Routing\Controller;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\OrderItem;
use Modules\User\Models\User;
use Modules\User\Models\UserChannel;
use Modules\User\Models\UserStock;
class DashboardController extends Controller
{
public function index(Content $content): Content
{
$this->content = $content->title('数据看板')->description('Description...');
$this->getVipUserData();
$this->getUserStockData();
// $this->getChannelData();
return $this->content;
}
/**
* Notes: 获取用户数据
*
* @Author: 玄尘
* @Date : 2021/11/17 11:24
*/
public function getVipUserData(): Content
{
$this->content->row($this->setDivider('会员统计'));
$users = [
'all' => [
'name' => '总会员量',
'color' => 'blue',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', '>', 1);
})->count()
],
'mo' => [
'name' => '月卡会员量',
'color' => 'red',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 2);
})->count()
],
'jk' => [
'name' => '季卡会员量',
'color' => 'red',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 3);
})->count()
],
'nk' => [
'name' => '年卡会员量',
'color' => 'yellow',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 4);
})->count(),
],
'onlone' => [
'name' => '在线会员数',
'color' => 'red',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', '>', 1);
})
->where('status', User::STATUS_INIT)
->count(),
],
'refund' => [
'name' => '退费会员数',
'color' => 'maroon',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', '>', 1);
})
->where('status', User::STATUS_REFUND)
->count(),
],
];
$this->content->row(function (Row $row) use ($users) {
foreach ($users as $user) {
$row->column(2, function (Column $column) use ($user) {
$column->append(new InfoBox(
$user['name'],
'users',
$user['color'],
'/admin/platform/vips',
$user['count'],
));
});
}
});
return $this->content;
}
/**
* Notes: 获取渠道数据
*
* @Author: 玄尘
* @Date: 2022/10/14 13:55
*/
public function getChannelData()
{
$this->content->row($this->setDivider('渠道数据'));
$channels = UserChannel::query()->get();
$users = [
'all' => [
'name' => '总会员量',
'color' => 'blue',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', '>', 2);
})
],
'jk' => [
'name' => '季卡会员量',
'color' => 'red',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 3);
})
],
'nk' => [
'name' => '年卡会员量',
'color' => 'yellow',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 4);
}),
],
'cs' => [
'name' => '创始会员量',
'color' => 'aqua',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 5);
}),
],
'hh' => [
'name' => '合伙人量',
'color' => 'navy',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 6);
}),
],
// 'onlone' => [
// 'name' => '在线会员数',
// 'color' => 'red',
// 'count' => User::query()
// ->whereHas('identities', function ($q) {
// $q->where('id', '>', 2);
// })
// ->where('status', User::STATUS_INIT),
// ],
'refund' => [
'name' => '退费会员数',
'color' => 'maroon',
'count' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', '>', 2);
})
->where('status', User::STATUS_REFUND),
],
];
foreach ($channels as $channel) {
$this->channel_id = $channel->id;
$this->content->row(function (Row $row) use ($channel, $users) {
foreach ($users as $user) {
$row->column(2, function (Column $column) use ($channel, $user) {
$column->append(new InfoBox(
$channel->name.'-'.$user['name'],
'users',
$user['color'],
'/admin/platform/vips?channel_id='.$channel->id,
(clone $user['count'])->where('channel_id', $channel->id)->count(),
));
});
}
});
}
return $this->content;
}
/**
* Notes: 用户水数量
*
* @Author: 玄尘
* @Date: 2022/9/1 13:35
* @return mixed
*/
public function getUserStockData()
{
$this->content->row($this->setDivider('会员库存数'));
$all = UserStock::query()->sum('stock');
$holds = UserStock::query()->sum('hold');
$sy = bcsub($all, $holds);
$users = [
'all' => [
'name' => '累计总箱数',
'color' => 'blue',
'count' => $all
],
'stock' => [
'name' => '累计提货数',
'color' => 'green',
'count' => $holds
],
'sy' => [
'name' => '累计剩余',
'color' => 'green',
'count' => $sy
],
'online' => [
'name' => '线上发货',
'color' => 'green',
'count' => OrderItem::query()
->whereHas('order', function ($q) {
$q->where('type', Order::TYPE_SAMPLE)
->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
])->where('channel', Order::CHANNEL_USER);
})
->sum('qty')
],
'offline' => [
'name' => '线下发货',
'color' => 'green',
'count' => OrderItem::query()
->whereHas('order', function ($q) {
$q->where('type', Order::TYPE_SAMPLE)
->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
])->where('channel', Order::CHANNEL_SYSTEM);
})
->sum('qty')
],
];
$this->content->row(function (Row $row) use ($users) {
foreach ($users as $user) {
$row->column(2, function (Column $column) use ($user) {
$column->append(new InfoBox(
$user['name'],
'goods',
$user['color'],
'/admin/users/stocks',
$user['count'],
));
});
}
});
return $this->content;
}
public function setDivider($title)
{
return <<<HTML
<div style="height: 20px; border-bottom: 1px solid #eee; text-align: center;margin-top: 20px;margin-bottom: 20px;">
<span style="font-size: 18px; padding: 0 10px;">
{$title}
</span>
</div>
HTML;
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace App\Admin\Controllers\Platform;
use App\Admin\Actions\LinkStockOrderDeliver;
use App\Admin\Actions\LinkVipOrderRefund;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Grid;
use Modules\Gout\Http\Controllers\Admin\Action\Audit;
use Modules\Gout\Models\GoutCase;
use Modules\Gout\Renderable\CaseData;
use Modules\Gout\Renderable\CaseSymptoms;
use Modules\User\Models\User;
use Modules\User\Models\UserStock;
class ExperienceController extends AdminController
{
protected $title = '体验官审核';
public function grid(): Grid
{
$grid = new Grid(new GoutCase());
$grid->disableCreateButton();
$grid->model()->with(['symptoms', 'user', 'user.sign', 'user.userStock'])
->withCount(['logs', 'surveys'])
->where('type', GoutCase::TYPE_TY);
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableEdit();
$actions->disableDelete();
$actions->disableView();
if ($actions->row->canAudit()) {
$actions->add(new Audit());
}
if ($actions->row->manage_status == GoutCase::MANAGE_STATUS_DELIVER) {
$actions->add(new LinkStockOrderDeliver());
}
if ($actions->row->manage_status == GoutCase::MANAGE_STATUS_REFUND) {
$actions->add(new LinkVipOrderRefund());
}
});
$grid->filter(function (Grid\Filter $filter) {
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->like('name', '姓名');
$filter->equal('mobile', '手机号');
});
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->equal('status', '状态')->select((new GoutCase())->status_map);
});
});
$grid->column('id', '#ID#');
$grid->column('name', '姓名');
$grid->column('mobile', '手机号');
$grid->column('建档信息')
->display(function ($title, $column) {
return '查看';
})->modal('建档信息', CaseData::class);
$grid->column('报告数据')
->display(function ($title, $column) {
return '查看';
})->modal('亚健康数据', CaseSymptoms::class);
$grid->column('symptoms', '症状')
->display(function () {
return $this->symptoms->pluck('title');
})->label();
$grid->column('是否关注')
->display(function () {
return $this->user->isOfficialSubscribe();
})
->bool();
//
$grid->column('缴纳保证金')
->display(function () {
return $this->user->isExperiencePrice() ? '已缴' : '待缴';
})
->label([
'已缴' => 'primary',
'待缴' => 'success',
]);
$grid->column('是否收货')
->display(function () {
return $this->user->userStock->stock_order_status_text;
})
->label(UserStock::STOCK_ORDER_STATUS_MAP);
$grid->column('is_coupon', '是否发券')
->using(GoutCase::COUPONS)
->label(GoutCase::COUPONS_MAP);
$grid->column('喝水打卡')
->display(function () {
return $this->user->sign->counts;
});
//
$grid->column('完结报告')
->display(function () {
return $this->logs_count > 1 ? '已上传' : '待上传';
})
->label([
'已上传' => 'primary',
'待上传' => 'success',
]);
//
$grid->column('是否退保')
->display(function () {
return $this->user->isExperiencePriceRefund() ? '是' : '否';
})
->label([
'是' => 'primary',
'否' => 'success',
]);
$grid->column('好转反馈')
->display(function () {
return $this->surveys_count;
});
$grid->column('manage_status', '状态')
->using((new GoutCase())->manage_status_map)
->label([
GoutCase::MANAGE_STATUS_INIT => 'primary',
GoutCase::MANAGE_STATUS_DELIVER => 'success',
GoutCase::MANAGE_STATUS_REFUND => 'danger',
GoutCase::MANAGE_STATUS_PASS => 'info',
GoutCase::MANAGE_STATUS_FINISH => 'warning',
]);
$grid->column('created_at', '创建时间');
return $grid;
}
}

View File

@@ -0,0 +1,257 @@
<?php
namespace App\Admin\Controllers\Platform;
use App\Admin\Actions\LinkCreateAddress;
use Carbon\Carbon;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\MessageBag;
use Modules\User\Http\Controllers\Admin\Actions\AddUserRemark;
use Modules\User\Http\Controllers\Admin\Actions\JoinIdentity;
use Modules\User\Http\Controllers\Admin\Actions\UserStatusInit;
use Modules\User\Http\Controllers\Admin\Actions\UserStatusRefund;
use Modules\User\Models\Identity;
use Modules\User\Models\IdentityLog;
use Modules\User\Models\IdentityMiddle;
use Modules\User\Models\User;
use Modules\User\Models\UserChannel;
use Modules\User\Renderable\UserLog;
class VipController extends AdminController
{
protected $title = '会员管理';
/**
* Notes : 用户管理列表
*
* @Date : 2021/3/11 1:59 下午
* @Author : <Jason.C>
* @return Grid
*/
public function grid(): Grid
{
$grid = new Grid(new User());
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableEdit();
$actions->disableDelete();
$actions->disableView();
if ($actions->row->status == User::STATUS_INIT) {
$actions->add(new UserStatusRefund());
}
if ($actions->row->status == User::STATUS_REFUND) {
$actions->add(new UserStatusInit());
}
$actions->add(new JoinIdentity());
$actions->add(new AddUserRemark());
$actions->add(new LinkCreateAddress());
});
$grid->quickSearch('username')->placeholder('快速搜索用户名');
$grid->filter(function (Grid\Filter $filter) {
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('username', '用户名');
$filter->equal('channel_id', '渠道')->select(UserChannel::pluck('name', 'id'));
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->like('info.nickname', '用户昵称');
});
$filter->column(1 / 3, function (Grid\Filter $filter) {
$filter->equal('identities.id', '身份')
->select(Identity::query()->where('id', '>', 1)->pluck('name', 'id'));
});
});
$grid->model()
->whereHas('identities', function ($q) {
$q->where('id', '>', 1);
})
->withCount(['addresses', 'logs'])
->with(['info', 'parent', 'identities', 'addresses', 'vipOrders', 'userStock', 'logs']);
//序号 姓名 手机号 会员类型 会员编号 缴费金额 加入时间 状态(正常,退费) 用箱数 提货箱数 剩余箱数
$grid->column('id', '序号');
$grid->column('username', '手机号');
$grid->column('identities', '会员类型')
->display(function () {
$data = [];
foreach ($this->identities as $identity) {
$data[] = $identity->name;
}
return $data;
})
->label();
$grid->column('serial', '会员编号')
->display(function () {
$data = [];
foreach ($this->identities as $identity) {
$data[] = $identity->serial_prefix.$identity->getOriginal('pivot_serial');
}
return $data;
})
->label();
$grid->column('price', '缴费金额')
->display(function () {
return $this->getOpenVipPrices();
})
->label();
$grid->column('created_at', '加入时间');
$grid->column('status', '状态')
->using(User::STATUS)
->label();
$grid->column('userStock.stock', '总箱数');
$grid->column('userStock.hold', '提货箱数');
$grid->column('userStock.residue', '剩余箱数');
$grid->column('addresses_count', '收货地址')
->link(function () {
return route('admin.mall.addresses.index', ['user_id' => $this->id]);
}, '_self');
$grid->column('logs_count', '备注')
->modal('备注信息', UserLog::class);
$grid->disableExport(false);
$grid->export(function ($export) {
$export->column('identities', function ($value, $original) {
return strip_tags($value);
});
$export->column('serial', function ($value, $original) {
return strip_tags($value);
});
$export->column('price', function ($value, $original) {
return strip_tags($value);
});
$export->column('status', function ($value, $original) {
return strip_tags($value);
});
// $export->column('use_way', function ($value, $original) {
// return strip_tags($value);
// });
// $export->column('所属用户', function ($value, $original) {
// return iconv('gb2312//ignore', 'utf-8',
// iconv('utf-8', 'gb2312//ignore', strip_tags(str_replace("&nbsp;", " ", $value))));
// });
//
// $export->column('couponGrant.code', function ($value, $original) {
// return $value."\n";
// });
$export->except(['addresses_count', 'logs_count']);
$export->filename($this->title.date("YmdHis"));
});
return $grid;
}
/**
* Notes : 编辑表单
*
* @Date : 2021/7/15 5:09 下午
* @Author : <Jason.C>
* @return Form
* @throws Exception
*/
public function form(): Form
{
// if (! config('user.create_user_by_admin')) {
// throw new Exception('不运允许操作用户');
// }
Admin::script(" $(document.body).append(`<script src='/vendor/js/setStock.js'>`); ");
$form = new Form(new User());
//姓名 手机号 身份 编号 (数字从1开始) 缴费金额 加入日期(当天) 总箱数
$form->text('info.nickname', '姓名')->required();
$form->text('username', '手机号')
->required()
->rules(['phone:CN,mobile', 'unique:users,username,{{id}}'], [
'phone' => '手机号格式错误'
]);
$form->select('join_identity_id', '加入身份')
->options(Identity::where('order', '>', 2)->pluck('name', 'id'))
->required();
$form->text('serial', '编号')->value($form->model()->getNewSerial(8))->required();
$form->number('price', '缴费金额')
->default(1)
->required();
$form->date('join_at', '加入时间')->default(now())->required();
$form->number('stock', '总箱数')
->default(0)
->setLabelClass(['identity_stock'])
->required();
$form->ignore(['join_at', 'price', 'serial', 'stock', 'join_identity_id']);
$form->saving(function (Form $form) {
$exists = IdentityMiddle::query()->where('serial', $form->serial)->first();
if ($exists) {
$error = new MessageBag([
'title' => '错误',
'message' => '编号已经存在',
]);
return back()->withInput()->with(compact('error'));
}
});
$form->saved(function (Form $form) {
$user = $form->model();
$user->update([
'created_at' => Carbon::parse(request()->jion_at)->startOfDay()
]);
$user->createOrder(request()->join_identity_id, 1, request()->price, request()->stock, [
'serial' => request()->serial,
'channel' => IdentityLog::CHANNEL_SYSTEM,
]);//创建订单
});
return $form;
}
/**
* Notes : User 列表选择, 这里没有判断,用户是否已经有店铺了,如果判断的情况,可能导致当前用户 无法被选中
*
* @Date : 2021/5/6 4:35 下午
* @Author : <Jason.C>
*/
public function ajax(Request $request)
{
$q = $request->get('q');
return User::leftJoin('user_infos as info', 'users.id', '=', 'info.user_id')
->where('username', 'like', "%$q%")
->orWhere('info.nickname', 'like', "%$q%")
->select('id', DB::raw('CONCAT(username, " [", info.nickname, "]") as text'))
->paginate();
}
/**
* Notes: 获取库存
*
* @Author: 玄尘
* @Date: 2022/9/7 15:23
* @param Request $request
* @return mixed
*/
public function stock(Request $request)
{
$q = $request->get('q');
$identity = Identity::find($q);
return ['status_code' => 200, 'value' => $identity->stock];
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace App\Admin\Controllers\Platform;
use Encore\Admin\Controllers\AdminController;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Exception;
use Modules\User\Http\Controllers\Admin\Actions\Pay;
use Modules\User\Http\Controllers\Admin\Actions\Refund;
use Modules\User\Models\Identity;
use Modules\User\Models\Order;
class VipOrderController extends AdminController
{
protected $title = '体验官退款';
/**
* Notes: 升级订单
*
* @Author: 玄尘
* @Date : 2021/6/7 15:19
* @return Grid
*/
public function grid(): Grid
{
try {
trait_exists('Modules\Payment\Traits\WithPayments');
$grid = new Grid(new Order());
$grid->model()
->whereHas('identity', function ($q) {
$q->ty();
})
->whereIn('state',[Order::STATE_SUCCESS,Order::STATE_REFUND])
->latest();
$grid->disableCreateButton();
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableEdit();
$actions->disableDelete();
$actions->disableView();
if ($actions->row->canRefund()) {
$actions->add(new Refund());
}
});
$grid->filter(function (Grid\Filter $filter) {
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->like('user.username', '用户名');
});
$filter->column(1 / 2, function (Grid\Filter $filter) {
$filter->equal('state', '状态')->select(Order::STATES);
});
});
$grid->column('id', '用户ID');
$grid->column('升级用户')->display(function () {
return $this->user->username."({$this->user->info->nickname})";
});
$grid->column('identity.name', '身份');
$grid->column('price', '金额');
$grid->column('state', '状态')->using(Order::STATES)->label();
$grid->column('type', '类型')->using(Order::TYPES)->label();
$grid->column('created_at', '升级时间');
return $grid;
} catch (Exception $exception) {
dd('Payment 模块不存在,无法加载订单数据');
}
}
public function form(): Form
{
$form = new Form(new Order());
$form->decimal('price', '金额')->required();
return $form;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace App\Admin\Controllers;
use App\Bonus\IdentityBonus;
use App\Http\Controllers\Controller;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Liuhelong\LaravelAdmin\Wechat\Models\WechatOffiaccountUser;
use Modules\Coupon\Models\Coupon;
use Modules\Coupon\Traits\WithCoupon;
use Modules\User\Models\Identity;
use Modules\User\Models\Order;
use Modules\User\Models\UserInvite;
use Modules\User\Models\UserStockLog;
use Modules\User\Models\UserWechat;
use Modules\User\Models\UserWechatOfficial;
use Modules\User\Traits\RankDataTrait;
use Modules\User\Traits\WechatTrait;
use Vinkla\Hashids\Facades\Hashids;
class TestController extends Controller
{
use WithCoupon, WechatTrait, RankDataTrait;
public function index()
{
$identities= Identity::all();
dd($identities->toArray());
}
/**
* Notes: 清空数据表的操作,为了测试用的
*
* @Author: <C.Jason>
* @Date : 2020/11/23 4:54 下午
*/
public function truncate(Request $request)
{
$name = $request->name;
$is_test = config('app.is_test');
dump('is_test'.$is_test);
if ($name != 'skyxu') {
dd('name不对');
}
if (! $is_test) {
dd('不是测试环境');
}
$tables = [
'admin_operation_log',
'bouns',
'bouns_orders',
'bouns_user_perves',
'coupon_grants',
'coupon_item_use_logs',
'coupon_use_logs',
'failed_jobs',
'gout_case_log_symptoms',
'gout_case_logs',
'gout_case_timelines',
'gout_case_symptom',
'gout_cases',
'gout_surveys',
'gout_votes',
'jobs',
'linker_relations',
'linkers',
'mall_addresses',
'mall_order_expresses',
'mall_order_items',
'mall_orders',
'mall_carts',
'mall_refund_items',
'mall_refund_logs',
'mall_refunds',
'mall_refund_expresses',
'notifications',
'payment_refunds',
'payments',
'user_account_logs',
'user_accounts',
'user_identity',
'user_identity_logs',
'user_identity_coupons',
'user_infos',
'user_invites',
'user_logs',
'user_orders',
'user_perves',
'user_relations',
'user_sign_logs',
'user_signs',
'user_sms',
'user_stock_logs',
'user_stocks',
'user_wechat_apps',
'user_wechat_minis',
'user_wechat_officials',
'user_wechats',
'users',
'versions',
'wechat_offiaccount_event_logs',
'withdraw_alipay_accounts',
'withdraw_bank_accounts',
'withdraw_logs',
'withdraws',
'personal_access_tokens',
];
foreach ($tables as $table) {
DB::table($table)->truncate();
}
dd('清理成功');
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Admin\Extensions;
class CleanCache
{
public function __toString()
{
$url = route('admin.cleanCache');
return <<<HTML
<li data-toggle="tooltip" data-placement="bottom" title="清除缓存">
<a href="javascript:void(0);" class="clean-cache">
<i class="fa fa-trash-o"></i>
</a>
</li>
<script>
$('.clean-cache').click(function () {
$.get('{$url}', function (data) {
toastr.success(data)
})
})
</script>
HTML;
}
}

View File

@@ -0,0 +1,10 @@
<?php
use Illuminate\Routing\Router;
Route::group([
'namespace' => 'Material',
], function (Router $router) {
$router->resource('materials', 'IndexController');
});

View File

@@ -0,0 +1,12 @@
<?php
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Route;
Route::group([
'prefix' => 'modules',
], function (Router $router) {
$router->get('', 'ModuleController@index');
$router->get('{name}/disable', 'ModuleController@disable')->name('module.disable');
$router->get('{name}/enable', 'ModuleController@enable')->name('module.enable');
});

View File

@@ -0,0 +1,15 @@
<?php
use Illuminate\Routing\Router;
Route::group([
'namespace' => 'Platform',
'prefix' => 'platform',
], function (Router $router) {
$router->resource('experiences', 'ExperienceController');//体验官审核
$router->resource('vip_orders', 'VipOrderController');//体验官审核
$router->resource('vips', 'VipController');//会员管理
$router->get('identity_stock', 'VipController@stock')->name('platform.identity_stock');;//会员管理
$router->get('dashboard', 'DashboardController@index');
});

12
app/Admin/Routes/test.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Route;
Route::group([
'prefix' => 'test',
], function (Router $router) {
$router->get('', 'TestController@index');
$router->get('truncate', 'TestController@truncate');
$router->get('leady', 'LeadyController@index');
});

View File

@@ -0,0 +1,103 @@
<?php
namespace App\Admin\Traits;
use Illuminate\Contracts\Support\Renderable;
trait WithUploads
{
/**
* Notes : 单张封面图上传
*
* @Date : 2021/4/25 2:06 下午
* @Author : < Jason.C >
* @param \Encore\Admin\Form $form
* @param string $filed
* @param string $label
*/
public function cover(Renderable $form, string $filed = 'cover', string $label = '封面图片')
{
$cover = $form->image($filed, $label)
->move('images/'.date('Y/m/d'))
->uniqueName()
->removable()
->retainable();
$waterConfig = config('admin.image_water');
if (! empty($waterConfig)) {
$cover->insert(...$waterConfig);
}
$coverThumb = config('admin.cover_thumb');
if (! empty($coverThumb)) {
$cover->thumbnail($coverThumb);
}
}
/**
* Notes: 上传视频
*
* @Author: 玄尘
* @Date: 2022/9/30 9:44
* @param Renderable $form
* @param string $filed
* @param string $label
*/
public function video(Renderable $form, string $filed = 'path', string $label = '视频')
{
$form->file($filed, $label)
->move('videos/'.date('Y/m/d'))
->uniqueName()
->removable()
->downloadable()
->retainable();
}
/**
* Notes : 统一的多图上传
*
* @Date : 2021/4/25 2:06 下午
* @Author : < Jason.C >
* @param \Encore\Admin\Form $form
* @param string $filed
* @param string $label
*/
public function pictures(Renderable $form, string $filed = 'pictures', string $label = '多图轮播')
{
$pictures = $form->multipleImage($filed, $label)
->move('images/'.date('Y/m/d'))
->uniqueName()
->removable()
->retainable();
// 多图如果开启排序的话,会报错,暂时没由解决办法 ->sortable()
$waterConfig = config('admin.image_water');
if (! empty($waterConfig)) {
$pictures->insert(...$waterConfig);
}
}
/**
* Notes : 统一的附件上传
*
* @Date : 2021/4/25 3:03 下午
* @Author : < Jason.C >
* @param \Illuminate\Contracts\Support\Renderable $form
* @param string $filed
* @param string $label
*/
public function attachments(Renderable $form, string $filed = 'attachments', string $label = '内容附件')
{
$form->multipleFile($filed, $label)
->move('attachments/'.date('Y/m/d'))
->uniqueName()
->removable()
->retainable()
->sortable();
}
}

62
app/Admin/bootstrap.php Normal file
View File

@@ -0,0 +1,62 @@
<?php
/**
* Laravel-admin - admin builder based on Laravel.
* @author z-song <https://github.com/z-song>
* Bootstraper for Admin.
* Here you can remove builtin form field:
* Encore\Admin\Form::forget(['map', 'editor']);
* Or extend custom form field:
* Encore\Admin\Form::extend('php', PHPEditor::class);
* Or require js and css assets:
* Admin::css('/packages/prettydocs/css/styles.css');
* Admin::js('/packages/prettydocs/js/main.js');
*/
use App\Admin\Extensions\CleanCache;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use Encore\Admin\Show;
use Encore\Admin\Widgets\Navbar;
Form::forget(['map', 'editor']);
Admin::navbar(function (Navbar $navbar) {
$navbar->right(new CleanCache());
$navbar->right(new Navbar\Fullscreen());
});
Form::init(function (Form $form) {
$form->disableEditingCheck();
$form->disableCreatingCheck();
$form->disableViewCheck();
$form->tools(function (Form\Tools $tools) {
$tools->disableView();
// $tools->disableDelete();
// $tools->disableList();
});
});
Show::init(function (Show $show) {
$show->panel()
->tools(function ($tools) {
// $tools->disableEdit();
// $tools->disableList();
$tools->disableDelete();
});;
});
Grid::init(function (Grid $grid) {
$grid->disableExport();
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableView();
});
$grid->disableBatchActions();
$grid->filter(function ($filter) {
$filter->disableIdFilter();
});
// $grid->expandFilter();
});

24
app/Admin/routes.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
use Encore\Admin\Facades\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'),
'as' => config('admin.route.as'),
], function (Router $router) {
$router->get('/', 'HomeController@index')->name('home');
$router->get('clean_cache', 'HomeController@cleanCache')->name('cleanCache');
foreach (glob(admin_path('Routes') . '/*.php') as $routeFile) {
require $routeFile;
}
});

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Api\Controllers;
use Jason\Api\Traits\ApiResponse;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use ApiResponse;
}

View File

@@ -0,0 +1,159 @@
<?php
namespace App\Api\Controllers\Data;
use App\Api\Controllers\Controller;
use App\Api\Resources\Order\OrderDataCollection;
use App\Api\Resources\User\UserDataCollection;
use App\Api\Resources\User\UserDataResource;
use Illuminate\Http\Request;
use Modules\Coupon\Http\Resources\User\UserBaseResource;
use Modules\Mall\Models\Order;
use Modules\User\Models\User;
class IndexController extends Controller
{
public function index()
{
$all = 1000;
$sold = Order::query()
->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
Order::STATUS_COMPLETED,
])
->count();
$donate = (new User())->getALlJzCount();
$orderQuery = Order::query()->where('type', Order::TYPE_NORMAL);
/**
*
*/
$amount = (clone $orderQuery)->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
Order::STATUS_COMPLETED,
])->sum('amount');
$data = [
'activities' => [
'all' => $all,
'donate' => $donate,
'residue' => bcsub($all, $donate),
],
'orders' => [
'users' => User::query()->whereHas('orders', function ($q) {
$q->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
Order::STATUS_COMPLETED,
])->where('type', Order::TYPE_NORMAL);
})->count(),
'all' => (clone $orderQuery)
->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
Order::STATUS_COMPLETED,
])->count(),
'paid' => (clone $orderQuery)->where('state', Order::STATUS_PAID)->count(),
'delivered' => (clone $orderQuery)->where('state', Order::STATUS_DELIVERED)->count(),
'signed' => (clone $orderQuery)->where('state', Order::STATUS_SIGNED)->count(),
'completed' => (clone $orderQuery)->where('state', Order::STATUS_COMPLETED)->count(),
'amount' => floatval($amount),
],
'users' => [
'all' => User::count(),
'yk' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 2);
})->count(),
'jk' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 3);
})->count(),
'nk' => User::query()
->whereHas('identities', function ($q) {
$q->where('id', 4);
})->count(),
]
];
return $this->success($data);
}
/**
* Notes: description
*
* @Author: 玄尘
* @Date: 2023/1/5 13:50
*/
public function orders(Request $request)
{
$status = $request->status ?? 'all';
$orders = Order::query()
->where('type', Order::TYPE_NORMAL)
->when($status !== 'all', function ($q) use ($status) {
$q->where('state', $status);
}, function ($q) {
$q->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
Order::STATUS_COMPLETED,
]);
})
->paginate();
return $this->success(new OrderDataCollection($orders));
}
/**
* Notes: description
*
* @Author: 玄尘
* @Date: 2023/1/5 14:13
*/
public function users()
{
$users = User::query()
->withCount([
'orders' => function ($q) {
$q->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
Order::STATUS_COMPLETED,
]);
}
])
->withSum([
'orders' => function ($q) {
$q->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
Order::STATUS_COMPLETED,
]);
}
], 'amount')
->whereHas('orders', function ($q) {
$q->whereIn('state', [
Order::STATUS_PAID,
Order::STATUS_DELIVERED,
Order::STATUS_SIGNED,
Order::STATUS_COMPLETED,
])->where('type', Order::TYPE_NORMAL);
})
->paginate();
return $this->success(new UserDataCollection($users));
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Api\Controllers;
class IndexController extends Controller
{
public function index()
{
return $this->success('Json Api is ready');
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Api\Controllers\Wechat;
use App\Api\WechatHandlers\EventMessageHandler;
use App\Api\WechatHandlers\FileMessageHandler;
use App\Api\WechatHandlers\ImageMessageHandler;
use App\Api\WechatHandlers\LinkMessageHandler;
use App\Api\WechatHandlers\LocationMessageHandler;
use App\Api\WechatHandlers\ShortVideoMessageHandler;
use App\Api\WechatHandlers\TextMessageHandler;
use App\Api\WechatHandlers\TransferMessageHandler;
use App\Api\WechatHandlers\VideoMessageHandler;
use App\Api\WechatHandlers\VoiceMessageHandler;
use EasyWeChat\Kernel\Messages\Message;
use Overtrue\LaravelWeChat\Controllers\Controller;
class IndexController extends Controller
{
public function serve()
{
$app = app('wechat.official_account');
$app->server->push(function ($message) {
switch ($message['MsgType']) {
case 'event':
return '收到事件消息';
break;
case 'text':
return '收到文字消息';
break;
case 'image':
return '收到图片消息';
break;
case 'voice':
return '收到语音消息';
break;
case 'video':
return '收到视频消息';
break;
case 'location':
return '收到坐标消息';
break;
case 'link':
return '收到链接消息';
break;
case 'file':
return '收到文件消息';
// ... 其它消息
default:
return '收到其它消息';
break;
}
// ...
});
$app->server->push(EventMessageHandler::class, Message::EVENT);
return $app->server->serve();
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Api\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class BaseCollection extends ResourceCollection
{
protected function page(): array
{
return [
'current' => $this->currentPage(),
'total_page' => $this->lastPage(),
'per_page' => $this->perPage(),
'has_more' => $this->hasMorePages(),
'total' => $this->total(),
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Api\Resources\Order;
use App\Api\Resources\BaseCollection;
use App\Api\Resources\User\UserBaseResource;
use Modules\Mall\Http\Resources\Api\Order\OrderExpressResource;
class OrderDataCollection extends BaseCollection
{
public function toArray($request): array
{
return [
'data' => $this->collection->map(function ($order) {
return [
'order_no' => $order->order_no,
'user' => new UserBaseResource($order->user),
'express' => new OrderExpressResource($order->express),
'qty' => $order->items()->sum('qty'),
'amount' => $order->amount,
'freight' => $order->freight,
'total' => $order->total,
'type' => $order->type_text,
'state' => $order->state_text,
];
}),
'page' => $this->page(),
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Api\Resources\User;
use Illuminate\Http\Resources\Json\JsonResource;
class UserBaseResource extends JsonResource
{
public function toArray($request): array
{
return [
'user_id' => $this->id,
'username' => $this->username,
'nickname' => $this->info->nickname,
'avatar' => $this->info->avatar ?? '',
];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Api\Resources\User;
use App\Api\Resources\BaseCollection;
class UserDataCollection extends BaseCollection
{
public function toArray($request): array
{
return [
'data' => $this->collection->map(function ($info) {
return new UserDataResource($info);
}),
'page' => $this->page(),
];
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Api\Resources\User;
use Illuminate\Http\Resources\Json\JsonResource;
class UserDataResource extends JsonResource
{
public function toArray($request): array
{
return [
'user_id' => $this->id,
'username' => $this->username,
'nickname' => $this->info->nickname,
'orders_count' => $this->orders_count,
'orders_sum_amount' => $this->orders_sum_amount,
'avatar' => $this->info->avatar ?? '',
];
}
}

14
app/Api/Routes/data.php Normal file
View File

@@ -0,0 +1,14 @@
<?php
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Route;
Route::group([
'namespace' => 'Data',
'middleware' => config('api.route.middleware'),
], function (Router $router) {
$router->get('data', 'IndexController@index');
$router->get('data/orders', 'IndexController@orders');
$router->get('data/users', 'IndexController@users');
});

12
app/Api/Routes/wechat.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Route;
Route::group([
'namespace' => 'Wechat',
'middleware' => config('api.route.middleware'),
], function (Router $router) {
$router->any('wechat', 'IndexController@serve')->name('wechat');
});

View File

@@ -0,0 +1,142 @@
<?php
namespace App\Api\WechatHandlers;
use Carbon\Carbon;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
use EasyWeChat\Kernel\Messages\Text;
use Modules\User\Models\UserWechat;
use Modules\User\Models\UserWechatOfficial;
use Modules\User\Traits\WechatTrait;
class EventMessageHandler implements EventHandlerInterface
{
use WechatTrait;
private $payload;
public function handle($payload = null)
{
if (method_exists($this, $payload->Event)) {
$this->payload = $payload;
return call_user_func_array([$this, $payload->Event], []);
} else {
// return '暂不支持的消息类型';
}
}
/**
* Notes: 关注事件
*
* @Author: 玄尘
* @Date: 2022/7/27 9:41
*/
private function subscribe()
{
$app = app('wechat.official_account');
$user = $app->user->get($this->payload->FromUserName);
if ($user->unionid) {
$wechatUsers = UserWechat::query()
->where('unionid', $user->unionid)
->get();
if ($wechatUsers->isNotEmpty()) {
foreach ($wechatUsers as $wechatUser) {
if ($wechatUser->official) {
if ($wechatUser->official->subscribe != 1) {
$wechatUser->official->update([
'subscribe' => 1,
'subscribed_at' => Carbon::parse($user->subscribe_time)->toDateTimeString(),
]);
}
} else {
$wechatUser->official()->create([
'openid' => $this->payload->FromUserName,
'subscribe' => 1,
'subscribed_at' => Carbon::parse($user->subscribe_time)->toDateTimeString(),
]);
}
}
}
//插入关注数据
$this->setUserSubscribe($user->unionid, $this->payload->FromUserName, 1);
} else {
// 先查找用户是否存在,不存在再注册
$officialUsers = UserWechatOfficial::query()
->where('openid', $this->payload->FromUserName)
->get();
//设置总表uniond
if ($officialUsers->isNotEmpty()) {
foreach ($officialUsers as $officialUser) {
if (! $officialUser->userWechat->unionid && $user->unionid) {
$officialUser->userWechat->update([
'unionid' => $user->unionid
]);
}
if ($officialUser->subscribe != 1) {
$officialUser->update([
'subscribe' => 1,
'subscribed_at' => Carbon::parse($user->subscribe_time)->toDateTimeString(),
]);
}
}
}
}
return $this->firstSubscribeMessage();
}
/**
* Notes: 取消关注事件
*
* @Author: 玄尘
* @Date: 2022/7/27 9:41
*/
private function unsubscribe()
{
$officialUsers = UserWechatOfficial::where('openid', $this->payload->FromUserName)->get();
if ($officialUsers->isNotEmpty()) {
foreach ($officialUsers as $officialUser) {
$officialUser->update([
'subscribe' => 0,
'subscribed_at' => null,
]);
//设置取消关注
if ($officialUser->userWechat) {
$this->setUserSubscribe($officialUser->userWechat->unionid, $officialUser->openid, 0);
}
}
}
}
/**
* Notes: 关注返回的消息
*
* @Author: 玄尘
* @Date: 2022/8/2 11:37
* @return Text
*/
private function firstSubscribeMessage()
{
$officialUser = UserWechatOfficial::where('openid', $this->payload->FromUserName)->first();
$text = new Text('扎西德勒感谢关注锶源昆仑。锶源昆仑天然饮用水水源地位于海拔4300米的昆仑山水中富含镁离子、锶元素、钙元素等微量元素且氘含量低稀世罕见仅供30000人饮用。
参与打卡“锶享体验官”活动,请点击公众号菜单上的按钮“锶享体验官”进入活动,来申请喝水体验官吧!');
if ($officialUser && $officialUser->userWechat) {
$nickname = $officialUser->userWechat->nickname;
$text = new Text('扎西德勒!'.$nickname.'感谢关注锶源昆仑。锶源昆仑天然饮用水水源地位于海拔4300米的昆仑山水中富含镁离子、锶元素、钙元素等微量元素且氘含量低稀世罕见仅供30000人饮用。
参与打卡“锶享体验官”活动,请点击公众号菜单上的按钮“锶享体验官”进入活动,来申请喝水体验官吧!');
}
return $text;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Api\WechatHandlers;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
class FileMessageHandler implements EventHandlerInterface
{
public function handle($payload = null)
{
$payload->ToUserName;
$payload->FromUserName;
$payload->CreateTime;
$payload->MsgId;
$payload->Title;
$payload->Description;
$payload->FileKey;
$payload->FileMd5;
$payload->FileTotalLen;
return '文件消息';
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Api\WechatHandlers;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
class ImageMessageHandler implements EventHandlerInterface
{
public function handle($payload = null)
{
// $payload->ToUserName;
// $payload->FromUserName;
// $payload->CreateTime;
// $payload->MsgId;
//
// $payload->MediaId;
// $payload->PicUrl;
return '图片消息';
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Api\WechatHandlers;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
class LinkMessageHandler implements EventHandlerInterface
{
public function handle($payload = null)
{
$payload->ToUserName;
$payload->FromUserName;
$payload->CreateTime;
$payload->MsgId;
$payload->Title;
$payload->Description;
$payload->Url;
return '链接消息';
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Api\WechatHandlers;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
class LocationMessageHandler implements EventHandlerInterface
{
public function handle($payload = null)
{
$payload->ToUserName;
$payload->FromUserName;
$payload->CreateTime;
$payload->MsgId;
$payload->Latitude;
$payload->Longitude;
$payload->Precision;
return '上报位置消息';
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Api\WechatHandlers;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
class ShortVideoMessageHandler implements EventHandlerInterface
{
public function handle($payload = null)
{
$payload->ToUserName;
$payload->FromUserName;
$payload->CreateTime;
$payload->MsgId;
$payload->MediaId;
$payload->ThumbMediaId;
return '短视频消息';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Api\WechatHandlers;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
use function AlibabaCloud\Client\json;
class TextMessageHandler implements EventHandlerInterface
{
public function handle($payload = null)
{
return '您的留言已经收到';
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Api\WechatHandlers;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
class TransferMessageHandler implements EventHandlerInterface
{
public function handle($payload = null)
{
return '客服消息'.json_encode($payload);
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Api\WechatHandlers;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
class VideoMessageHandler implements EventHandlerInterface
{
public function handle($payload = null)
{
$payload->ToUserName;
$payload->FromUserName;
$payload->CreateTime;
$payload->MsgId;
$payload->MediaId;
$payload->ThumbMediaId;
return '视频消息';
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Api\WechatHandlers;
use EasyWeChat\Kernel\Contracts\EventHandlerInterface;
class VoiceMessageHandler implements EventHandlerInterface
{
public function handle($payload = null)
{
$payload->ToUserName;
$payload->FromUserName;
$payload->CreateTime;
$payload->MsgId;
$payload->MediaId;
$payload->Format;
$payload->Recognition;
return '语音消息';
}
}

1
app/Api/bootstrap.php Normal file
View File

@@ -0,0 +1 @@
<?php

25
app/Api/routes.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Route;
//Route::get('/', 'IndexController@index')->name('home');
/**
* 分组的路由示例
*/
Route::group([
// 'as' => '',
// 'domain' => '',
// 'middleware' => '',
// 'namespace' => '',
// 'prefix' => '',
], function (Router $router) {
$router->get('/', 'IndexController@index')->name('home');
});
/**
* 文件夹引入的示例
*/
foreach (glob(app_path('Api/Routes').'/*.php') as $routeFile) {
require $routeFile;
}

305
app/Bonus/IdentityBonus.php Normal file
View File

@@ -0,0 +1,305 @@
<?php
namespace App\Bonus;
use Carbon\Carbon;
use Modules\Coupon\Models\Coupon;
use Modules\Coupon\Models\CouponGrant;
use Modules\User\Models\Identity;
use Modules\User\Models\Order;
use Modules\User\Models\User;
class IdentityBonus
{
/**
* 开通身份
*
* @param User $user
* @param Order $order
* @param array $source
* @return bool|string
*/
public static function BuyIdentity(User $user, Order $order, array $source = [])
{
dd(1);
try {
$identity = $order->identity;
$parent = $user->parent;
$parentIdentity = $parent ? $user->parent->identityFirst() : '';
//开通赠送水滴
$crystal = $order->identity->getRule('give_crystal', '0');
if ($crystal > 0) {
$user->account->rule('open_identity_score', $crystal, true, array_merge($source, [
'user_order_id' => $order->id
]));
}
//发券
self::grantCoupon($user, $order, $parent, $parentIdentity);
//是否有上级
if ($parent && $parentIdentity && $order->price > 0) {
//增加业绩
$parent->addPerf($order->price, $order);
//开通的是季卡
if ($identity->job == Identity::JOB_JK) {
//直推季卡获得水滴
$hasRecommend_jk_score = $parentIdentity->getRule('recommend_jk_score', '');
if ($hasRecommend_jk_score) {
$user->parent->account->rule(
'recommend_jk_score',
$hasRecommend_jk_score,
false,
array_merge($source, [
'user_order_id' => $order->id
])
);
}
//合伙人推荐奖励
self::setHhRecommendJk($hasRecommend_jk_score, $user, $order, $source);
}
//开通的是年卡
if ($identity->job == Identity::JOB_NK) {
//直推年卡获得水滴
$hasRecommend_nk_score = $parentIdentity->getRule('recommend_nk_score', '');
if ($hasRecommend_nk_score) {
$user->parent->account->rule(
'recommend_nk_score',
$hasRecommend_nk_score,
false,
array_merge($source, [
'user_order_id' => $order->id
])
);
}
//合伙人推荐奖励
self::setHhRecommendNk($hasRecommend_nk_score, $user, $order, $source);
}
}
return true;
} catch (\Exception $exception) {
return $exception->getMessage();
}
}
/**
* Notes: 发券
* 推荐会员得优惠券 季卡 年卡 创始
*
* @Author: 玄尘
* @Date: 2022/8/31 13:53
*/
public static function grantCoupon($user, $order, $parent, $parentIdentity)
{
if ($parent && $parentIdentity->job !== Identity::JOB_HH) {
$jkIdentity = Identity::query()->Jk()->first();
$openJkCount = $parent->getOpenJkCount();//开通季卡次数
$recommendUserCoupunCount = $parent->getRecommendUserCouponCount();//推荐季卡得优惠券数
$recommend_coupon = $parentIdentity->getRule('recommend_coupon', null);//是否有推荐奖励
//推荐季卡得优惠券
if ($recommend_coupon && $order->type == Order::TYPE_OPEN && $openJkCount > $recommendUserCoupunCount) {
$coupon = Coupon::query()
->whereHas('items', function ($q) use ($jkIdentity) {
$q->withGoods($jkIdentity);
})
->first();
$ended_at = Carbon::parse($parentIdentity->pivot->ended_at);
if ($coupon && $ended_at->gt(now())) {
$days = $ended_at->addMonth()->diffInDays(now());
#TODO 推季卡会员得优惠券
// $user->parent->getCoupon($coupon->id, CouponGrant::CHANNEL_EXPAND, $days);
$user->parent->getCoupon($coupon->id, CouponGrant::CHANNEL_EXPAND);
}
}
}
}
/**
* Notes: 合伙人推荐季卡
*
* @Author: 玄尘
* @Date: 2022/8/19 15:11
*/
public static function setHhRecommendJk($score, $user, $order, $source)
{
$hhUsers = $user->getParentHh();//获取所有合伙人
$hhIdentity = Identity::query()->where('job', Identity::JOB_HH)->first();
$rule_name = 'recommend_indirect_jk_balance';
if ($hhIdentity && ! empty($hhUsers)) {
//有人获得了推荐水滴就是间推
$i = 1;
foreach ($hhUsers as $hhUser) {
$jkChildren = $hhUser->getJkChildrenCount();
$amount = 0;
if ($i > 1) {
break;
}
//间接
if ($score) {
if ($jkChildren > 10) {
$rateName = 'recommend_rate_indirect_jk_balance_gt';
} else {
$rateName = 'recommend_rate_indirect_jk_balance_lte';
}
} else {//直接
if ($jkChildren > 10) {
$rateName = 'recommend_rate_jk_balance_gt';
} else {
$rateName = 'recommend_rate_jk_balance_lte';
}
$rule_name = 'recommend_jk_balance';
}
$hasRate = $hhIdentity->getRule($rateName, 0);//获取比例
$rateData = self::getNonZeroRate($hasRate);
$amount = bcmul($order->price, $rateData, 2);
if ($amount) {
//执行分润
$hhUser->account->rule(
$rule_name,
$amount,
false,
array_merge($source, [
'user_order_id' => $order->id,
'rate' => $rateData
])
);
//培育津贴
self::allowance($hhUser, $amount, $source);
}
$i++;
}
} else {
info('没有找到合伙人');
}
}
/**
* Notes: 合伙人间推季卡
*
* @Author: 玄尘
* @Date: 2022/8/19 15:13
*/
public static function setHhRecommendNk($score, $user, $order, $source)
{
$hhUsers = $user->getParentHh();//获取所有合伙人
$hhIdentity = Identity::query()->where('job', Identity::JOB_HH)->first();
$rule_name = 'recommend_indirect_nk_balance';
if ($hhIdentity && ! empty($hhUsers)) {
//有人获得了推荐水滴就是间推
$i = 1;
foreach ($hhUsers as $hhUser) {
$nkChildren = $hhUser->getNkChildrenCount();
$amount = 0;
if ($i > 1) {
break;
}
//间接
if ($score) {
if ($nkChildren > 10) {
$rateName = 'recommend_rate_indirect_nk_balance_gt';
} else {
$rateName = 'recommend_rate_indirect_nk_balance_lte';
}
} else {//直接
if ($nkChildren > 10) {
$rateName = 'recommend_rate_nk_balance_gt';
} else {
$rateName = 'recommend_rate_nk_balance_lte';
}
$rule_name = 'recommend_nk_balance';
}
$hasRate = $hhIdentity->getRule($rateName, 0);//获取比例
$rateData = self::getNonZeroRate($hasRate);
$amount = bcmul($order->price, $rateData, 2);
if ($amount > 0) {
//执行分润
$hhUser->account->rule(
$rule_name,
$amount,
false,
array_merge($source, [
'user_order_id' => $order->id,
'rate' => $rateData
])
);
//培育津贴
self::allowance($hhUser, $amount, $source);
}
$i++;
}
}
}
/**
* Notes: 获取百分比数据
*
* @Author: 玄尘
* @Date: 2022/8/19 14:56
* @param $rate
* @param $decimals
* @return int|string|null
*/
public static function getNonZeroRate($rate, $decimals = 2)
{
return $rate > 0 ? bcdiv($rate, 100, $decimals) : 0;
}
/**
* Notes: 培育津贴
*
* @Author: 玄尘
* @Date: 2022/8/30 14:32
*/
public static function allowance(User $user, $amount, $source)
{
$UserIdentity = $user->identityFirst();
$hasRate = $UserIdentity->getRule('recommend_rate_allowance', 0);//获取培育津贴比例
if ($UserIdentity->job == Identity::JOB_HH && $hasRate) {
$hhUsers = $user->getParentHh();//获取所有合伙人
$rateData = self::getNonZeroRate($hasRate);
$amount = bcmul($amount, $rateData, 2);
$i = 1;
if (! empty($hhUsers)) {
foreach ($hhUsers as $hhUser) {
if ($i > 1) {
break;
}
$hhUser->account->rule(
'allowance_balance',
$amount,
false,
$source
);
$i++;
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Channels;
use Illuminate\Notifications\Notification;
class WechatMiniChannel
{
public function send($notifiable, Notification $notification)
{
$notification->toWeChat($notifiable);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Console\Commands;
use App\Jobs\Bonus\MonthPerfJob;
use Illuminate\Console\Command;
use Modules\User\Models\User;
class MonthPerfCommand extends Command
{
protected $signature = 'Bonus:MonthPerf {last?}';
protected $description = '分红:计算用户月度业绩';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$last = $this->argument('last') ?: 0;
User::whereHas('identities', function ($query) {
$query->where('id', 6);
})
->whereHas('identityMiddle', function ($query) {
$query->where('star', '>', 0);
})
->chunkById(100, function ($users) use ($last) {
foreach ($users as $user) {
MonthPerfJob::dispatch($user, $last);
}
});
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace App\Console\Commands;
use App\Jobs\Bonus\SendBounsJob;
use App\Models\Bouns;
use App\Models\BounsUserPerf;
use Illuminate\Console\Command;
use Exception;
class StartBounsCommand extends Command
{
protected $signature = 'Bonus:StartMonth {star} {last?}';
protected $description = '分红:开始分红 {1-5} {last?}';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$star = $this->argument('star') ?: 0;
if (!in_array($star, Bouns::TYPEARRAY)) {
throw new Exception('星级参数不正确');
}
$last = $this->argument('last') ?: 0;
$time = now()->startOfMonth()->toDateTimeString();
if ($last) {
$time = now()->subMonth()->startOfMonth()->toDateTimeString();
}
$bouns = Bouns::where('date', $time)
->where('type', $star)
->where('status', Bouns::STATUS_INIT)
->first();
if (!$bouns) {
throw new Exception('分红内容不存在或状态不正确');
}
$bounsUserPerf = BounsUserPerf::where('date', $bouns->date)
->where('star', '>=', $bouns->type)
->get();
if ($bounsUserPerf->count() > 0) {
$allOld = bcmul($bounsUserPerf->sum('old_perf'), 0.3, 4);//累计业绩
$allNew = bcmul($bounsUserPerf->sum('new_perf'), 0.7, 4);//新增业绩
$allPerf = bcadd($allOld, $allNew, 4);//总业绩
$price = bcdiv($bouns->total, $allPerf, 6);//单价
$bouns->doIng();
foreach ($bounsUserPerf as $bounsPerf) {
SendBounsJob::dispatch($bounsPerf, $price, $bouns);
}
} else {
$bouns->doEnd();
}
}
}

66
app/Console/Kernel.php Normal file
View File

@@ -0,0 +1,66 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Nwidart\Modules\Facades\Module as ModuleManager;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
* @var array
*/
protected $commands = [
//
];
/**
* Define the application's command schedule.
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')->hourly();
$schedule->command('Bonus:MonthPerf 1')->monthlyOn(1, '00:01');
$schedule->command('Bonus:StartMonth 1 1')->monthlyOn(1, '00:10');
$schedule->command('Bonus:StartMonth 2 1')->monthlyOn(1, '00:15');
$schedule->command('Bonus:StartMonth 3 1')->monthlyOn(1, '00:20');
$schedule->command('Bonus:StartMonth 4 1')->monthlyOn(1, '00:25');
$schedule->command('Bonus:StartMonth 5 1')->monthlyOn(1, '00:30');
$this->modules($schedule);
}
/**
* 要执行任务的位置增加Console\Kernel类
* 类中 runCommand(Schedule $schedule)
* 模型中的command在模型的ServiceProvider自行注册
* @param \Illuminate\Console\Scheduling\Schedule $schedule
*/
protected function modules(Schedule $schedule)
{
$data = ModuleManager::toCollection();
foreach ($data as $name => $module) {
$nameSpace = "\\Modules\\$name\\Console\\Kernel";
if (class_exists($nameSpace)) {
$runKernel = resolve($nameSpace);
$runKernel->runCommand($schedule);
}
}
}
/**
* Register the commands for the application.
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
// require base_path('routes/console.php');
}
}

View File

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

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Routing\Controller as BaseController;
use Jason\Api\Traits\ApiResponse;
class Controller extends BaseController
{
use ApiResponse;
/**
* Notes : 授权token返回格式
* @Date : 2021/3/16 5:00 下午
* @Author : < Jason.C >
* @param string $token
* @return array
*/
protected function respondWithToken(string $token): array
{
return [
'access_token' => $token,
'token_type' => 'Bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60,
];
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Http\Controllers;
use App\Models\Material;
use Illuminate\Http\Request;
class ImageController extends Controller
{
public function index(Request $request)
{
$name = $request->name;
$img = Material::query()->where('title', $name)->first();
return view('image', compact('img'));
}
}

66
app/Http/Kernel.php Normal file
View File

@@ -0,0 +1,66 @@
<?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
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\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
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
// 'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::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,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}

View File

@@ -0,0 +1,17 @@
<?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
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,17 @@
<?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
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null ...$guards
* @return mixed
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}

View File

@@ -0,0 +1,19 @@
<?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
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

View File

@@ -0,0 +1,20 @@
<?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
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Middleware;
use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array|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;
}

View File

@@ -0,0 +1,17 @@
<?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
*/
protected $except = [
'wechat'
];
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Jobs\Bonus;
use App\Bonus\IdentityBonus;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Modules\User\Models\Order;
use Modules\User\Models\User;
class BuyIdentityJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue;
public $queue = 'BONUS';
public $delay = 0;
public $tries = 1;
public $timeout = 30;
protected $user; //bonus
protected $order; //bonus
protected $source;//bonus
public function __construct(User $user, Order $order, array $source = [])
{
$this->user = $user->fresh();
$this->order = $order->fresh();
$this->source = $source;
}
public function handle()
{
IdentityBonus::BuyIdentity($this->user, $this->order, $this->source);
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace App\Jobs\Bonus;
use App\Models\BounsUserPerf;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Modules\User\Models\User;
class MonthPerfJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue;
public $queue = 'BONUS';
public $delay = 0;
public $tries = 1;
public $timeout = 30;
protected $user; //bonus
protected $last; //bonus
/**
* @param User $user 用户
* @param bool $last 是否上个月
*/
public function __construct(User $user, bool $last)
{
$this->user = $user->fresh();
$this->last = $last;
}
public function handle()
{
$time = [now()->startOfMonth()->toDateTimeString(), now()->endOfMonth()->toDateTimeString()];
if ($this->last) {
$time = [
now()->subMonth()->startOfMonth()->toDateTimeString(),
now()->subMonth()->endOfMonth()->toDateTimeString()
];
}
$old = $this->user->allPerf();
$new = $this->user->allPerf($time);
BounsUserPerf::updateOrCreate([
'user_id' => $this->user->id,
'date' => $time[0],
], [
'star' => $this->user->identityFirst()->pivot->star,
'old_perf' => $old,
'new_perf' => $new,
]);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Jobs\Bonus;
use App\Models\Bouns;
use App\Models\BounsUserPerf;
use App\Models\Job;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
class SendBounsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue;
public $queue = 'BONUS';
public $delay = 0;
public $tries = 1;
public $timeout = 30;
protected BounsUserPerf $bounsPerf; //BounsUserPerf
protected float $price;//单价
protected Bouns $bouns;//单价
public function __construct(BounsUserPerf $bounsPerf, float $price, Bouns $bouns)
{
$this->bounsPerf = $bounsPerf->fresh();
$this->price = $price;
$this->bouns = $bouns;
}
public function handle()
{
try {
$perf = bcadd(bcmul($this->bounsPerf->old_perf, 0.3, 4),
bcmul($this->bounsPerf->new_perf, 0.7, 4), 4);
$this->bounsPerf->price = $this->price;
$this->bounsPerf->amount = $this->price * $perf;
$this->bounsPerf->user->account->rule('star_balance', $this->bounsPerf->amount, false, [
'remark' => Bouns::TYPES[$this->bouns->type],
'star' => $this->bouns->type,
]);
} catch (\Exception $e) {
$this->bounsPerf->status = BounsUserPerf::STATUS_ERROR;
}
$this->bounsPerf->save();
if (Job::where('queue', 'BONUS')->where('payload', 'like', '%SendBounsJob%')->count() <= 1) {
$this->bouns->doEnd();
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Listeners;
use Illuminate\Contracts\Queue\ShouldQueue;
use Modules\Payment\Events\Paid;
class PaymentPaidListener implements ShouldQueue
{
public function handle(Paid $event): void
{
$payment = $event->payment;
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Listeners;
use App\Jobs\Bonus\BuyIdentityJob;
use App\Models\Bouns;
use Illuminate\Contracts\Queue\ShouldQueue;
use Modules\Task\Facades\TaskFacade;
use Modules\User\Events\UserOrderPaid;
class UserOrderPaidListener implements ShouldQueue
{
public function handle(UserOrderPaid $event)
{
$order = $event->order;
$user = $event->order->user;
$identity = $event->order->identity;
$source = [
'identity_id' => $identity->id,
'order_id' => $event->order->id,
'type' => $event->order->type,
];
// BuyIdentityJob::dispatch($user, $order, $source);//个人赠送水滴
// Bouns::addBouns($order, $order->price);
#TODO 开通会员赠送水滴
// TaskFacade::do('open_vip', $user->id, [
// 'identity_id' => $event->order->identity_id
// ]);
#TODO 邀请一名健康体验馆 赠送水滴
// if ($user->parent) {
// TaskFacade::do('recommend_ty', $user->parent->id, [
// 'user_id' => $user->id
// ]);
// }
}
}

128
app/Models/Bouns.php Normal file
View File

@@ -0,0 +1,128 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model as BaseModel;
class Bouns extends Model
{
protected $dates = ['date'];
protected $casts = [
'source' => 'array',
];
const ONE_STAR = 1;
const TWO_STAR = 2;
const THREE_STAR = 3;
const FOUR_STAR = 4;
const FIVE_STAR = 5;
const TYPES = [
self::ONE_STAR => '一星分红',
self::TWO_STAR => '二星分红',
self::THREE_STAR => '三星分红',
self::FOUR_STAR => '四星分红',
self::FIVE_STAR => '五星分红',
];
const TYPEARRAY = [self::ONE_STAR, self::TWO_STAR, self::THREE_STAR, self::FOUR_STAR, self::FIVE_STAR];
const STATUS_INIT = 0;
const STATUS_SENDING = 1;
const STATUS_SENDED = 2;
const STATUS_REJECT = 8;
const STATUS_ERROR = 9;
const STATUS = [
self::STATUS_INIT => '待发放',
self::STATUS_SENDING => '发放中',
self::STATUS_SENDED => '发放完毕',
self::STATUS_REJECT => '停发',
self::STATUS_ERROR => '错误',
];
protected function orders()
{
return $this->hasMany(BounsOrder::class);
}
/**
* 增加累计额度,增加对应订单记录
* @param BaseModel $order
* @param float $total
* @param float $rate
* @return void
*/
protected function addAmount(BaseModel $order, float $total, float $rate)
{
$amount = bcmul($total, bcdiv($rate, 100, 4), 2);
if ($this->orders()->create([
'order_type' => $order->getMorphClass(),
'order_id' => $order->id,
'type' => $this->type,
'total' => $total,
'amount' => $amount,
])) {
$this->increment('total', $amount);
}
}
/**
* 获取分红模型
* @param int $type 类别
* @param bool $last 是否上一期
* @return mixed
*/
public static function getBouns(int $type, bool $last = false)
{
$time = now()->startOfMonth();
if ($last) {
$time = now()->subMonth()->startOfMonth();
}
return Bouns::firstOrCreate([
'type' => $type,
'date' => $time->toDateTimeString(),
], [
'total' => 0,
'status' => self::STATUS_INIT,
]);
}
/**
* 增加分红
* @param BaseModel $order 订单
* @param float $total 订单金额(未计算前)
* @return void
*/
public static function addBouns(BaseModel $order, float $total)
{
$rateArray = [
self::ONE_STAR => app('Conf_user')['one_star_balance_rate'] ?? 0,
self::TWO_STAR => app('Conf_user')['two_star_balance_rate'] ?? 0,
self::THREE_STAR => app('Conf_user')['three_star_balance_rate'] ?? 0,
self::FOUR_STAR => app('Conf_user')['four_star_balance_rate'] ?? 0,
self::FIVE_STAR => app('Conf_user')['five_star_balance_rate'] ?? 0,
];
foreach ($rateArray as $key => $rate) {
if ($rate > 0) {
$model = Bouns::getBouns($key);
$model->addAmount($order, $total, $rate);
}
}
}
public function doIng()
{
$this->status = self::STATUS_SENDING;
$this->save();
}
public function doEnd()
{
$this->status = self::STATUS_SENDED;
$this->save();
}
public function doError()
{
$this->status = self::STATUS_ERROR;
$this->save();
}
}

12
app/Models/BounsOrder.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
namespace App\Models;
class BounsOrder extends Model
{
public function bouns()
{
return $this->belongsTo(Bouns::class);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Modules\User\Traits\BelongsToUser;
class BounsUserPerf extends Model
{
use BelongsToUser;
const STATUS_INIT = 0;
const STATUS_SUCCESS = 1;
const STATUS_ERROR = 2;
const STATUS = [
self::STATUS_INIT => '待发放',
self::STATUS_SUCCESS => '完成',
self::STATUS_ERROR => '失败',
];
public function bouns()
{
return $this->belongsTo(Bouns::class);
}
}

8
app/Models/Job.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
namespace App\Models;
class Job extends Model
{
}

11
app/Models/Material.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
namespace App\Models;
use App\Traits\HasCovers;
class Material extends Model
{
use HasCovers;
}

27
app/Models/Model.php Normal file
View File

@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use App\Traits\Macroable;
use Encore\Admin\Traits\DefaultDatetimeFormat;
use Illuminate\Database\Eloquent\Model as Eloquent;
class Model extends Eloquent
{
use DefaultDatetimeFormat,
Macroable;
/**
* 进制批量写入的字段
* @var array
*/
protected $guarded = [];
/**
* 修改模型默认分页数量
* @var int
*/
protected $perPage = 10;
}

51
app/Models/Module.php Normal file
View File

@@ -0,0 +1,51 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Request;
use Nwidart\Modules\Facades\Module as ModuleManager;
class Module extends Model
{
/**
* Notes : 自定义返回数据
* @Date : 2021/3/11 9:59 上午
* @Author : < Jason.C >
* @return \Illuminate\Pagination\LengthAwarePaginator
*/
public function paginate(): LengthAwarePaginator
{
$perPage = Request::get('per_page', 20);
// $page = Request::get('page', 1);
// $start = ($page - 1) * $perPage;
$data = ModuleManager::toCollection();
$movies = $data->map(function ($module) {
return [
'id' => $module->getName(),
'name' => $module->getName(),
'alias' => $module->getAlias(),
'description' => $module->getDescription(),
'priority' => $module->getPriority(),
'keywords' => $module->get('keywords'),
'requires' => $module->getRequires(),
'enabled' => $module->isEnabled(),
'version' => $module->get('version'),
'author' => $module->get('author'),
];
});
$movies = static::hydrate($movies->toArray());
$paginator = new LengthAwarePaginator($movies, ModuleManager::count(), $perPage);
$paginator->setPath(url()->current());
return $paginator;
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace App\Notifications;
use App\Channels\WechatMiniChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\DatabaseChannel;
use Illuminate\Notifications\Notification;
use Modules\User\Models\Identity;
use Modules\User\Models\IdentityMiddle;
use Modules\User\Models\User;
class SystemOpenVip extends Notification
{
use Queueable;
protected $identityMiddle;
protected $identity;
protected $title;
protected $remark;
protected $url;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(IdentityMiddle $identityMiddle)
{
$identity = $identityMiddle->identity;
$remark = '赠送'.$identity->stock.'箱水';
// if ($identity->job == Identity::JOB_HH) {
// $remark = '开启奖金模式';
// }
$this->title = "恭喜您!开通{$identityMiddle->identity->name}成功!";
$this->identityMiddle = $identityMiddle;
$this->identity = $identity;
$this->remark = $remark;
$this->url = config('user.web.base');
}
public function via(): array
{
return [DatabaseChannel::class, WechatMiniChannel::class];
}
/**
* Notes: 开通会员
*
* @Author: 玄尘
* @Date: 2022/8/9 10:44
* @param User $notifiable
* @return bool
*/
public function toWeChat(User $notifiable): bool
{
if ($notifiable->isOfficialSubscribe()) {
$app = app('wechat.official_account');
$start_at = $this->identityMiddle->started_at ?? '';
$end_at = $this->identityMiddle->ended_at ?? '';
$time = $start_at.' ~ '.$end_at;
if (empty($start_at) || empty($end_at)) {
$time = '永久';
}
$app->template_message->send([
'touser' => $notifiable->wechat->official_openid,
'template_id' => 'gtS1LS9Irw7h2RtQLT5Cxx4p28-k8PrPyH53HBU2oWk',
'url' => $this->url,
'data' => [
'first' => $this->title,
'keyword1' => $time,
'keyword2' => $this->remark,
'remark' => '',
],
]);
}
return true;
}
/**
* 发送到数据库
*
* @param mixed $notifiable
* @return array
*/
public function toDatabase(User $notifiable): array
{
return [
'title' => $this->title,
'content' => $this->remark,
'url' => $this->url,
];
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace App\Notifications;
use App\Channels\WechatMiniChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\DatabaseChannel;
use Illuminate\Notifications\Notification;
use Modules\Mall\Models\Order;
use Modules\Mall\Models\OrderExpress;
use Modules\User\Models\User;
class SystemOrderDelivered extends Notification
{
use Queueable;
protected $order;
protected $title;
protected $url;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($title, Order $order, $url = '')
{
$this->title = $title;
$this->order = $order;
$this->url = $url;
}
public function via(): array
{
return [DatabaseChannel::class, WechatMiniChannel::class];
}
/**
* Notes: 订单发货
*
* @Author: 玄尘
* @Date: 2022/8/4 16:22
* @param User $notifiable
* @return bool
*/
public function toWeChat(User $notifiable): bool
{
if ($notifiable->isOfficialSubscribe()) {
$app = app('wechat.official_account');
$remark = '您的宝贝已经发货,请耐心等待';
if ($this->order->express->type == OrderExpress::TYPE_LOGISTICS) {
$remark .= ',经办人:'.$this->order->express->person;
}
$res = $app->template_message->send([
'touser' => $notifiable->wechat->official_openid,
'template_id' => 'UvUA6wvPSegvT7i8IVrLipktbtCmyjtdnuKD8EvyOO8',
'url' => $this->url,
'data' => [
'first' => $this->title,
'keyword1' => $this->order->order_no,
'keyword2' => $this->order->express->deliver_at,
'keyword3' => $this->order->express->express_id > 0 ? $this->order->express->express->name : '',
'keyword4' => $this->order->express->express_no ?? '',
'keyword5' => $this->order->express->getFullAddress(),
'remark' => $remark,
],
]);
}
return true;
}
/**
* Notes: 数据库
*
* @Author: 玄尘
* @Date: 2022/8/4 16:38
* @param User $notifiable
* @return array
*/
public function toDatabase(User $notifiable): array
{
return [
'title' => $this->title,
'content' => '订单编号:'.$this->order->order_no.' 已发货',
'url' => $this->url,
];
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace App\Notifications;
use App\Channels\WechatMiniChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\DatabaseChannel;
use Illuminate\Notifications\Notification;
use Modules\User\Models\User;
class SystemRemindUserSign extends Notification
{
use Queueable;
protected $content;
protected $title;
protected $url;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
$this->title = '喝水打卡提醒';
$this->content = '您今天还没有喝水打卡,请前去打卡';
$this->url = config('user.web.base');
}
public function via(): array
{
return [DatabaseChannel::class, WechatMiniChannel::class];
}
/**
* Notes: 喝水打卡提醒
*
* @Author: 玄尘
* @Date: 2022/8/8 8:48
* @param User $notifiable
* @return bool
*/
public function toWeChat(User $notifiable): bool
{
if ($notifiable->isOfficialSubscribe()) {
$app = app('wechat.official_account');
$app->template_message->send([
'touser' => $notifiable->wechat->official_openid,
'template_id' => 'N7-vo1bSYXahw22pplkHtI7WGg96dPf1KdMxbKdx6ao',
'url' => $this->url,
'data' => [
'first' => $this->title,
'keyword1' => now(),
'keyword2' => $this->content,
'remark' => '',
],
]);
}
return true;
}
/**
* 发送到数据库
*
* @param mixed $notifiable
* @return array
*/
public function toDatabase(User $notifiable): array
{
return [
'title' => $this->title,
'content' => $this->content,
'url' => $this->url,
];
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace App\Notifications;
use App\Channels\WechatMiniChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Channels\DatabaseChannel;
use Illuminate\Notifications\Notification;
use Modules\User\Models\IdentityMiddle;
use Modules\User\Models\User;
class SystemUpdateCase extends Notification
{
use Queueable;
protected $content;
protected $title;
protected $url;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
$this->title = '上传报告提醒';
$this->url = config('user.web.base');
}
public function via(): array
{
return [DatabaseChannel::class, WechatMiniChannel::class];
}
/**
* Notes: 喝水打卡提醒
*
* @Author: 玄尘
* @Date: 2022/8/8 8:48
* @param User $notifiable
* @return bool
*/
public function toWeChat(User $notifiable): bool
{
if ($notifiable->isOfficialSubscribe()) {
$app = app('wechat.official_account');
$app->template_message->send([
'touser' => $notifiable->wechat->official_openid,
'template_id' => '3sksrHdMTu3k1yderqyP5hOYXWltNf-CvESRG4r3Fnc',
'url' => $this->url,
'data' => [
'first' => $this->title,
'keyword1' => now(),
'keyword2' => '您已经连续打卡30天请上传报告',
'remark' => '',
],
]);
}
return true;
}
/**
* 发送到数据库
*
* @param mixed $notifiable
* @return array
*/
public function toDatabase(User $notifiable): array
{
return [
'title' => $this->title,
'content' => '您已经连续打卡30天请上传报告',
'url' => $this->url,
];
}
}

View File

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

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Providers;
use App\Listeners\UserOrderPaidListener;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Modules\User\Events\UserOrderPaid;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
//开通会员
UserOrderPaid::class => [
UserOrderPaidListener::class,
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
//
}
}

View File

@@ -0,0 +1,63 @@
<?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 the "home" route for your application.
*
* This is used by Laravel authentication to redirect users after login.
*
* @var string
*/
public const HOME = '/home';
/**
* The controller namespace for the application.
*
* When present, controller route declarations will automatically be prefixed with this namespace.
*
* @var string|null
*/
// protected $namespace = 'App\\Http\\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
});
}
/**
* Configure the rate limiters for the application.
*
* @return void
*/
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
});
}
}

84
app/Traits/HasClicks.php Normal file
View File

@@ -0,0 +1,84 @@
<?php
namespace App\Traits;
use Illuminate\Support\Facades\Cache;
/**
* 预期给所有拥有浏览计数的模型使用
* 使用缓存,计算浏览量,定期更新缓存至数据库中
*/
trait HasClicks
{
protected int $saveRate = 20;
/**
* Notes : 获取点击量的字段
* @Date : 2021/3/17 9:39 上午
* @Author : < Jason.C >
* @return string
*/
private function getClicksField(): string
{
return $this->clicks_filed ?? 'clicks';
}
/**
* Notes : 获取缓存前缀
* @Date : 2021/3/16 5:52 下午
* @Author : < Jason.C >
* @return string
*/
private function getClickCachePrefix(): string
{
return $this->cachePrefix ?? class_basename(__CLASS__);
}
/**
* Notes : 生成一个缓存KEY
* @Date : 2021/3/16 5:52 下午
* @Author : < Jason.C >
* @param string|null $appends
* @return string
*/
private function getCacheKey(string $appends = null): string
{
return $this->getClickCachePrefix() . ':' . $this->getKey() . ':' . $appends;
}
/**
* Notes : 增加点击量
* @Date : 2021/3/17 9:20 上午
* @Author : < Jason.C >
* @param int $step
*/
public function incrementClicks(int $step = 1): void
{
Cache::increment($this->getCacheKey('clicks'), $step);
if (rand(1, $this->saveRate) === 1) {
$this->update([$this->getClicksField() => $this->clicks]);
}
}
/**
* Notes : 获取缓存的浏览次数
* @Date : 2021/3/16 5:52 下午
* @Author : < Jason.C >
* @return int
*/
public function getClicksAttribute(): int
{
$clicks = Cache::get($this->getCacheKey('clicks'));
if (is_null($clicks)) {
return Cache::rememberForever($this->getCacheKey('clicks'), function () {
return $this->getAttributes()[$this->getClicksField()];
});
} else {
return $clicks;
}
}
}

83
app/Traits/HasCovers.php Normal file
View File

@@ -0,0 +1,83 @@
<?php
namespace App\Traits;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
trait HasCovers
{
/**
* Notes : 获取封面图片字段(单图)
* @Date : 2021/3/16 4:34 下午
* @Author : < Jason.C >
* @return string
*/
public function getCoverField(): string
{
return $this->cover_field ?? 'cover';
}
/**
* Notes : 获取图片字段(多图)
* @Date : 2021/3/16 4:35 下午
* @Author : < Jason.C >
* @return string
*/
public function getPicturesField(): string
{
return $this->pictures_field ?? 'pictures';
}
/**
* Notes : 解析单图地址
* @Date : 2021/3/16 4:54 下午
* @Author : < Jason.C >
* @return string
*/
public function getCoverUrlAttribute(): string
{
$cover = $this->getAttribute($this->getCoverField());
return $this->parseImageUrl($cover);
}
/**
* Notes : 解析多图地址
* @Date : 2021/3/16 4:54 下午
* @Author : < Jason.C >
* @return array
*/
public function getPicturesUrlAttribute(): array
{
$pictures = $this->getAttribute($this->getPicturesField());
if (empty($pictures)) {
return [];
}
return collect($pictures)->map(function ($picture) {
return $this->parseImageUrl($picture);
})->toArray();
}
/**
* Notes : 解析图片文件的实际展示地址
* @Date : 2021/3/16 4:53 下午
* @Author : < Jason.C >
* @param string|null $image
* @return string
*/
public function parseImageUrl(?string $image): string
{
if (empty($image)) {
return '';
} elseif (Str::startsWith($image, 'http')) {
return $image;
} else {
return Storage::url($image);
}
}
}

87
app/Traits/HasStatus.php Normal file
View File

@@ -0,0 +1,87 @@
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;
trait HasStatus
{
/**
* Notes : 获取状态字段,主模型可配置 $status_field
* @Date : 2021/3/16 4:34 下午
* @Author : < Jason.C >
* @return string
*/
protected function getStatusField(): string
{
return $this->status_field ?? 'status';
}
/**
* Notes : 获取各状态的名称
* @Date : 2021/5/27 11:50 上午
* @Author : < Jason.C >
* @return string[]
*/
protected function getStatusMap(): array
{
return isset($this->status_map) && !empty($this->status_map) ? $this->status_map : [
0 => '待审核',
1 => '正常',
2 => '驳回',
3 => '关闭',
];
}
/**
* 正常显示的数据
* @Author:<Mr.Wang>
* @Date :2021-04-09
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeShown(Builder $query): Builder
{
return $query->where($this->getStatusField(), 1);
}
/**
* 不显示的数据
* @Author :<Mr.Wang>
* @Date :2021-04-09
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeHidden(Builder $query): Builder
{
return $query->where($this->getStatusField(), 0);
}
/**
* Notes : 状态查询
* @Date : 2021/6/28 10:25 上午
* @Author : < Jason.C >
* @param \Illuminate\Database\Eloquent\Builder $query
* @param int $status
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeOfStatus(Builder $query, int $status): Builder
{
return $query->where($this->getStatusField(), $status);
}
/**
* Notes : 获取状态的文本信息
* @Date : 2021/4/25 2:10 下午
* @Author : < Jason.C >
* @return string
*/
public function getStatusTextAttribute(): string
{
$map = $this->getStatusMap();
return $map[$this->{$this->getStatusField()}] ?? '未知';
}
}

50
app/Traits/Macroable.php Normal file
View File

@@ -0,0 +1,50 @@
<?php
namespace App\Traits;
trait Macroable
{
use \Illuminate\Support\Traits\Macroable {
__call as macroCall;
}
/**
* @param string $key
* @return mixed
*/
public function getRelationValue($key)
{
$relation = parent::getRelationValue($key);
if (!$relation && static::hasMacro($key)) {
return $this->getRelationshipFromMethod($key);
}
return $relation;
}
/**
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
return parent::__call($method, $parameters);
}
/**
* @param string $method
* @param array $parameters
* @return mixed
*/
public static function __callStatic($method, $parameters)
{
return parent::__callStatic($method, $parameters);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;
trait OrderByIdDesc
{
/**
* Notes: 初始化trait自动在模型中注入作用域
* @Author: <C.Jason>
* @Date : 2020/1/19 1:42 下午
*/
public static function bootOrderByIdDesc(): void
{
static::addGlobalScope(function (Builder $builder) {
$builder->orderByDesc('id');
});
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;
trait OrderByOrderAsc
{
/**
* Notes: 初始化trait自动在模型中注入作用域
* @Author: <C.Jason>
* @Date : 2020/1/19 1:42 下午
*/
public static function bootOrderByOrderAsc(): void
{
static::addGlobalScope(function (Builder $builder) {
$builder->orderBy('order')->orderByDesc('id');
});
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;
trait WithPosition
{
/**
* Notes : 获取定位的数组
* @Date : 2021/7/2 11:31 上午
* @Author : < Jason.C >
*/
protected function getPositionMap(): array
{
return $this->position_map ?? [];
}
/**
* Notes: 定位查询作用域
* @Author: Mr.wang
* @Date : 2021/5/11 10:48
* @param \Illuminate\Database\Eloquent\Builder $query
* @param int $pos
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeOfPosition(Builder $query, int $pos): Builder
{
return $query->whereRaw('position & ' . $pos);
}
/**
* Notes: 设置定位
* @Author: Mr.wang
* @Date : 2020/5/11 10:48
* @param int $value
*/
protected function setPositionAttribute($value): void
{
if (is_array($value) && !blank($value)) {
$this->attributes['position'] = array_sum($value);
}
}
/**
* Notes: 获取定位数据
* @Author: Mr.wang
* @Date : 2020/5/11 10:48
* @param int $value
* @return array
*/
protected function getPositionAttribute(int $value): array
{
$position = [];
foreach ($this->getPositionMap() as $k => $v) {
if ($k & $value) {
array_push($position, $k);
}
}
return $position;
}
}