1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249 |
- <?php
- namespace addons\workorder\library;
- use think\Db;
- use think\Exception;
- use think\exception\PDOException;
- use think\Lang;
- use think\Validate;
- use app\common\library\Email;
- use app\common\library\Sms as Smslib;
- use addons\workorder\model\Orders;
- use addons\workorder\model\Records;
- use addons\workorder\model\Engineer;
- class General
- {
- protected static $instance = null;
- protected static $fileField = ['image', 'images', 'file', 'files'];
- protected static $imgArr = ['jpg', 'png', 'bmp', 'jpeg', 'gif'];
- protected $workorderConfig = [];
- public function __construct()
- {
- $this->workorderConfig = get_addon_config('workorder');
- $this->workorderConfig['notice_mail'] = isset($this->workorderConfig['notice_mail']) ? explode(',', $this->workorderConfig['notice_mail']) : [];
- $this->workorderConfig['notice_sms'] = isset($this->workorderConfig['notice_sms']) ? explode(',', $this->workorderConfig['notice_sms']) : [];
- Lang::load(ROOT_PATH . 'addons' . DS . 'workorder' . DS . 'lang' . DS . \think\Request::instance()->langset() . '.php');
- }
- public static function instance($options = [])
- {
- if (is_null(self::$instance)) {
- self::$instance = new static($options);
- }
- return self::$instance;
- }
- /**
- * 计算一个工程师的平均回复时间
- * 通常在有新的回复时调用
- * @param int $engineerId 工程师ID
- * @return float
- * @throws \think\Exception
- * @throws \think\exception\PDOException
- */
- public static function calcEngineerAvgResTime($engineerId)
- {
- $workorderTimeStatistics = Db::name('workorder_time_statistics')
- ->alias('s')
- ->join('workorder_orders o', 'o.id=s.order_id')
- ->where('o.deletetime', null)
- ->where('s.type', 'in', '1,2')
- ->where('s.engineer_id', $engineerId)
- ->where('s.time_consum', '>', '0')
- ->avg('s.time_consum');
- Db::name('workorder_engineers')->where('id', $engineerId)->update([
- 'avg_response_time' => ceil($workorderTimeStatistics)
- ]);
- return $workorderTimeStatistics;
- }
- public static function orderNumberChangeCalcEngineerStatistics($orderId, $engineer = false, $type = 'del')
- {
- if ($engineer) {
- self::calcEngineerAvgResTime($engineer);
- }
- // 沟通记录
- $records = Records::where('order_id', $orderId)
- ->order('createtime asc')
- ->select();
- // 共有哪些工程师
- $engineers = [];
- if ($engineer) {
- $engineers[$engineer] = $engineer;
- }
- foreach ($records as $index => $record) {
- if ($record->engineer_id) {
- $engineers[$record->engineer_id] = $record->engineer_id;
- }
- }
- $engineers = Engineer::select($engineers);
- Db::startTrans();
- try {
- if ($type == 'del') {
- foreach ($engineers as $item) {
- $item->work_order_quantity--;
- $item->save();
- }
- } elseif ($type == 'restore') {
- foreach ($engineers as $item) {
- $item->work_order_quantity++;
- $item->save();
- }
- }
- Db::commit();
- } catch (PDOException | Exception $e) {
- Db::rollback();
- return false;
- }
- return true;
- }
- public function autoClose()
- {
- if ($this->workorderConfig['auto_close'] == 0) {
- return true;
- }
- $nowTime = time();
- $orders = Orders::all(['status' => '3']);
- foreach ($orders as $index => $order) {
- $userLastRecordTime = Records::where('order_id', $order->id)
- ->where('user_id', '>', 0)
- ->order('createtime desc')
- ->value('createtime');
- $autoCloseTime = $userLastRecordTime + (int)($this->workorderConfig['auto_close'] * 3600);
- if ($autoCloseTime <= time()) {
- $order->status = '4';
- $order->save();
- $record = [
- 'order_id' => $order->id,
- 'engineer_id' => $order->engineer_id,
- 'message_type' => 3,
- 'message' => __('The job has been closed automatically!')
- ];
- Records::create($record);
- // 记录结单耗时
- $timeStatistics = Db::name('workorder_time_statistics')
- ->where('order_id', $order->id)
- ->where('type', 0)
- ->where('time_consum', null)
- ->find();
- if ($timeStatistics) {
- Db::name('workorder_time_statistics')->where('id', $timeStatistics['id'])->update([
- 'endtime' => $nowTime,
- 'time_consum' => $nowTime - $timeStatistics['starttime'],
- 'engineer_id' => $order->engineer_id,// 防转移,冲正为当前工程师
- ]);
- }
- }
- }
- return true;
- }
- /**
- * 发送邮件通知
- * @param object $row 工单
- * @param string $event 事件
- * @return bool
- * @throws \think\exception\DbException
- */
- public function mailNotice($row, $event, $subject, $message)
- {
- if (!in_array($event, $this->workorderConfig['notice_mail'])) {
- return false;
- }
- if ($event == 'user_order_handle' || $event == 'user_got_reply') {
- if (!isset($row->email) || !isset($row->remind) || !$row->email || ($row->remind != 2)) {
- return false;
- }
- } else {
- if (!$row->engineer_id) {
- return false;
- }
- $row = \addons\workorder\model\Engineer::get($row->engineer_id);
- $row->email = $row->user->email;
- }
- if (!Validate::is($row->email, "email")) {
- return false;
- }
- $email = new Email;
- $result = $email->to($row->email)
- ->subject($subject)
- ->message('<div style="min-height:550px; padding: 100px 55px 200px;">' . $message . '</div>')
- ->send();
- if ($result) {
- return true;
- } else {
- //$email->getError();
- return false;
- }
- }
- /**
- * 发送短信通知
- * @param object $row 工单
- * @param string $event 事件
- * @return bool
- * @throws \think\exception\DbException
- */
- public function smsNotice($row, $event)
- {
- if (!in_array($event, $this->workorderConfig['notice_sms'])) {
- return false;
- }
- // 最多12个字符
- $msg = mb_strlen($row->title) > 12 ? mb_substr($row->title, 0, 9) . '...' : $row->title;
- // 兼容短信宝和创蓝
- $smsbao = get_addon_info('smsbao');
- $clsms = get_addon_info('clsms');
- if (($smsbao && $smsbao['state'] == 1) || ($clsms && $clsms['state'] == 1)) {
- switch ($event) {
- case 'user_order_handle':
- $msg = __('The engineer has viewed the work order you submitted:%s and is processing it.', [$msg]);
- break;
- case 'user_got_reply':
- $msg = __('The engineer has replied to your work order:%s, please check the feedback in time.', [$msg]);
- break;
- case 'engineer_new_order':
- $msg = __('Dear engineer, you have received a new work order:%s, please handle it in time.', [$msg]);
- break;
- case 'engineer_got_reply':
- $msg = __('Dear engineer, work order:%s, user feedback has been received, please handle it in time.', [$msg]);
- break;
- case 'engineer_urging':
- $msg = __('Dear engineer, work order:%s, the user hopes you will reply as soon as possible.', [$msg]);
- break;
- default:
- $msg = __('Sending scenario not recognized by the work order system');
- break;
- }
- }
- if ($event == 'user_order_handle' || $event == 'user_got_reply') {
- if (!isset($row->mobile) || !isset($row->remind) || !$row->mobile || ($row->remind != 1)) {
- return false;
- }
- } else {
- if (!$row->engineer_id) {
- return false;
- }
- $row = \addons\workorder\model\Engineer::get($row->engineer_id);
- $row->mobile = $row->user->mobile;
- }
- if (!$row->mobile || !Validate::regex($row->mobile, "^1\d{10}$")) {
- return false;
- }
- if (!\think\Hook::get('sms_send')) {
- return false;
- }
- // 兼容创蓝
- if ($clsms && $clsms['state'] == 1) {
- $clsms = new \addons\clsms\library\Clsms();
- $result = $clsms->smstype(0)->mobile($row->mobile)->msg($msg)->send();
- if ($result) {
- return true;
- } else {
- return false;
- }
- }
- // 兼容阿里云短信
- $alisms = get_addon_info('alisms');
- if ($alisms && $alisms['state'] == 1) {
- $params = [
- 'mobile' => $row->mobile,
- 'msg' => [
- 'title' => $msg
- ],
- 'event' => $event
- ];
- $ret = \think\Hook::listen('sms_notice', $params, null, true);
- } else {
- $ret = Smslib::notice($row->mobile, $msg, $event);
- }
- if ($ret) {
- return true;
- } else {
- return false;
- }
- }
- /**
- * 获取文件后缀
- * @param string $filename 文件路径/名称
- * @return string
- */
- public static function getFileExtension($filename)
- {
- $filename = explode('.', $filename);
- return end($filename);
- }
- /**
- * 生成文件后缀图片
- * @param string $suffix 后缀
- * @param null $background
- * @return string
- */
- public static function buildSuffixImage($suffix, $background = null)
- {
- $suffix = mb_substr(strtoupper($suffix), 0, 4);
- $total = unpack('L', hash('adler32', $suffix, true))[1];
- $hue = $total % 360;
- list($r, $g, $b) = hsv2rgb($hue / 360, 0.3, 0.9);
- $background = $background ? $background : "rgb({$r},{$g},{$b})";
- $icon = <<<EOT
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
- <path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
- <path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
- <polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
- <path style="fill:{$background};" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16 V416z"/>
- <path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
- <g><text><tspan x="220" y="380" font-size="124" font-family="Verdana, Helvetica, Arial, sans-serif" fill="white" text-anchor="middle">{$suffix}</tspan></text></g>
- </svg>
- EOT;
- return $icon;
- }
- /**
- * 清理xss
- * @param string $val
- * @return string
- */
- public static function removeXss($val)
- {
- if (!$val) {
- return '';
- }
- if (function_exists('xss_clean')) {
- return xss_clean($val);
- }
- $val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
- $search = 'abcdefghijklmnopqrstuvwxyz';
- $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
- $search .= '1234567890!@#$%^&*()';
- $search .= '~`";:?+/={}[]-_|\'\\';
- for ($i = 0; $i < strlen($search); $i++) {
- $val = preg_replace('/(&#[xX]0{0,8}' . dechex(ord($search[$i])) . ';?)/i', $search[$i], $val); // with a ;
- $val = preg_replace('/(�{0,8}' . ord($search[$i]) . ';?)/', $search[$i], $val); // with a ;
- }
- $ra1 = [
- 'javascript',
- 'vbscript',
- 'expression',
- 'applet',
- 'meta',
- 'xml',
- 'blink',
- 'link',
- 'style',
- 'script',
- 'embed',
- 'object',
- 'iframe',
- 'frame',
- 'frameset',
- 'ilayer',
- 'layer',
- 'bgsound',
- 'title',
- 'base'
- ];
- $ra2 = [
- 'onabort',
- 'onactivate',
- 'onafterprint',
- 'onafterupdate',
- 'onbeforeactivate',
- 'onbeforecopy',
- 'onbeforecut',
- 'onbeforedeactivate',
- 'onbeforeeditfocus',
- 'onbeforepaste',
- 'onbeforeprint',
- 'onbeforeunload',
- 'onbeforeupdate',
- 'onblur',
- 'onbounce',
- 'oncellchange',
- 'onchange',
- 'onclick',
- 'oncontextmenu',
- 'oncontrolselect',
- 'oncopy',
- 'oncut',
- 'ondataavailable',
- 'ondatasetchanged',
- 'ondatasetcomplete',
- 'ondblclick',
- 'ondeactivate',
- 'ondrag',
- 'ondragend',
- 'ondragenter',
- 'ondragleave',
- 'ondragover',
- 'ondragstart',
- 'ondrop',
- 'onerror',
- 'onerrorupdate',
- 'onfilterchange',
- 'onfinish',
- 'onfocus',
- 'onfocusin',
- 'onfocusout',
- 'onhelp',
- 'onkeydown',
- 'onkeypress',
- 'onkeyup',
- 'onlayoutcomplete',
- 'onload',
- 'onlosecapture',
- 'onmousedown',
- 'onmouseenter',
- 'onmouseleave',
- 'onmousemove',
- 'onmouseout',
- 'onmouseover',
- 'onmouseup',
- 'onmousewheel',
- 'onmove',
- 'onmoveend',
- 'onmovestart',
- 'onpaste',
- 'onpropertychange',
- 'onreadystatechange',
- 'onreset',
- 'onresize',
- 'onresizeend',
- 'onresizestart',
- 'onrowenter',
- 'onrowexit',
- 'onrowsdelete',
- 'onrowsinserted',
- 'onscroll',
- 'onselect',
- 'onselectionchange',
- 'onselectstart',
- 'onstart',
- 'onstop',
- 'onsubmit',
- 'onunload'
- ];
- $ra = array_merge($ra1, $ra2);
- $found = true;
- while ($found == true) {
- $val_before = $val;
- for ($i = 0; $i < sizeof($ra); $i++) {
- $pattern = '/';
- for ($j = 0; $j < strlen($ra[$i]); $j++) {
- if ($j > 0) {
- $pattern .= '(';
- $pattern .= '(&#[xX]0{0,8}([9ab]);)';
- $pattern .= '|';
- $pattern .= '|(�{0,8}([9|10|13]);)';
- $pattern .= ')*';
- }
- $pattern .= $ra[$i][$j];
- }
- $pattern .= '/i';
- $replacement = substr($ra[$i], 0, 2) . '<k>' . substr($ra[$i], 2);
- $val = preg_replace($pattern, $replacement, $val);
- if ($val_before == $val) {
- $found = false;
- }
- }
- }
- return $val;
- }
- public static function createOrder($row, $userId)
- {
- $fields = Orders::getFields(null, 0);
- $records = [];
- // 要入沟通记录的字段-所有文件自动入沟通记录
- $otherField = '';
- $recordField = [
- 'describe' => '0',// 描述-富文本
- 'confidential' => '4',// 机密信息-机密信息
- ];
- foreach ($fields as $index => $field) {
- if ($field['isrequire'] && $field['type_list'] != 'text' && (!isset($row[$field['name']]) || $row[$field['name']] == '')) {
- return ['code' => 0, 'msg' => __('%s can not be empty!', $field['title'])];
- }
- if (isset($row[$field['name']]) && is_array($row[$field['name']])) {
- $row[$field['name']] = implode(',', $row[$field['name']]);
- }
- if ($field['type_list'] == 'editor') {
- $fieldOrigin = \think\Request::instance()->param($field['name'], '', 'trim');
- if (!$fieldOrigin) {
- $fieldOrigin = \think\Request::instance()->param('row/a', '', 'trim');
- $fieldOrigin = isset($fieldOrigin[$field['name']]) ? $fieldOrigin[$field['name']] : '';
- }
- $row[$field['name']] = self::removeXss($fieldOrigin);
- }
- // 将uniapp端的城市名与数据库中的进行兼容
- if ($field['type_list'] == 'city' && isset($row[$field['name']]) && $row[$field['name']]) {
- $city = explode('/', $row[$field['name']]);
- if ($city && mb_strpos($city[0], '市') !== false) {
- $city[1] = $city[0];
- $city[0] = mb_substr($city[0], 0, mb_strlen($city[0]) - 1);
- $row[$field['name']] = implode('/', $city);
- }
- }
- if (array_key_exists($field['name'], $recordField) && isset($row[$field['name']]) && $row[$field['name']]) {
- $records[] = [
- 'user_id' => $userId,
- 'message_type' => $recordField[$field['name']],
- 'message' => $row[$field['name']]
- ];
- }
- if (in_array($field['type_list'], self::$fileField) && isset($row[$field['name']]) && $row[$field['name']]) {
- $attachment = explode(',', trim($row[$field['name']], ','));
- $message_type = ($field['type_list'] == 'image' || $field['type_list'] == 'images') ? 1 : 2;
- foreach ($attachment as $item) {
- $itemInfo = pathinfo($item);
- if (in_array($itemInfo['extension'], self::$imgArr)) {
- $message_type = 1;
- }
- $records[] = [
- 'user_id' => $userId,
- 'message_type' => $message_type,
- 'message' => $item
- ];
- }
- }
- // 非聊天记录字段且非文件字段且非基本信息字段,对值进行格式化后写入聊天记录
- if (!array_key_exists($field['name'], $recordField) && !in_array($field['type_list'], self::$fileField) && !$field['isbasicinfo']) {
- $fieldValue = self::fieldValue($field, $row[$field['name']]);
- if ($fieldValue) {
- $otherField .= $field['title'] . ':' . $fieldValue . '<br />';
- }
- }
- }
- if ($otherField) {
- $records[] = [
- 'user_id' => $userId,
- 'message_type' => 0,
- 'message' => $otherField
- ];
- }
- $row['user_id'] = $userId;
- $row['status'] = 0;
- $row['createtime'] = time();
- $row['updatetime'] = time();
- try {
- $order = Orders::create($row);
- if ($records) {
- foreach ($records as $index => $record) {
- $records[$index]['batch'] = 1;
- $records[$index]['order_id'] = $order->id;
- }
- $recordsModel = new Records;
- $recordsModel->saveAll($records);
- }
- // 分派工单
- $General = General::instance();
- $engineerId = $General->distribution($order, $userId);
- // 准备记录结单和首回耗时
- $timeStatistics[0] = [
- 'type' => 0,
- 'order_id' => $order->id,
- 'engineer_id' => $engineerId,
- 'starttime' => time()
- ];
- $timeStatistics[1] = $timeStatistics[0];
- $timeStatistics[1]['type'] = 2;
- Db::name('workorder_time_statistics')->insertAll($timeStatistics);
- } catch (Exception $e) {
- return ['code' => 0, 'msg' => 'error:' . $e->getMessage()];
- }
- return [
- 'code' => 1,
- 'data' => ['id' => $order->id]
- ];
- }
- public static function formatFileSize($num)
- {
- $p = 0;
- $format = 'bytes';
- if ($num > 0 && $num < 1024) {
- $p = 0;
- return number_format($num) . ' ' . $format;
- }
- if ($num >= 1024 && $num < pow(1024, 2)) {
- $p = 1;
- $format = 'KB';
- }
- if ($num >= pow(1024, 2) && $num < pow(1024, 3)) {
- $p = 2;
- $format = 'MB';
- }
- if ($num >= pow(1024, 3) && $num < pow(1024, 4)) {
- $p = 3;
- $format = 'GB';
- }
- if ($num >= pow(1024, 4) && $num < pow(1024, 5)) {
- $p = 3;
- $format = 'TB';
- }
- $num /= pow(1024, $p);
- return number_format($num, 2) . ' ' . $format;
- }
- public function createEvaluate($order, $row, $userId)
- {
- $nowTime = time();
- if (!$row['stars']) {
- return ['code' => 0, 'msg' => __('Please select the overall evaluation~')];
- } elseif (!isset($row['solved'])) {
- return ['code' => 0, 'msg' => __('Please select whether the problem has been solved~')];
- }
- $row['order_id'] = $order->id;
- $row['category_id'] = $order->category_id;
- $row['user_id'] = $userId;
- $row['createtime'] = $nowTime;
- if (Db::name('workorder_evaluate')->insert($row)) {
- $order->status = 5;
- $order->save();
- // 记录结单耗时
- $timeStatistics = Db::name('workorder_time_statistics')
- ->where('order_id', $order->id)
- ->where('type', 0)
- ->where('time_consum', null)
- ->find();
- if ($timeStatistics) {
- Db::name('workorder_time_statistics')->where('id', $timeStatistics['id'])->update([
- 'endtime' => $nowTime,
- 'time_consum' => $nowTime - $timeStatistics['starttime'],
- 'engineer_id' => $order->engineer_id,// 防转移,冲正为当前工程师
- ]);
- }
- return ['code' => 1, 'msg' => __('Evaluation submitted successfully~')];
- } else {
- return ['code' => 0, 'msg' => __('Evaluation failed, please try again!')];
- }
- }
- /**
- * 回复工单
- * @param object $order 工单数据
- * @param object $user 用户数据
- * @param object $engineer 工程师数据
- * @param array $row 回复数据
- * @var $order Orders 实例
- */
- public function createReply($order, $user, $engineer, $row)
- {
- $nowTime = time();
- $replyField = $order->getFields(null, $user ? 1 : 2);
- // 要入沟通记录的字段-所有文件自动入沟通记录
- $recordField = [
- 'reply_describe' => '0',// 描述-富文本
- 'reply_confidential' => '4',// 机密信息-机密信息
- ];
- $records = [];
- $otherField = '';
- foreach ($replyField as $index => $field) {
- if ($field['isrequire'] && (!isset($row[$field['name']]) || $row[$field['name']] == '')) {
- return ['code' => 0, 'msg' => __('%s can not be empty!', $field['title'])];
- }
- if ($field['type_list'] == 'editor') {
- $fieldOrigin = \think\Request::instance()->param($field['name'], '', 'trim');
- if (!$fieldOrigin) {
- $fieldOrigin = \think\Request::instance()->param('row/a', '', 'trim');
- $fieldOrigin = $fieldOrigin[$field['name']] ?? '';
- }
- $row[$field['name']] = self::removeXss($fieldOrigin);
- }
- if (array_key_exists($field['name'], $recordField) && isset($row[$field['name']]) && $row[$field['name']]) {
- $records[] = [
- 'message_type' => $recordField[$field['name']],
- 'message' => $row[$field['name']]
- ];
- }
- if (in_array($field['type_list'], self::$fileField) && isset($row[$field['name']]) && $row[$field['name']]) {
- $attachment = explode(',', trim($row[$field['name']], ','));
- $message_type = ($field['type_list'] == 'image' || $field['type_list'] == 'images') ? 1 : 2;
- foreach ($attachment as $item) {
- $itemInfo = pathinfo($item);
- if (in_array($itemInfo['extension'], self::$imgArr)) {
- $message_type = 1;
- }
- $records[] = [
- 'message_type' => $message_type,
- 'message' => $item
- ];
- }
- }
- // 非回复字段且非文件字段,对值进行格式化后写入聊天记录
- if (!array_key_exists($field['name'], $recordField) && !in_array($field['type_list'], self::$fileField)) {
- $fieldValue = self::fieldValue($field, $row[$field['name']]);
- if ($fieldValue) {
- $otherField .= $field['title'] . ':' . $fieldValue . '<br />';
- }
- }
- }
- if ($otherField) {
- $records[] = [
- 'message_type' => 0,
- 'message' => $otherField
- ];
- }
- $batch = Records::where('order_id', $order->id)->max('batch');
- foreach ($records as $index => $record) {
- $records[$index]['order_id'] = $order->id;
- $records[$index]['batch'] = $batch + 1;
- if ($user) {
- $records[$index]['sender'] = 'user';
- $records[$index]['user_id'] = $user->id;
- $records[$index]['nickname'] = $user->nickname;
- $records[$index]['avatar'] = cdnurl($user->avatar, true);
- } elseif ($engineer) {
- $records[$index]['sender'] = 'engineer';
- $records[$index]['engineer_id'] = $engineer->id;
- $records[$index]['title'] = $engineer->title;
- $records[$index]['avatar'] = ($engineer->user && $engineer->user->avatar) ? cdnurl($engineer->user->avatar, true) : (function_exists('letter_avatar') ? letter_avatar($engineer->title) : cdnurl('/assets/img/avatar.png', true));
- }
- }
- $recordsModel = new Records;
- $records = $recordsModel->allowField(true)->saveAll($records);
- if ($records) {
- $timeStatistics = Db::name('workorder_time_statistics')
- ->where('order_id', $order->id)
- ->where('type', 'in', '1,2')
- ->where('time_consum', null)
- ->find();
- $order->title = $order->title ?? __('Untitled');
- if ($engineer) {
- if ($order->status == 2) {
- $order->status = 3;
- $this->mailNotice($order, 'user_got_reply', __('[new reply received for work order]') . $order->title, __('Work order:%s the engineer has replied to your question. Please check / feed back in time.', [$order->title]));
- $this->smsNotice($order, 'user_got_reply');
- }
- if ($order->lasturgingtime) {
- $order->lasturgingtime = null;
- }
- // 计算回复时间
- if ($timeStatistics) {
- Db::name('workorder_time_statistics')->where('id', $timeStatistics['id'])->update([
- 'endtime' => $nowTime,
- 'time_consum' => $nowTime - $timeStatistics['starttime'],
- 'engineer_id' => $order->engineer_id,// 防转移,冲正为当前工程师
- ]);
- $this->calcEngineerAvgResTime($order->engineer_id);
- }
- } else {
- if ($order->status == 3) {
- $order->status = 2;
- $this->mailNotice($order, 'engineer_got_reply', __('[work order receives new feedback]') . $order->title, __('Work order:%s the user has fed back new information, please check / reply in time.', [$order->title]));
- $this->smsNotice($order, 'engineer_got_reply');
- }
- // 准备记录回复时间
- if (!$timeStatistics) {
- $timeStatistics = [
- 'type' => 1,
- 'order_id' => $order->id,
- 'engineer_id' => $order->engineer_id,
- 'starttime' => time()
- ];
- Db::name('workorder_time_statistics')->insert($timeStatistics);
- }
- }
- $order->allowField(true)->save();
- return [
- 'code' => 1,
- 'msg' => __('Reply Success~'),
- 'data' => [
- 'records' => $records
- ]
- ];
- }
- return ['code' => 0, 'msg' => __('Nothing happened~')];
- }
- public function transfer($order, $transferEngineer)
- {
- $engineer = Engineer::get($transferEngineer);
- if (!$engineer->user_id) {
- return ['code' => 0, 'msg' => __('The engineer has not bound users!')];
- }
- if ($engineer->user_id == $order->user_id) {
- return ['code' => 0, 'msg' => __('Engineer and issuer cannot be the same person!')];
- }
- if ($engineer && $engineer->status == '1') {
- $order->engineer_id = $transferEngineer;
- $order->save();
- $engineer->lastreceivetime = time();
- $engineer->work_order_quantity++;
- $engineer->save();
- $record = [
- 'order_id' => $order->id,
- 'engineer_id' => $transferEngineer,
- 'message_type' => 3,
- 'message' => __('The work order has been transferred to:%s', $engineer->title)
- ];
- Records::create($record);
- return ['code' => 1, 'msg' => __('Work order transferred~')];
- }
- return ['code' => 0, 'msg' => __('Nothing happened~')];
- }
- public function distribution($order, $userId)
- {
- if (isset($order->engineer_id) && $order->engineer_id) {
- return false;
- } elseif (!$order->category_id) {
- return false;
- }
- $distribution_engineer = null;
- $where['status'] = '1';
- $where['user_id'] = ['<>', $userId];
- if ($this->workorderConfig['distribution_type'] == 2) {
- // 技能分派-循环
- $category_engineer = Db::name('workorder_category')->where('id', $order->category_id)->value('we_ids');
- if (!$category_engineer) {
- return false;
- }
- $where['id'] = ['in', $category_engineer];
- $this->workorderConfig['distribution_type'] = 0;
- } elseif ($this->workorderConfig['distribution_type'] == 3) {
- // 技能分派-负载
- $category_engineer = Db::name('workorder_category')->where('id', $order->category_id)->value('we_ids');
- if (!$category_engineer) {
- return false;
- }
- $where['id'] = ['in', $category_engineer];
- $this->workorderConfig['distribution_type'] = 1;
- }
- if ($this->workorderConfig['distribution_type'] == 0) {
- $distribution_engineer = Engineer::where($where)->order('lastreceivetime asc')->find();
- } elseif ($this->workorderConfig['distribution_type'] == 1) {
- $engineer = Engineer::where($where)->select();
- foreach ($engineer as $index => $item) {
- $orderNumber = Orders::where('engineer_id', $item->id)->count();
- if ($orderNumber == 0) {
- $distribution_engineer = $item;
- break;
- } else {
- if (isset($minOrder)) {
- if ($orderNumber < $minOrder) {
- $minOrder = $orderNumber;
- }
- } else {
- $distribution_engineer = $item;
- $minOrder = $orderNumber;
- }
- }
- }
- }
- if (!$distribution_engineer) {
- return false;
- }
- $distribution_engineer->lastreceivetime = time();
- $distribution_engineer->work_order_quantity++;
- $distribution_engineer->save();
- $order->engineer_id = $distribution_engineer->id;
- $order->status = 1;
- $order->save();
- // 发送通知
- if (isset($order->title)) {
- $this->mailNotice($order, 'engineer_new_order', __('[new work order]') . $order->title, __('The user submitted a new job:%s please process it as soon as possible.', [$order->title]));
- $this->smsNotice($order, 'engineer_new_order');
- }
- return $distribution_engineer->id;
- }
- public static function handleUrl($url, $category)
- {
- if (preg_match('/^https?:\/\//i', $url)) {
- if (strpos($url, '?') === false) {
- $url .= '?category=' . $category;
- } else {
- $url .= '&category=' . $category;
- }
- return $url;
- } else {
- return url($url, ['category' => $category]);
- }
- }
- /**
- * 推荐知识点
- */
- public static function recKbs($id, $category = false, $limit = 20)
- {
- $recKbs = [];
- if ($category) {
- $kbsIds = Db::name('workorder_category')
- ->where('id', $category)
- ->where('status', '1')
- ->where('deletetime', null)
- ->value('kbs_ids');
- if ($kbsIds) {
- $recKbs = Db::name('workorder_kbs')
- ->field('id,title,url')
- ->where('id', 'in', $kbsIds)
- ->where('status', '1')
- ->where('deletetime', null)
- ->where('id', '<>', $id)
- ->order('weigh desc')
- ->limit($limit)
- ->select();
- }
- }
- if (!$recKbs) {
- $recKbs = Db::name('workorder_kbs')
- ->field('id,title')
- ->where('status', '1')
- ->where('deletetime', null)
- ->where('id', '<>', $id)
- ->order('weigh desc,views desc')
- ->limit($limit)
- ->select();
- }
- return $recKbs;
- }
- public function engineerViewed($order)
- {
- if ($order->status == 1) {
- $order->status = 2;
- $order->allowField(true)->save();
- $record = [
- 'order_id' => $order->id,
- 'engineer_id' => $order->engineer_id,
- 'message_type' => 3,
- 'message' => __('The engineer has viewed your submitted questions')
- ];
- Records::create($record);
- // 发送通知
- $this->mailNotice($order, 'user_order_handle', __('[work order is already being processed]') . $order->title, __('Work order:%s the engineer has reviewed the problem you submitted and is processing it', [$order->title]));
- $this->smsNotice($order, 'user_order_handle');
- return true;
- }
- return false;
- }
- /**
- * 工单详情处理
- * @var Orders $order
- */
- public function orderInfoHandle($order, $isUser, $isCurrentEngineer)
- {
- $nowTime = time();
- // 下次可催办时间
- $urging_rate = (int)($this->workorderConfig['urging_rate'] * 60);
- $nextUrgingTime = (int)$order->lasturgingtime + $urging_rate;
- $order->title = $order->title ?? __('Untitled');
- // 标记工程师处理中
- if ($isCurrentEngineer) {
- $this->engineerViewed($order);
- }
- $order->urging = false;
- $order->close = false;
- if ($isUser && $order->status <= 2 && $nextUrgingTime <= $nowTime && $order->engineer_id && $this->workorderConfig['user_urging']) {
- $order->urging = true;
- }
- if ($order->status <= 3) {
- $order->close = true;
- }
- $order->status = $this->handleStatus($order->status, $isCurrentEngineer);
- // 处理用户头像
- if ($order->user) {
- $order->user->avatar = $order->user->avatar ? cdnurl($order->user->avatar, true) : (function_exists('letter_avatar') ? letter_avatar($order->user->nickname) : cdnurl('/assets/img/avatar.png', true));
- }
- $basicField = [];// 工单基本信息字段
- $allField = $order->getFields($order, 0);
- foreach ($allField as $index => $field) {
- if ($field['isbasicinfo']) {
- $field['value'] = self::fieldValue($field, $field['value']);
- $basicField[] = $field;
- }
- }
- if ($isCurrentEngineer) {
- $basicField[] = [
- 'title' => __('Submit user'),
- 'value' => $order->user->nickname
- ];
- if (!$this->workorderConfig['engineer_close']) {
- $order->close = false;
- }
- }
- return [
- 'order' => $order,
- 'basicField' => $basicField
- ];
- }
- public static function fieldValue($field, $value)
- {
- if ($field['type_list'] == 'switch') {
- return $value ? __('open') : __('close');
- }
- if ($value == '') {
- return '';
- }
- $listField = ['select', 'selects', 'checkbox', 'radio'];
- if (in_array($field['type_list'], $listField)) {
- if (is_array($field['values_list'])) {
- $valueTmp = '';
- if (!is_array($value)) {
- $value = explode(',', $value);
- }
- foreach ($value as $key => $item) {
- $valueTmp .= isset($field['values_list'][$item]) ? $field['values_list'][$item] . ',' : '';
- }
- return trim($valueTmp, ',');
- }
- }
- return $value;
- }
- /**
- * 关闭工单
- * @return array
- * @var Orders $order
- */
- public function closeOrder($order, $isCurrentEngineer)
- {
- $nowTime = time();
- if ($order->status == 4 || $order->status == 5) {
- return ['code' => 0, 'msg' => __('The work order has been closed!')];
- }
- if ($isCurrentEngineer && ($this->workorderConfig['engineer_close'] == 0)) {
- return ['code' => 0, 'msg' => __('The engineer was not allowed to close the work order~')];
- }
- $order->status = 4;
- $order->save();
- $timeStatistics = Db::name('workorder_time_statistics')
- ->where('order_id', $order->id)
- ->where('type', 0)
- ->where('time_consum', null)
- ->find();
- if ($timeStatistics) {
- // 记录结单耗时
- Db::name('workorder_time_statistics')
- ->where('id', $timeStatistics['id'])
- ->update([
- 'endtime' => $nowTime,
- 'time_consum' => $nowTime - $timeStatistics['starttime'],
- 'engineer_id' => $order->engineer_id,// 防转移,冲正为当前工程师
- ]);
- }
- return ['code' => 1, 'msg' => __('Work order closed successfully~')];
- }
- /**
- * 催单
- * @return array
- * @var Orders $order
- */
- public function urgingOrder($order)
- {
- $nowTime = time();
- if (!$this->workorderConfig['user_urging']) {
- return ['code' => 0, 'msg' => __('Reminder function not enabled~')];
- }
- // 下次可催办时间-在isPost内有使用
- $urging_rate = (int)($this->workorderConfig['urging_rate'] * 60);
- $nextUrgingTime = (int)$order->lasturgingtime + $urging_rate;
- if ($nextUrgingTime <= $nowTime) {
- $order->lasturgingtime = $nowTime;
- $order->save();
- $order->title = $order->title ?? __('Untitled');
- $this->mailNotice($order, 'engineer_urging', __('[work order reminder]') . $order->title, __('Dear engineer, work order:%s, the user hopes you will reply as soon as possible.', [$order->title]));
- $this->smsNotice($order, 'engineer_urging');
- return ['code' => 1, 'msg' => __('Reminder message sent successfully~')];
- } else {
- return ['code' => 0, 'msg' => __('Urge message sent frequently!')];
- }
- }
- /**
- * 获取一个工单的聊天记录和所有工程师
- * @param int $id 工单ID
- * @param int $engineer 工单当前工程师
- * @return array
- */
- public function orderRecords($id, $engineer = 0)
- {
- // 沟通记录
- $records = Records::where('order_id', $id)
- ->order('createtime asc')
- ->select();
- // 共有哪些工程师
- $engineers = [];
- if ($engineer) {
- $engineers[$engineer] = $engineer;
- }
- foreach ($records as $index => $record) {
- if ($record->engineer_id) {
- $engineers[$record->engineer_id] = $record->engineer_id;
- }
- if ($record->engineer && $record->engineer->user) {
- $record->engineer->user->avatar = $record->engineer->user->avatar ? cdnurl($record->engineer->user->avatar, true) : (function_exists('letter_avatar') ? letter_avatar($record->engineer->title) : cdnurl('/assets/img/avatar.png', true));
- }
- }
- $engineers = Engineer::select($engineers);
- $engineerInfoConfig = Engineer::getEngineerInfoConfig();
- foreach ($engineers as $index => $engineer) {
- if (!$engineer->introduce) {
- $engineer->introduce = ($engineer->user && $engineer->user->bio) ? $engineer->user->bio : '';
- }
- $engineer->all_order_number = in_array('all_order_number', $engineerInfoConfig) ? $engineer->all_order_number : false;
- $engineer->wx = in_array('wx', $engineerInfoConfig) ? $engineer->wx : false;
- $engineer->qq = in_array('qq', $engineerInfoConfig) ? $engineer->qq : false;
- if ($engineer->user) {
- $engineer->user->mobile = in_array('mobile', $engineerInfoConfig) ? $engineer->user->mobile : false;
- $engineer->user->email = in_array('email', $engineerInfoConfig) ? $engineer->user->email : false;
- $engineer->user->avatar = $engineer->user->avatar ? cdnurl($engineer->user->avatar, true) : (function_exists('letter_avatar') ? letter_avatar($engineer->title) : cdnurl('/assets/img/avatar.png', true));
- }
- }
- return [
- 'records' => $records,
- 'engineers' => $engineers
- ];
- }
- public function handleStatus($status, $isEngineer = false)
- {
- $colors = [
- 'info',
- 'info',
- 'warning',
- 'danger',
- 'danger',
- 'success'
- ];
- $statusLang = __('Status ' . $status);
- if ($isEngineer) {
- switch ($status) {
- case 3:
- $statusLang = __('Waiting for user feedback');
- break;
- case 4:
- $statusLang = __('To be evaluated by users');
- break;
- case 2:
- $statusLang = __('Waiting for your reply');
- break;
- }
- }
- return [
- 'pc' => '<span class="text-' . $colors[$status] . '">' . $statusLang . '</span>',
- 'h5' => '<span class="text-' . $colors[$status] . '">[' . $statusLang . ']</span>',
- 'original' => $statusLang,
- 'number' => $status
- ];
- }
- }
|