
- 添加AfterSales类处理售后服务相关接口 - 新增ContentTemplate控制器和模型用于内容模板管理 - 添加diandian渠道服务类 - 更新Feedov控制器和添加FeedovHfAuto命令类 - 新增autoCard视图用于自动发卡设置
381 lines
12 KiB
PHP
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|