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('
' . $message . '
')
->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 = <<
{$suffix}
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) . '' . 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 . '
';
}
}
}
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 . '
';
}
}
}
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' => '' . $statusLang . '',
'h5' => '[' . $statusLang . ']',
'original' => $statusLang,
'number' => $status
];
}
}