241 lines
7.7 KiB
PHP
241 lines
7.7 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
|
|
{
|
|
|
|
/**
|
|
* 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));
|
|
|
|
//"COUNT(CASE WHEN status IN (".$orderValid.") THEN 1 END) AS ing_num",
|
|
$fields = [
|
|
'dispatch_admin_id',
|
|
// 使用 IFNULL 确保结果为 null 时返回 0
|
|
"IFNULL(COUNT(CASE WHEN status = 60 THEN 1 END), 0) AS finish_num", //完成数
|
|
"IFNULL(COUNT(CASE WHEN status IN (".$orderValid.") THEN 1 END), 0) AS count_num", //总订单数 (排除取消 和草稿)
|
|
"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", //总成本
|
|
"IFNULL(SUM(CASE WHEN status = 60 THEN (refund_amount + worker_refund_amount) END), 0) AS refund_total", //退款总数
|
|
"IFNULL(COUNT(CASE WHEN refund_amount > 0 OR worker_refund_amount > 0 THEN 1 END), 0) AS refund_count", //退款订单数量
|
|
"IFNULL(AVG(CASE WHEN status > 10 THEN UNIX_TIMESTAMP(dispatch_time) - UNIX_TIMESTAMP(create_time) END), 0) AS avg_time_diff", //派单时效
|
|
// "SUM(CASE WHEN status = 60 THEN (field1 + field2) END) AS performance",
|
|
];
|
|
|
|
$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';
|
|
}
|
|
$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);
|
|
|
|
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;
|
|
}
|
|
|
|
}
|