250 lines
8.0 KiB
PHP
250 lines
8.0 KiB
PHP
<?php
|
||
|
||
namespace app\admin\controller\statistics;
|
||
|
||
use app\admin\model\Admin;
|
||
use app\admin\model\Order;
|
||
use app\common\controller\Backend;
|
||
use think\exception\DbException;
|
||
use think\response\Json;
|
||
use function Symfony\Component\Clock\now;
|
||
|
||
/**
|
||
* 订单列管理
|
||
*
|
||
* @icon fa fa-circle-o
|
||
*/
|
||
class Dispatcher extends Backend
|
||
{
|
||
protected $noNeedRight = ['chart','chartData'];
|
||
/**
|
||
* Staorder模型对象
|
||
* @var Order
|
||
*/
|
||
protected $model = null;
|
||
|
||
public function _initialize(): void
|
||
{
|
||
parent::_initialize();
|
||
$this->model = new Order();
|
||
}
|
||
|
||
|
||
|
||
|
||
/**
|
||
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||
*/
|
||
|
||
|
||
/**
|
||
* 查看
|
||
*
|
||
* @return string|Json
|
||
* @throws \think\Exception
|
||
* @throws DbException
|
||
*/
|
||
public function index()
|
||
{
|
||
//$this->chart();
|
||
//$today = now()->sub('')->format('Y-m-d' );
|
||
$today = now()->format('Y-m-01');
|
||
$today_end = now()->format('Y-m-d');
|
||
|
||
|
||
//设置过滤方法
|
||
$this->request->filter(['strip_tags', 'trim']);
|
||
if (false === $this->request->isAjax()) {
|
||
$this->assign('daterange',$today.' - '.$today_end);
|
||
|
||
$this->assignconfig('default_daterange',now()->format('Y-m-01 00:00:00').' - '.now()->format('Y-m-d 23:59:59'));
|
||
|
||
return $this->view->fetch();
|
||
}
|
||
|
||
$filter = $this->request->param('filter');
|
||
$filter = json_decode($filter,true);
|
||
|
||
$orderByData = [
|
||
'sort' => $this->request->get('sort','dispatch_admin_id'),
|
||
'order' => $this->request->get('order','desc')
|
||
];
|
||
|
||
$filter['orderBy'] = $orderByData;
|
||
|
||
|
||
if(!empty($filter['daterange'])){
|
||
$arr = explode(' - ',$filter['daterange']);
|
||
if(trim($arr[0])){
|
||
$filter['start_time'] = trim($arr[0]);
|
||
}
|
||
if(trim($arr[1])){
|
||
$filter['end_time'] = trim($arr[1]);
|
||
}
|
||
}
|
||
|
||
$list = $this->chart($filter,false);
|
||
|
||
$result = array("total" => $list->total(), "rows" => $list->items());
|
||
|
||
return json($result);
|
||
}
|
||
|
||
|
||
public function chartData()
|
||
{
|
||
$filter = $this->request->post();
|
||
|
||
if(!empty($filter['daterange'])){
|
||
$arr = explode(' - ',$filter['daterange']);
|
||
if(trim($arr[0])){
|
||
$filter['start_time'] = trim($arr[0]);
|
||
}
|
||
if(trim($arr[1])){
|
||
$filter['end_time'] = trim($arr[1]).' 23:59:59';
|
||
}
|
||
}
|
||
|
||
$data = $this->chart($filter,true);
|
||
|
||
|
||
$newData = [
|
||
['派单员','总业绩','转化率','利润率','变现值']
|
||
];
|
||
foreach ($data as $datum){
|
||
$newData[] = [
|
||
$datum['admin_user'],
|
||
$datum['performance'],
|
||
$datum['trans_rate'],
|
||
$datum['performance_rate'],
|
||
$datum['cash_value'],
|
||
];
|
||
}
|
||
return $newData;
|
||
}
|
||
|
||
//图表统计
|
||
public function chart($filter,$getAll=false): \think\Collection|\think\Paginator|bool|array|string|\PDOStatement
|
||
{
|
||
|
||
$orderValid = implode(',',$this->model->tabStatus(Order::TAB_VALID));
|
||
|
||
$fields = [
|
||
'dispatch_admin_id',
|
||
// 只要是 COUNT(...) 不用 IFNULL
|
||
"COUNT(CASE WHEN status = 60 THEN 1 END) AS finish_num", // 完成数
|
||
"COUNT(CASE WHEN status IN (".$orderValid.") THEN 1 END) AS count_num", // 总订单数(排除取消和草稿)
|
||
"COUNT(CASE WHEN status IN (".$orderValid.") AND is_overtime = 1 THEN 1 END) AS overtime_num", // 超时数
|
||
|
||
// SUM 可能为 NULL,使用 IFNULL 保证 0
|
||
"IFNULL(SUM(CASE WHEN status = 60 THEN total END), 0) AS total", // 成效额
|
||
"IFNULL(SUM(CASE WHEN status = 60 THEN performance END), 0) AS performance", // 业绩
|
||
"IFNULL(SUM(CASE WHEN status = 60 THEN (cost + material_cost) END), 0) AS cost_total", // 总成本
|
||
|
||
// refund_total 计算改成 IFNULL(SUM(...), 0)
|
||
"IFNULL(SUM(CASE WHEN status = 60 THEN (refund_amount + worker_refund_amount) ELSE 0 END), 0) AS refund_total",
|
||
|
||
"COUNT(CASE WHEN status = 60 AND (refund_amount > 0 OR worker_refund_amount > 0) THEN 1 END) AS refund_count", // 退款订单数量
|
||
|
||
"IFNULL(AVG(CASE WHEN status = 60 THEN UNIX_TIMESTAMP(dispatch_time) - UNIX_TIMESTAMP(create_time) END), 0) AS avg_time_diff", // 派单时效
|
||
];
|
||
|
||
$builder = $this->model
|
||
->field($fields);
|
||
//->where('dispatch_admin_id','>',0);
|
||
|
||
if(!empty($filter['orderBy'])){
|
||
$builder->order($filter['orderBy']['sort'],$filter['orderBy']['order']);
|
||
}
|
||
|
||
if(isset($filter['dispatch_admin_id'])){
|
||
$builder->whereIn('dispatch_admin_id',$filter['dispatch_admin_id']);
|
||
}
|
||
|
||
if(!empty($filter['start_time']) && !empty($filter['end_time'])){
|
||
/*$time_by = $filter['time_by'] ??1;
|
||
if($time_by == 1){ //按派单时间
|
||
$time_field = 'dispatch_time';
|
||
}else{ //按录单时间
|
||
$time_field = 'create_time';
|
||
}*/
|
||
|
||
$time_field = 'audit_time';
|
||
$builder->whereBetween($time_field,[$filter['start_time'],$filter['end_time']]);
|
||
}
|
||
//城市
|
||
if(!empty($filter['area_id'])){
|
||
$builder->where('area_id',$filter['area_id']);
|
||
}
|
||
//项目
|
||
if(!empty($filter['item_id'])){
|
||
$builder->where('item_id',$filter['item_id']);
|
||
}
|
||
|
||
if($getAll){
|
||
$data = $builder->group('dispatch_admin_id')->limit(50)->select();
|
||
}else{
|
||
$data = $builder->group('dispatch_admin_id')->paginate();
|
||
}
|
||
|
||
$newData = [];
|
||
|
||
if(!empty($data)){
|
||
foreach ($data as &$datum){
|
||
//利润率 = 总业绩/总成效额
|
||
$datum->performance_rate = $this->_calc($datum->performance,$datum->total,4,true);
|
||
//转化率 = 完单数 / 总订单数
|
||
$datum->trans_rate = $this->_calc($datum->finish_num,$datum->count_num,4,true);
|
||
//变现值 = 总业绩 / 总订单数
|
||
$datum->cash_value = $this->_calc($datum->performance,$datum->count_num,2);
|
||
//客单利润 = 总利润 / 完单数
|
||
$datum->performance_avg = $this->_calc($datum->performance,$datum->finish_num,2);
|
||
//客单价 = 总成效额 / 完单数
|
||
$datum->total_avg = $this->_calc($datum->total,$datum->finish_num,2);
|
||
//超时率
|
||
$datum->overtime_rate = $this->_calc($datum->overtime_num,$datum->count_num,4,true);
|
||
|
||
if(!empty($datum->dispatch_admin_id)){
|
||
$datum->admin_user = Admin::where('id',$datum->dispatch_admin_id)->value('nickname')??'系统';
|
||
}else{
|
||
$datum->admin_user = '系统';
|
||
}
|
||
$datum->avg_time_diff = $this->_calc($datum->avg_time_diff,3600,2);
|
||
|
||
$datum->id = $datum->dispatch_admin_id;
|
||
$newData[] = $datum->toArray();
|
||
}
|
||
}
|
||
if($getAll){
|
||
return $newData;
|
||
}else{
|
||
return $data;
|
||
}
|
||
//dump($newData);exit;
|
||
}
|
||
|
||
|
||
/**
|
||
* @param $a
|
||
* @param $b
|
||
* @param int $scale
|
||
* @return int|string
|
||
*/
|
||
private function _calc($a, $b, int $scale=4, $is_percent=false): int|string
|
||
{
|
||
$a = $a??0;
|
||
$b = $b??0;
|
||
|
||
$val = $b > 0 ? bcdiv($a,$b,$scale) : 0;
|
||
|
||
if($is_percent){
|
||
|
||
return bcmul($val,100,2);
|
||
}
|
||
return $val;
|
||
}
|
||
|
||
}
|