model = new Order(); $template = Template::where('group_id',self::GROUP_ID)->with(['kpiitem'])->find()->toArray(); $itemUnits = []; foreach ($template['kpiitem'] as $item) { $itemRate[$item['attr']] = $item['pivot']['rate']; $itemUnits[$item['attr']] = $item['unit']; } $item_title = []; $item_title['ZHL'] = [ 'title' => '转化率'.(!empty($itemRate['ZHL'])?'(权重'.$itemRate['ZHL'].'%)':''), 'isshow' => !empty($itemRate['ZHL']), 'unit' => '%' ]; $item_title['LRL'] = [ 'title' => '利润率'.(!empty($itemRate['LRL'])?'(权重'.$itemRate['LRL'].'%)':''), 'isshow' =>!empty($itemRate['LRL']), ]; $item_title['PDSX'] = [ 'title' => '派单时效'.(!empty($itemRate['PDSX'])?'(权重'.$itemRate['PDSX'].'%)':''), 'isshow' =>!empty($itemRate['PDSX']), ]; $item_title['PCCGL'] = [ 'title' => '派出成功率'.(!empty($itemRate['PCCGL'])?'(权重'.$itemRate['PCCGL'].'%)':''), 'isshow' =>!empty($itemRate['PCCGL']), ]; $item_title['GDJSL'] = [ 'title' => '跟单及时率'.(!empty($itemRate['GDJSL'])?'(权重'.$itemRate['GDJSL'].'%)':''), 'isshow' =>!empty($itemRate['GDJSL']), ]; $item_title['LRSFS'] = [ 'title' => '录入师傅数'.(!empty($itemRate['LRSFS'])?'(权重'.$itemRate['LRSFS'].'%)':''), 'isshow' =>!empty($itemRate['LRSFS']), ]; /*$item_title['LRL'] = '利润率'.(!empty($itemRate['LRL'])?$itemRate['LRL'].'%':''); $item_title['PDSX'] = '派单时效'.(!empty($itemRate['PDSX'])?$itemRate['LRL'].'%':''); $item_title['PCCGL'] = '派出成功率'.(!empty($itemRate['PCCGL'])?$itemRate['LRL'].'%':''); $item_title['GDJSL'] = '跟单及时率'.(!empty($itemRate['GDJSL'])?$itemRate['LRL'].'%':''); $item_title['LRSFS'] = '录入师傅数'.(!empty($itemRate['LRSFS'])?$itemRate['LRL'].'%':'');*/ $this->assignconfig('item_titles',$item_title); } /** * 查看 * * @return string|Json * @throws \think\Exception * @throws DbException */ public function index() { //$this->chart(); //$today = now()->sub('')->format('Y-m-d' ); $month = now()->format('Y-m'); //设置过滤方法 $this->request->filter(['strip_tags', 'trim']); if (false === $this->request->isAjax()) { $this->assignconfig('month',$month); return $this->view->fetch(); } $filter = $this->request->param('filter'); $filter = json_decode($filter,true); if(empty($filter['monthrange'])){ /*$arr = explode(' - ',$filter['daterange']); if(trim($arr[0])){ $filter['start_time'] = trim($arr[0]); } if(trim($arr[1])){ $filter['end_time'] = trim($arr[1]); }*/ $filter['monthrange'] = date('Y-m'); } $this->getMonthTimeRange($filter); $filter['group_id'] = self::GROUP_ID; if(!empty($filter['admin_user'])){ $adminIds = Admin::where('username','like','%'.$filter['admin_user'].'%')->column('id'); $filter['admin_user_ids'] = $adminIds; } $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 { $template = Template::where('group_id',self::GROUP_ID)->with(['kpiitem'])->find(); if(empty($template) || empty($template->kpiitem)){ return []; } $kpiItem = []; foreach ($template->kpiitem as $item){ $kpiItem[$item->attr] = $item->toArray(); } $orderValid = implode(',',$this->model->tabStatus(Order::TAB_VALID)); //"COUNT(CASE WHEN status IN (".$orderValid.") THEN 1 END) AS ing_num", $fields = [ 'dispatch_admin_id', 'worker_num', // 使用 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 ->where('status',Order::STATUS_FINISHED) ->field($fields); //->where('dispatch_admin_id','>',0); if(isset($filter['admin_user_ids'])){ $builder->whereIn('dispatch_admin_id',$filter['admin_user_ids']); } 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']]); } $subsql = $this->_subsql($filter); $builder->join([$subsql => 'a'], 'a.admin_id = dispatch_admin_id', 'LEFT'); //城市 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 = []; $max_score = $template->max_score??100; 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,4); $datum->id = $datum->dispatch_admin_id; $kpi_total = 0; //kpi //转化率 $datum->zhl_score = 0; $datum->zhl_target_value = $kpiItem[KpiItem::ATTR_ZHL]['target_value']??0; $datum->unit = ''; if(!empty($kpiItem[KpiItem::ATTR_ZHL])){ $datum->zhl_score = $this->_kpi_score($datum->trans_rate,$kpiItem[KpiItem::ATTR_ZHL]); $kpi_total += $datum->zhl_score; //$datum->zhl_unit = $kpiItem[KpiItem::ATTR_ZHL]['unit']; } //利润率 $datum->lrl_score = 0; $datum->lrl_target_value = $kpiItem[KpiItem::ATTR_LRL]['target_value']??0; if (!empty($kpiItem[KpiItem::ATTR_LRL])) { $datum->lrl_score = $this->_kpi_score($datum->performance_rate, $kpiItem[KpiItem::ATTR_LRL]); $kpi_total += $datum->lrl_score; //$datum->lrl_unit = $kpiItem[KpiItem::ATTR_LRL]['unit']; } //派单时效 $datum->pdsx_score = 0; $datum->pdsx_target_value = $kpiItem[KpiItem::ATTR_PDSX]['target_value']??0; //秒 if (!empty($kpiItem[KpiItem::ATTR_PDSX])) { $datum->pdsx_score = $this->_kpi_score($datum->avg_time_diff, $kpiItem[KpiItem::ATTR_PDSX],$fande=true); $kpi_total += $datum->pdsx_score; //$datum->pdsx_unit = $kpiItem[KpiItem::ATTR_PDSX]['unit']; } //派单成功率 $datum->succ_rate = $this->_calc($datum->finish_num,$datum->count_num,4,true); $datum->pccgl_score = 0; $datum->pccgl_target_value = $kpiItem[KpiItem::ATTR_PCCGL]['target_value']??0; if (!empty($kpiItem[KpiItem::ATTR_PCCGL])) { $datum->pccgl_score = $this->_kpi_score($datum->succ_rate, $kpiItem[KpiItem::ATTR_PCCGL]); $kpi_total += $datum->pccgl_score; //$datum->pccgl_unit = $kpiItem[KpiItem::ATTR_PCCGL]['unit']; } //录单师傅数 $datum->lrsfs_score = 0; $datum->lrsfs_target_value = $kpiItem[KpiItem::ATTR_LRSFS]['target_value']??0; $datum->worker_num = $datum->worker_num??0; if (!empty($kpiItem[KpiItem::ATTR_LRSFS])) { $datum->lrsfs_score = $this->_kpi_score($datum->worker_num, $kpiItem[KpiItem::ATTR_LRSFS]); $kpi_total += $datum->lrsfs_score; } $datum->kpi_total = bcadd($kpi_total,0,2); $datum->kpi_value = $this->_calc($kpi_total,$template->score ?: 1,2); if($datum->kpi_value <= 0){ $datum->kpi_money = 0; }else{ $datum->kpi_money = bcmul($datum->performance,$datum->kpi_value,4); if($datum->kpi_money <= 0){ $datum->kpi_money = 0; }else{ $datum->kpi_money = bcdiv($datum->kpi_money,100,2); } } $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; } private function _kpi_score($num,$item,$fande=false) { //完成值/目标值*单个指标分*权重 if($item['target_value'] == 0){ $item['target_value'] = 1; } if($fande){ if($num == 0){ $num = $item['target_value']; } $rate = bcdiv($item['target_value'],$num,4); }else{ $rate = bcdiv($num,$item['target_value'],4); } $score = bcmul($item['score'],$rate,4); $score = bcmul($score,$item['pivot']['rate']/100,2); if($score<= 0){ $score = 0; } return $score; } /** * 获取指定年月的开始和结束时间戳(到秒) * @param string $yearMonth 格式为 "YYYY-MM" 的日期字符串 * @return array 包含开始时间戳和结束时间戳的数组 */ function getMonthTimeRange(&$filter) { $yearMonth = $filter['monthrange']; // 解析输入的年月字符串 list($year, $month) = explode('-', $yearMonth); // 获取当月第一天的时间戳(00:00:00) $startTime = strtotime("{$year}-{$month}-01 00:00:00"); // 获取下个月第一天的时间戳 $nextMonth = strtotime("+1 month", $startTime); // 当月最后一天的时间戳(23:59:59) $endTime = $nextMonth - 1; $filter['start_time'] = date('Y-m-d H:i:s',$startTime); $filter['end_time'] = date('Y-m-d H:i:s',$endTime); return $filter; } public function _subsql($filter){ $builder = new \app\admin\model\Worker(); $fields = [ 'admin_id', "count(*) as worker_num", //完成数 ]; $builder->field($fields)->whereBetween('create_time',[$filter['start_time'],$filter['end_time']]); $builder->group('admin_id'); return $builder->buildSql(); } }