getConfig(); $config = array_merge($fileConfig?:[],$dbConfig?:[]); return isset($config[$name])?$config[$name]:$config; } # 工单号生成 public function ticketNum($prefix='YHGD') { $IdcsmartTicketModel = new IdcsmartTicketModel(); $ticketNum = rand_str(7,'NUMBER'); for ($i=0;$i<10;$i++){ # 至多10次比较 $exist = $IdcsmartTicketModel->where('ticket_num',$ticketNum)->find(); if (empty($exist)){ break; } $ticketNum = rand_str(7,'NUMBER'); } return [$ticketNum,$ticketNum]; } public function setConfig($param) { $PluginModel = new PluginModel(); $plugin = $PluginModel->where('name','IdcsmartTicket')->find(); $config = json_decode($plugin['config'],true); $config['refresh_time'] = $param['refresh_time']??($config['refresh_time']??3); $config['ticket_notice_open'] = $param['ticket_notice_open']??($config['ticket_notice_open']??1); if (isset($param['ticket_notice_description']) && !empty($param['ticket_notice_description'])){ $config['ticket_notice_description'] = htmlspecialchars($param['ticket_notice_description']); }else{ $config['ticket_notice_description'] = $config['ticket_notice_description']??""; } if (isset($param['ticket_type_id']) && !empty($param['ticket_type_id'])){ $IdcsmartTicketTypeModel = new IdcsmartTicketTypeModel(); $ticketType = $IdcsmartTicketTypeModel->where('id',$param['ticket_type_id'])->find(); if (empty($ticketType)){ return ['status'=>400,'msg'=>lang_plugins('ticket_type_is_not_exist')]; } $config['ticket_type_id'] = $param['ticket_type_id']; } $config['downstream_delivery'] = intval($param['downstream_delivery']??0); $config['ticket_close_switch'] = $param['ticket_close_switch']??($config['ticket_close_switch']??0); $config['ticket_close_hour'] = $param['ticket_close_hour']??($config['ticket_close_hour']??0); $plugin->save([ 'config' => json_encode($config) ]); return [ 'status' => 200, 'msg' => lang_plugins('success_message') ]; } /** * 时间 2022-5-25 * @title curl下载解压包到指定路径 * @desc curl下载解压包到指定路径 * @author theworld * @version v1 * @param string url - 下载链接地址 * @param string file_name - 目标路径 * @return mixed */ public static function curl_download($url, $file_name) { $ch = curl_init($url); //设置抓取的url $dir = $file_name; $fp = fopen($dir, "wb"); curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $res=curl_exec($ch); curl_close($ch); fclose($fp); return $res; } public static function idcsmartApiCurlUploadFile($api_id,$file_path,$file_name) { $login = idcsmart_api_login($api_id); if ($login['status']!=200){ return $login; } if($login['data']['supplier']['type']!='default'){ return ['status'=>400,'msg'=>lang_plugins('ticket_supplier_not_support_delivery')]; } // 下载对象存储的文件 $fileUrl = getOssUrl([ 'file_path' => $file_path, 'file_name' => $file_name ])['url']; if (strpos($file_name,'^')!==false){ $file_name = explode('^',$file_name)[1]; } $file = UPLOAD_DEFAULT.$file_name; self::curl_download($fileUrl, $file); if (!file_exists($file)){ return ['status'=>400,'msg'=>lang_plugins('source_file_not_exist')]; } $url = $login['data']['url'] . '/console/v1/upload'; $header = [ 'Authorization: Bearer '.$login['data']['jwt'] ]; $file = realpath($file); $data = ['file' => new \CURLFile($file)]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); $ssl = substr($url, 0, 8) == 'https://' ? true : false; //if ($ssl) { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在 //} curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); if(!empty($header)){ curl_setopt($ch, CURLOPT_HTTPHEADER, $header); } $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($file==UPLOAD_DEFAULT.$file_name){ unlink($file); } if ($http_code!=200){ return ['status'=>400, 'msg'=>lang_plugins('ticket_network_desertion')]; } $response = json_decode($response,true); return $response; } /** * 时间 2024-06-18 * @title 判断后台当前工单是否可操作 * @desc 判断后台当前工单是否可操作,涉及: * @author wyh * @version v1 * @param int ticketId - 工单ID required * @return boolean */ public static function checkUpstreamTicket($ticketId) { // 考虑到目前逻辑是一个工单最多对应一个产品,所以这里直接拉取 $IdcsmartTicketUpstreamModel = new IdcsmartTicketUpstreamModel(); $ticketUpstream = $IdcsmartTicketUpstreamModel->where('ticket_id',$ticketId)->find(); // 1、没有对应的上游工单,可操作 if (empty($ticketUpstream)){ return true; } // 2、对于多级传递来说,中间层不可操作(有上游,也有下游) $IdcsmartTicketModel = new IdcsmartTicketModel(); $ticket = $IdcsmartTicketModel->find($ticketId); if ($ticket['downstream_ticket_id']>0){ // 若本地已关闭传递,且下游已开启传递或者已关闭;则可操作 if ($ticketUpstream['delivery_status']==0/* && $ticket['downstream_delivery_status']==1*/){ return true; } return false; } // 3、已关闭传递,可操作 if ($ticketUpstream['delivery_status']==0){ return true; } return false; } /** * 时间 2024-06-18 * @title 判断前台当前工单是否可操作 * @desc 判断前台当前工单是否可操作,涉及: * @author wyh * @version v1 * @param int ticketId - 工单ID required * @return boolean */ public static function checkDownstreamTicket($ticketId) { $IdcsmartTicketModel = new IdcsmartTicketModel(); $ticket = $IdcsmartTicketModel->find($ticketId); if ($ticket['is_downstream']==1 && $ticket['downstream_delivery_status']==1){ return false; } return true; } // 后台判断是否可以向下游推送 public static function checkPushTicketToDownstream(IdcsmartTicketModel $ticket) { if ($ticket['is_downstream'] && $ticket['downstream_delivery_status']==1){ return true; } return false; } // 下游验证签名 public static function validateSign($params){ $sign = $params['signature']; // 用这几个参数生成签名 $data = [ 'id'=>(int)$params['id'], // 数据类型必须和生成签名时的类型一致 'token'=>$params['token'], 'rand_str'=>$params['rand_str'], ]; ksort($data, SORT_STRING); $str = json_encode($data); $signature = md5($str); return strtoupper($signature) === $sign; } // 推送通用curl请求 public static function commonCurl($url, $data = [], $timeout = 30, $request = 'POST', $header = []) { $result = curl($url,$data,$timeout,$request,$header); if($result['http_code'] != 200){ return ['status'=>400, 'msg'=>lang('network_desertion'), 'content'=>$result['content']]; } $result = json_decode($result['content'], true); if ($result['status']==401){ $result['status']=400; $result['msg'] = lang('network_desertion'); } return $result; } // 推送工单状态至下游 public static function pushTicketStatus(IdcsmartTicketModel $ticket,IdcsmartTicketStatusModel $ticketStatus,$param) { if (self::checkPushTicketToDownstream($ticket)){ // 非默认状态 if (!in_array($param['status'],[1,2,3,4,5])){ // 完结状态推送至下游,下游为已回复 if ($ticketStatus['status']==1){ $param['status'] = 3; }else{ // 未完结状态推送至下游,下游为处理中 $param['status'] = 5; } } $data = [ 'status' => $param['status'] ]; $sign = create_sign(['id'=>$ticket['downstream_ticket_id']],$ticket['downstream_token']); $data = array_merge($data,$sign); $res = IdcsmartTicketLogic::commonCurl(rtrim($ticket['downstream_url'],'/')."/console/v1/api/ticket/{$ticket['downstream_ticket_id']}/status", $data, 30, 'PUT'); if ($res['status']==200){ active_log(lang_plugins('ticket_log_admin_update_downstream_ticket_status_success', ['{admin}'=>'admin#'.request()->admin_id.'#' .request()->admin_name.'#','{ticket}'=>'ticket#'.$ticket['ticket_num'],'{status}'=>$ticketStatus['name'],'{url}'=>$ticket['downstream_url']]), 'addon_idcsmart_ticket', $ticket['id']); }else{ active_log(lang_plugins('ticket_log_admin_update_downstream_ticket_status_fail', ['{admin}'=>'admin#'.request()->admin_id.'#' .request()->admin_name.'#','{ticket}'=>'ticket#'.$ticket['ticket_num'],'{status}'=>$ticketStatus['name'],'{url}'=>$ticket['downstream_url'],'{reason}'=>$res['msg']]), 'addon_idcsmart_ticket', $ticket['id']); } } return true; } // 推送工单回复内容更改至下游 public static function pushTicketReply(IdcsmartTicketModel $ticket,IdcsmartTicketReplyModel $ticketReply,$param) { if (self::checkPushTicketToDownstream($ticket)){ $data = [ 'ticket_reply_id' => $ticketReply['downstream_ticket_reply_id'], 'content' => $param['content']??"" ]; $sign = create_sign(['id'=>$ticket['downstream_ticket_id']],$ticket['downstream_token']); $data = array_merge($data,$sign); $res = IdcsmartTicketLogic::commonCurl(rtrim($ticket['downstream_url'],'/')."/console/v1/api/ticket/{$ticket['downstream_ticket_id']}/reply", $data, 30, 'PUT'); if ($res['status']==200){ active_log(lang_plugins('ticket_log_admin_update_downstream_ticket_reply_success', ['{admin}'=>'admin#'.request()->admin_id.'#' .request()->admin_name.'#','{ticket}'=>'ticket#'.$ticket['ticket_num'],'{ticket_reply_id}'=>$ticketReply['id'],'{url}'=>$ticket['downstream_url']]), 'addon_idcsmart_ticket', $ticket['id']); }else{ active_log(lang_plugins('ticket_log_admin_update_downstream_ticket_reply_fail', ['{admin}'=>'admin#'.request()->admin_id.'#' .request()->admin_name.'#','{ticket}'=>'ticket#'.$ticket['ticket_num'],'{ticket_reply_id}'=>$ticketReply['id'],'{url}'=>$ticket['downstream_url'],'{reason}'=>$res['msg']]), 'addon_idcsmart_ticket', $ticket['id']); } } return true; } // 推送工单回复删除至下游 public static function pushTicketReplyDelete(IdcsmartTicketModel $ticket,IdcsmartTicketReplyModel $ticketReply,$param) { if (self::checkPushTicketToDownstream($ticket)){ $data = [ 'ticket_reply_id' => $ticketReply['downstream_ticket_reply_id'], ]; $sign = create_sign(['id'=>$ticket['downstream_ticket_id']],$ticket['downstream_token']); $data = array_merge($data,$sign); $res = IdcsmartTicketLogic::commonCurl(rtrim($ticket['downstream_url'],'/')."/console/v1/api/ticket/{$ticket['downstream_ticket_id']}/reply", $data, 30, 'DELETE'); if ($res['status']==200){ active_log(lang_plugins('ticket_log_admin_update_downstream_ticket_reply_delete_success', ['{admin}'=>'admin#'.request()->admin_id.'#' .request()->admin_name.'#','{ticket}'=>'ticket#'.$ticket['ticket_num'],'{ticket_reply_id}'=>$ticketReply['id'],'{url}'=>$ticket['downstream_url']]), 'addon_idcsmart_ticket', $ticket['id']); }else{ active_log(lang_plugins('ticket_log_admin_update_downstream_ticket_reply_delete_fail', ['{admin}'=>'admin#'.request()->admin_id.'#' .request()->admin_name.'#','{ticket}'=>'ticket#'.$ticket['ticket_num'],'{ticket_reply_id}'=>$ticketReply['id'],'{url}'=>$ticket['downstream_url'],'{reason}'=>$res['msg']]), 'addon_idcsmart_ticket', $ticket['id']); } } return true; } // 推送工单回复至下游 public static function pushTicketReplyCreate(IdcsmartTicketModel $ticket,IdcsmartTicketReplyModel $ticketReply,$param) { if (self::checkPushTicketToDownstream($ticket)){ $data = [ 'content' => htmlspecialchars_decode($ticketReply['content']), 'upstream_ticket_reply_id' => $ticketReply['id'], 'attachment' => [], ]; $sign = create_sign(['id'=>$ticket['downstream_ticket_id']],$ticket['downstream_token']); $data = array_merge($data,$sign); $res = IdcsmartTicketLogic::commonCurl(rtrim($ticket['downstream_url'],'/')."/console/v1/api/ticket/{$ticket['downstream_ticket_id']}/reply", $data, 30, 'POST'); if ($res['status']==200){ $ticketReply->save([ 'is_downstream' => 1, 'downstream_ticket_reply_id' => $res['data']['ticket_reply_id'], 'update_time' => time() ]); active_log(lang_plugins('ticket_log_admin_update_downstream_ticket_reply_create_success', ['{admin}'=>'admin#'.request()->admin_id.'#' .request()->admin_name.'#','{ticket}'=>'ticket#'.$ticket['ticket_num'],'{ticket_reply_id}'=>$ticketReply['id'],'{url}'=>$ticket['downstream_url']]), 'addon_idcsmart_ticket', $ticket['id']); }else{ active_log(lang_plugins('ticket_log_admin_update_downstream_ticket_reply_create_fail', ['{admin}'=>'admin#'.request()->admin_id.'#' .request()->admin_name.'#','{ticket}'=>'ticket#'.$ticket['ticket_num'],'{ticket_reply_id}'=>$ticketReply['id'],'{url}'=>$ticket['downstream_url'],'{reason}'=>$res['msg']]), 'addon_idcsmart_ticket', $ticket['id']); } } return true; } }