setName('xQueue:ChannelOrderStatusMonitor')->setDescription('[ 监控系统 ] 渠道订单问题监控预警'); } /** * @param Input $input * @param Output $output * @throws \think\admin\Exception */ protected function execute(Input $input, Output $output) { ini_set('memory_limit', '1024M'); $this->redis(); $channelService = ChannelService::instance(); $RobotService = new WeChatBotC($this->app); $channelList = $channelService->db()->where(['status'=>1])->whereLike('other_data','%Monitor_OrderStatus_status%')->select()->toArray(); if(!$channelList) $this->setQueueSuccess("未找到支持监控订单的渠道"); $channelList_num = count($channelList); $count_sum = 0; $day = date('Y-m-d', strtotime('-1 day')); $tip_info=[]; // $key = '41b42bd4-c9f9-4617-9531-0a358dd97a82'; foreach($channelList as $channel) { $other_param = json_decode($channel['other_data'],true); if(!$other_param){ continue; } $roomid = $other_param['QYWX_roomid']??null; if(!$roomid || $other_param['Monitor_OrderStatus_status'] !=1){ continue; } $DNDTime = $other_param['Monitor_OrderStatus_DNDTime']??null; $sender = $other_param['QYWX_sender']??null; if($DNDTime){ $DNDTime =explode(',', $DNDTime); if ($this->isDNDTime([$DNDTime[0], $DNDTime[1]])) { if(!$this->redis->get('channel_orderStatus_DNDTime_'.$channel['id'])){ $ac = $RobotService->get_contacts($sender) ?? ''; $this->redis->set('channel_orderStatus_DNDTime_'.$channel['id'], 1, 24*60*60); $RobotService->send_text("渠道预警:\n开启免打扰,免打扰时间:".$DNDTime[0]."-".$DNDTime[1]." \n@".$ac,$roomid,$sender); } $this->setQueueProgress(" {$channel['name']} 现在在免打扰时间,跳过执行", $count_sum / $channelList_num * 100); continue; }else{ $currentHour = date('G'); $end =explode(':', $DNDTime[1]); if($currentHour >= $end[0] && $currentHour < ($end[0]+1)){ if($this->redis->get('channel_orderStatus_DNDTime_'.$channel['id'])){ $this->redis->delete('channel_orderStatus_DNDTime_'.$channel['id']); $ac = $RobotService->get_contacts($sender) ?? ''; $RobotService->send_text("渠道预警:\n已关闭免打扰 \n@".$ac,$roomid,$sender); } } } } if(isset($other_param['Monitor_OrderStatus_OrderFailRate'])){ $orderFailRate = $this->orderFailRate($channel,$other_param['Monitor_OrderStatus_OrderFailRate_pid']??null,$other_param['Monitor_OrderStatus_OrderFailRate_pkey']??null,$other_param['Monitor_OrderStatus_OrderFailRate_cash']??null); if($orderFailRate >= $other_param['Monitor_OrderStatus_OrderFailRate']){ $tip_info[] = "渠道名称:{$channel['name']},失败率:{$orderFailRate}%,失败率大于{$other_param['Monitor_OrderStatus_OrderFailRate']}%,请及时处理!"; $ac = $RobotService->get_contacts($sender) ?? ''; $redisKey = 'channel_order_status_OrderFailRate_monitor_' . $channel['id']; $num = $this->redis->get($redisKey) ?? 0; if ($num == 0) { $this->redis->set($redisKey, 1); $RobotService->send_text("渠道预警:最近五分钟订单失败率超过{$other_param['Monitor_OrderStatus_OrderFailRate']}%。\n成功率只剩下" . (100 - $orderFailRate) . '%,请及时处理!' . ' @' . $ac, $roomid, $sender); } else { $this->redis->set($redisKey, $num + 1); $intervals = [2 => 15, 4 => 25, 8 => 45, 12 => 60, 24 => 60, 36 => 60, 72 => 60]; foreach ($intervals as $threshold => $minutes) { if ($num == $threshold) { $RobotService->send_text("渠道预警:最近{$minutes}分钟订单失败率连续超过{$other_param['Monitor_OrderStatus_OrderFailRate']}%。\n成功率只剩下" . (100 - $orderFailRate) . '%,请及时处理!' . ' @' . $ac, $roomid, $sender); break; } } } $count_sum++; }else{ if($this->redis->get('channel_order_status_OrderFailRate_monitor_'.$channel['id'])){ $this->redis->delete('channel_order_status_OrderFailRate_monitor_'.$channel['id']); } } } if(isset($other_param['Monitor_OrderStatus_OrderStuck'])){ $orderStuck = $this->orderStuck($channel,$other_param['Monitor_OrderStatus_OrderStuck']); if(!empty($orderStuck)){ $count = count($orderStuck); $min = $other_param['Monitor_OrderStatus_OrderStuck']/60; $tip_info[] = "渠道名称:{$channel['name']},订单卡顿:{$count}个,请及时处理!均大于{$min}分钟,请及时处理!"; $ac = $RobotService->get_contacts($sender) ?? '' ; $orderStuck_msg = implode("\n", $orderStuck); $RobotService->send_text('渠道预警:目前有'.$count.'个订单卡顿,均大于'.$min."分钟,请及时处理!如下:\n".$orderStuck_msg." \n@".$ac,$roomid,$sender); } } } // if(!empty($tip_info)){ // $errorMsg = "渠道预警:\n"; // $errorMsg .= implode("\n", $tip_info); // $RobotService->send_text('渠道预警:目前有'.$count.'个订单卡顿,均大于'.$min."分钟,请及时处理!如下:\n".$orderStuck_msg." \n@".$ac,$roomid,$sender); // // } $this->setQueueSuccess("共处理 {$channelList_num} 个渠道, 完成{$count_sum} 个查询处理!"); } #获取订单失败率 #获取订单失败率 public function orderFailRate($channel, $pid = null, $p_key = null, $cash = null) { $OrderService = OrderService::instance(); $cur = time(); $time = 300; $startTime = date('Y-m-d H:i:s', $cur - $time); $where = [ 'cid' => $channel['id'], 'create_at' => ['>=', $startTime], ]; if ($pid) { $where['pid'] = ['in', $pid]; } if ($p_key) { $where['product_key'] = ['in', $p_key]; } if ($cash) { $where['cash'] = ['in',$cash]; } // 单次查询计算失败率 $result = $OrderService->db() ->fieldRaw('SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as fail_count, COUNT(*) as total_count') ->where($where) ->where('status', 'in', [2, 3]) ->find(); // 使用 find() 方法获取单条记录 $failCount = $result['fail_count']; $totalCount = $result['total_count']; if ($totalCount == 0) { return 0; } // $failCount = $OrderService->db()->where('create_at', '>=', date('Y-m-d H:i:s', $cur - $time))->where($where)->count(); // // $where['status'] = ['in', '2,3']; // $totalCount = $OrderService->db()->where('create_at', '>=', date('Y-m-d H:i:s', $cur - $time))->where($where)->count(); return floatval(bcdiv($failCount, $totalCount, 2)) * 100; } #检查是否卡顿 #检查是否卡顿 public function orderStuck($channel, $times) { $OrderService = OrderService::instance(); $cur = time(); $time = $times; $where = [ 'cid' => $channel['id'], 'status' => 4, ]; $orders = $OrderService->db()->field('order_id, account, channel_order_id, create_at')->where('create_at', '<=', date('Y-m-d H:i:s', $cur - $time))->where($where)->select()->toArray(); if (!empty($orders)) { $tip_infos = []; foreach ($orders as $order) { $tip_info_msg = "订单号:{$order['order_id']},账号:{$order['account']}"; if ($order['channel_order_id']) { $tip_info_msg .= ",渠道订单号:{$order['channel_order_id']}"; } $start = strtotime($order['create_at']); $end = time(); $time_diff = $end - $start; $min = $this->secToTime($time_diff); $tip_infos[] = $tip_info_msg . ",卡顿时间超过:" . $min; } return $tip_infos; } return []; } protected function redis() { if (!$this->redis) { $this->redis = RedisService::getInstance(); } } public function maketime($v) { if (!$v) { return ''; } if (is_numeric($v)) { return $v; } if (is_array($v)) { $v = $v[1]; } if (strstr($v, ' ')) { $t = explode(' ', $v); $v = $t[0]; $s = explode(':', $t[1]); } else { $s = array(0, 0, 0); } if (!isset($s[1])) { $s[1] = 0; } if (!isset($s[2])) { $s[2] = 0; } if (strstr($v, '-')) { $t = explode('-', $v); } elseif (strstr($v, '/')) { $u = explode('/', $v); $t[0] = $u[2]; $t[1] = $u[0]; $t[2] = $u[1]; } if (!isset($t)) { $t = array(0, 0, 0); } if (!isset($t[1])) { $t[1] = 0; } if (!isset($t[2])) { $t[2] = 0; } $v = mktime($s[0], $s[1], $s[2], $t[1], $t[2], $t[0]); return $v; } public function secToTime($times) { $result = '0秒'; if ($times>0) { $hour = floor($times/3600); $minute = floor(($times-3600 * $hour)/60); $second = floor((($times-3600 * $hour) - 60 * $minute) % 60); $result = ''; if ($hour > 0) { $result = $hour . '时'; } if ($minute > 0) { $result .= $minute . '分'; } if ($second > 0) { $result .= $second . '秒'; } } return $result; } protected function isDNDTime($tme = ['23:30','8:00']) { $currentHour = date('G'); $currentMinute = date('i'); $start =explode(':', $tme[0]); $end =explode(':', $tme[1]); if($start[0]<$end[0]){ if(($currentHour == $start[0] && $currentMinute >= $start[1]) || ($currentHour> $start[0] && $currentHour<= $end[0])){ return true; } if(($currentHour == $end[0] && $currentMinute <= $end[1]) || $currentHour< $end[0]){ return true; } }else{ if(($currentHour == $start[0] && $currentMinute >= $start[1]) || $currentHour> $start[0] ){ return true; } if(($currentHour == $end[0] && $currentMinute <= $end[1]) || $currentHour< $end[0]){ return true; } } return false; } }