REAPI/app/queue/command/monitor/ChannelOrderStatusMonitor.php
mzeros 84856367e9 feat(openapi): 新增码速达API接口
- 添加AfterSales类处理售后服务相关接口
- 新增ContentTemplate控制器和模型用于内容模板管理
- 添加diandian渠道服务类
- 更新Feedov控制器和添加FeedovHfAuto命令类
- 新增autoCard视图用于自动发卡设置
2025-03-11 00:17:40 +08:00

381 lines
12 KiB
PHP

<?php
namespace app\queue\command\monitor;
use app\channel\service\ChannelService;
use app\gateway\service\RedisService;
use app\merchant\service\OrderService;
use app\robot\controller\WeChatBot as WeChatBotC;
use app\robot\controller\WxWorkBot;
use think\admin\Command;
use think\console\Input;
use think\console\Output;
/**
* 渠道订单问题监控预警
* Class ChannelOrderStatusMonitor
* @package app\data\command
*/
class ChannelOrderStatusMonitor extends Command
{
protected $redis = false;
protected function configure()
{
$this->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;
}
}