Merge branch 'develop' into feature/mzl_dev

# Conflicts:
#	.gitignore
#	composer.lock
This commit is contained in:
mzl
2025-12-02 14:17:25 +08:00
parent d53f18181a
commit 32a7752f8b
21 changed files with 838 additions and 35 deletions
+1 -1
View File
@@ -11,4 +11,4 @@
/start.php /start.php
/windows.php /windows.php
/bootstrap.php /bootstrap.php
/composer.lock /composer.lock
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\InquiryTransmitConfigService;
class InquiryTransmitConfigController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new InquiryTransmitConfigService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
@@ -0,0 +1,280 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\model\InquiryTransmit;
use app\common\model\InquiryTransmitConfig;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\InquiryTransmitService;
use support\Log;
use support\Request;
class InquiryTransmitController extends BaseController
{
use CrudControllerTrait;
protected $noNeedLogin = ['form'];
protected $noNeedHeaderData = ['form'];
protected $service;
public function __construct()
{
$this->service = new InquiryTransmitService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
// }
public function form(Request $request)
{
try {
$params = $request->all();
$url = $request->header('referer');
$domain = parse_url($url, PHP_URL_HOST);
$transmitConfig = InquiryTransmitConfig::where('domain', $domain)->first();
if ($transmitConfig) {
$mailto = $transmitConfig->mailto;
$fromDomain = $domain;
} else {
$mailto = '1696136552@qq.com';
$fromDomain = 'referer Failed to obtain';
}
if ($request->method() == 'GET') {
$config = [
'iLang' => 'en',
'Act' => 'Inquiry',
'post_url' => getenv('MAIN_DOMAIN').'/inquiry/transmit/form'.'?m=Data&a=save',
'mailto' => $mailto,
'fromDomain' => $fromDomain,
'fromPath' => parse_url($params['fromlink'], PHP_URL_PATH),
'm' => $params['m'],
'a' => $params['a'],
'tid' => $params['tid'],
'cid' => $params['cid'],
'version' => $params['version'],
'fromlink' => $params['fromlink']
];
$html = match ($params['tid']) {
'en-5-LP-pnx' => $this->formHmtl($config),
'en-3-P-vdx' => $this->formHmtl2($config)
};
return $this->success(['html' => $html], 'success');
// return json([
// 'code' => 200,
// 'data' => [
// 'html' => $html
// ],
// 'message' => 'success'
// ]);
}
if ($request->method() == 'POST') {
$ip = $request->getRealIp($safe_mode=true);
$edata = [
'mailto' => $params['mailto'],
'Date' => date('Y-m-d H:i:s', time()),
'Email' => $params['userEMail'],
'Name' => $params['userName'],
'Tel' => $params['userTel'],
'Company' => '',
'Country' => $params['uCountry'],
'Form Url' => $params['fromlink'],
'Message' => $params['Message'],
'Ip' => $ip,
'IpLocation' => ip2region($ip).'(仅供参考)',
'iLang' => $params['iLang'],
'mailto' => $params['mailto'],
'navigator_language' => $params['navigator_language'],
'navigator_platform' => $params['navigator_platform'],
'screen_width' => $params['screen_width'],
'screen_height' => $params['screen_height'],
'uCountry' => $params['uCountry']
];
$this->service->serviceStore($request, $edata, $domain);
$this->emailHtml($edata);
return $this->success([], 'Thank you for your message! We will contact with you soon.');
// return json([
// 'code' => 200,
// 'data' => [],
// 'message' => 'Thank you for your message! We will contact with you soon.'
// ]);
}
} catch (\Throwable $th) {
Log::info('Data:'.date('Y-m-d H:i:s').':'.$th->getMessage().'|'.$th->getLine().'|'.$th->getFile());
}
}
public function formHmtl($config)
{
// https://v1.xzgoogle.com/form.php?m=Temp&a=getTpl&tid=en-5-LP-pnx&cid=760&version=v4&fromlink=https://magicbridgeauto.com/contact-us/
$html = '<form class="xz-form-x xz-form-pnx" action="'.$config['post_url'].'"> <style> .xz-form-pnx { --c-theme: #BB0010; --c-border: #f0f0f0; --c-bg: #fff; --c-label: #333; --c-font: #666; --c-green: #00B160; --size: 1.6rem; --gap: 1.25em; --h: 3em; --lh: 1.8em; --pd: .4em .625em; --br: 4px; --btn-w: 18.75em; --btn-jc: center; --lines: 4; } .xz-form-pnx { font-size: var(--size); } .xz-form-pnx .xz-form-infos { display: grid; grid-template-columns: 1fr 1fr; grid-gap: var(--gap); margin-bottom: var(--gap); } .xz-form-pnx .xz-form-info, .xz-form-pnx .xz-form-msg { border: solid 1px var(--c-border); background: var(--c-bg); padding: var(--pd); display: flex; align-items: center; border-radius: var(--br); box-sizing: border-box; } .xz-form-pnx .xz-form-info { height: var(--h); } .xz-form-pnx .xz-form-msg { align-items: flex-start; } .xz-form-pnx .xz-form-input, .xz-form-pnx .xz-form-textarea { width: 100%; border: none; font-size: var(--size); line-height: var(--lh); outline: 0; padding: 0; margin-left: 1em; color: var(--c-font); } .xz-form-pnx .xz-form-textarea { height: calc(var(--lh) * var(--lines)); min-height: var(--h); font-family: inherit; resize: none; } .xz-form-pnx .xz-form-label { display: block; color: var(--c-label); line-height: var(--lh); white-space: nowrap; } .xz-form-pnx .xz-form-label.required:after { content: "*"; margin-left: .625em; color: var(--c-theme); } .xz-form-pnx .xz-form-btns { margin-top: var(--gap); display: flex; justify-content: var(--btn-jc); } .xz-form-pnx .xz-form-button { font-size: var(--size); border-radius: var(--br); display: inline-flex; justify-content: center; align-items: center; width: var(--btn-w); height: var(--h); background: var(--c-theme); color: #fff; border: none; transition: all .3s; display: flex; justify-content: center; align-items: center; gap: 1em; } .xz-form-pnx .xz-form-button path { fill: #fff; } .xz-form-pnx .xz-form-button:hover { filter: brightness(1.1); } .xz-form-pnx .xz-form-button:active { transform: scale(.98); } @media screen and (max-width: 500px){ .xz-form-pnx { --gap: .8em; } .xz-form-pnx .xz-form-infos { grid-template-columns: 1fr; } .xz-form-pnx .xz-form-button { width: 100%; } } .xzform_o { position: relative; } .xz-form-pnx .xz-form-message.success { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); z-index: 9; max-width: 75%; display: flex; text-align: center; align-items: center; padding: 4em; background: rgba(255,255,255,.8); color: var(--c-green); font-size: 1.125em; line-height: 1.8em; font-weight: 500; box-shadow: 0 .5rem 3rem -.5rem rgba(0,0,0,.3); } </style> <input type="hidden" name="iLang" value="'.$config['iLang'].'"> <input type="hidden" name="Act" value="'.$config['Act'].'"> <input type="hidden" name="mailto" value="'.$config['mailto'].'"> <input type="hidden" name="customerId" value="'.$config['cid'].'"> <input type="hidden" name="fromDomain" value="'.$config['fromDomain'].'"> <input type="hidden" name="fromlink" value="'.$config['fromlink'].'"> <input type="hidden" name="fromPath" value="'.$config['fromPath'].'"> <input type="hidden" name="navigator.language"> <input type="hidden" name="navigator.platform"> <input type="hidden" name="screen.width"> <input type="hidden" name="screen.height"> <div class="xz-form-infos"> <div class="xz-form-info"> <label class="xz-form-label" for="userName-pnx">Name</label> <input type="text" name="userName" id="userName-pnx" maxlength="80" class="xz-form-input" required> </div> <div class="xz-form-info"> <label class="xz-form-label" for="uCountry-pnx">Country/Region</label> <input type="text" name="uCountry" id="uCountry-pnx" maxlength="80" class="xz-form-input" required> </div> <div class="xz-form-info"> <label class="xz-form-label" for="userEMail-pnx">Email</label> <input type="email" name="userEMail" id="userEMail-pnx" maxlength="80" class="xz-form-input"> </div> <div class="xz-form-info"> <label class="xz-form-label" for="userTel-pnx">Mobile</label> <input type="tel" name="userTel" id="userTel-pnx" maxlength="80" class="xz-form-input" required placeholder="Or Whatsapp/Skype"> </div> </div> <div class="xz-form-msg"> <label class="xz-form-label" for="Message-pnx">Message</label> <textarea name="Message" id="Message-pnx" class="xz-form-textarea" required></textarea> </div> <div class="xz-form-btns"> <button class="xz-form-button"> Submit <svg t="1664258923484" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3462" width="20" height="20"> <path d="M871.04 89.770667L120.064 380.16a51.2 51.2 0 0 0-1.792 94.762667l303.36 130.56 131.072 303.957333a51.2 51.2 0 0 0 94.805333-1.877333l289.792-751.573334a51.2 51.2 0 0 0-66.261333-66.133333z m-41.130667 107.392l-231.978666 601.642666-97.962667-227.114666-3.584-7.338667a85.333333 85.333333 0 0 0-41.045333-37.248l-226.56-97.536 601.173333-232.405333z" p-id="3463"></path> </svg> </button> </div></form>';
return $html;
}
public function formHmtl2($config)
{
// https://v1.xzgoogle.com/form.php?m=Temp&a=getTpl&tid=en-3-P-vdx&cid=760&version=v4&fromlink=https://magicbridgeauto.com/news/
$html = '<form class="xz-form-x xz-form-vdx" action="'.$config['post_url'].'"> <style> .xz-form-vdx { --c-theme: #2C9F1C; --c-border: #f8f8f8; --c-bg: #f8f8f8; --c-font: #333; --c-green: #2C9F1C; --size: 1.6rem; --gap: .625em; --h: 3.5em; --lh: 1.25em; --pd: 1em; --br: .625em; --btn-w: 100%; --lines: 1; font-size: var(--size); } .xz-form-vdx .xz-form-infos { display: grid; grid-template-columns: 1fr; grid-gap: var(--gap); margin-bottom: var(--gap); } .xz-form-vdx .xz-form-info, .xz-form-vdx .xz-form-msg { border: solid 1px var(--c-border); background: var(--c-bg); padding: var(--pd); display: flex; align-items: center; border-radius: var(--br); box-sizing: border-box; } .xz-form-vdx .xz-form-info { height: var(--h); } .xz-form-vdx .xz-form-input, .xz-form-vdx .xz-form-textarea { width: 100%; border: none; font-size: var(--size); line-height: var(--lh); outline: 0; padding: 0; color: var(--c-font); background: transparent; } .xz-form-vdx .xz-form-textarea { height: calc(var(--lh) * var(--lines)); font-family: inherit; resize: none; } .xz-form-vdx .xz-form-btns { margin-top: var(--gap); display: flex; justify-content: var(--btn-jc); } .xz-form-vdx .xz-form-button { font-size: var(--size); border-radius: var(--br); display: inline-flex; justify-content: center; align-items: center; width: var(--btn-w); height: var(--h); background: var(--c-theme); color: #fff; border: none; transition: all .3s; display: flex; justify-content: center; align-items: center; } .xz-form-vdx .xz-form-button:hover { filter: brightness(1.1); } .xz-form-vdx .xz-form-button:active { transform: scale(.98); } .xzform_o { position: relative; } .xz-form-vdx .xz-form-message.success { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 9; max-width: 90%; display: flex; text-align: center; align-items: center; padding: 2em; background: rgba(255, 255, 255, .8); color: var(--c-green); font-size: 1em; line-height: 1.5em; box-shadow: 0 .5rem 3rem -.5rem rgba(0, 0, 0, .3); } </style> <input type="hidden" name="iLang" value="'.$config['iLang'].'"> <input type="hidden" name="Act" value="'.$config['Act'].'"> <input type="hidden" name="mailto" value="'.$config['mailto'].'"> <input type="hidden" name="customerId" value="'.$config['cid'].'"> <input type="hidden" name="fromDomain" value="'.$config['fromDomain'].'"> <input type="hidden" name="fromlink" value="'.$config['fromlink'].'"> <input type="hidden" name="fromPath" value="'.$config['fromPath'].'"> <input type="hidden" name="navigator.language"> <input type="hidden" name="navigator.platform"> <input type="hidden" name="screen.width"> <input type="hidden" name="screen.height"> <div class="xz-form-infos"> <div class="xz-form-info"> <input type="text" name="userName" id="userName-vdx" maxlength="80" class="xz-form-input" placeholder="Name"> </div> <div class="xz-form-info"> <input type="email" name="userEMail" id="userEMail-vdx" maxlength="80" class="xz-form-input" placeholder="Email"> </div> </div> <div class="xz-form-msg"> <textarea name="Message" id="Message-vdx" class="xz-form-textarea" placeholder="What can we do for you? *" required></textarea> </div> <div class="xz-form-btns"> <button class="xz-form-button"> SUBMIT <svg t="1690535811615" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5056" width="1em" height="1em" fill="#fff"> <path d="M851.2 166.4c-12.8-6.4-25.6-6.4-32 0l-640 320C166.4 486.4 160 499.2 160 512s6.4 25.6 12.8 25.6l153.6 96c12.8 6.4 25.6 6.4 38.4-6.4L704 320l12.8 6.4-307.2 326.4c-6.4 6.4-6.4 12.8-6.4 19.2l0 140.8c0 12.8 6.4 25.6 19.2 32 12.8 6.4 25.6 0 32-6.4l76.8-76.8 153.6 102.4c6.4 6.4 12.8 6.4 19.2 6.4 6.4 0 6.4 0 12.8 0 12.8-6.4 19.2-12.8 19.2-25.6l128-640C864 185.6 864 172.8 851.2 166.4z" p-id="5057"></path> </svg> </button> </div></form>';
return $html;
}
public function emailHtml($data)
{
$html = '<!doctype html>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Lead Preview</title>
<meta name="x-apple-disable-message-reformatting">
</head>
<body style="margin:0;padding:0;background:#f6f7f9;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="background:#f6f7f9;">
<tr>
<td align="center" style="padding:24px 12px;">
<!-- container -->
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="600" style="width:600px; max-width:100%; background:#ffffff; border:1px solid #e5e7eb; border-radius:8px;">
<!-- header -->
<tr>
<td style="padding:16px 20px; border-bottom:1px solid #e5e7eb; font:700 18px/1.4 Arial, Helvetica, sans-serif; color:#111;">
Preview
</td>
</tr>
<!-- Preview section -->
<tr>
<td style="padding:18px 20px;">
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
<!-- 一行:左 label / 右 value -->
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">Date:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">'.$data['Date'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">Email:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">
<a href="mailto:muhseen2222@yaho.com" style="color:#2563eb; text-decoration:none;">'.$data['Email'].'</a>
</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">Name:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;" dir="auto">'.$data['Name'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">Tel:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">'.$data['Tel'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">Company:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#666;"></td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">Country:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;" dir="auto">'.$data['Country'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">Form Url:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222; word-break:break-word;">
<a href="'.$data['Form Url'].'" style="color:#2563eb; text-decoration:none;">'.$data['Form Url'].'</a>
</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">Message:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;" dir="auto">'.$data['Message'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">Ip:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">'.$data['Ip'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">IpLocation:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">'.$data['IpLocation'].'</td>
</tr>
</table>
</td>
</tr>
<!-- divider + More -->
<tr>
<td style="padding:0 20px;">
<div style="height:1px; background:#e5e7eb; line-height:1px; font-size:0;">&nbsp;</div>
</td>
</tr>
<tr>
<td style="padding:14px 20px 0; font:700 18px Arial, Helvetica, sans-serif; color:#111;">More</td>
</tr>
<tr>
<td style="padding:6px 20px 18px;">
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">iLang:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">'.$data['iLang'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">mailto:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">
<a href="mailto:info@borderlesscar.com" style="color:#2563eb; text-decoration:none;">'.$data['mailto'].'</a>
</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">navigator_language:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">'.$data['navigator_language'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">navigator_platform:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">'.$data['navigator_platform'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">screen_width:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">'.$data['screen_width'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">screen_height:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;">'.$data['screen_height'].'</td>
</tr>
<tr>
<td style="width:140px; padding:6px 0; font:700 14px Arial, Helvetica, sans-serif; color:#111;">uCountry:</td>
<td style="padding:6px 0; font:14px Arial, Helvetica, sans-serif; color:#222;" dir="auto">'.$data['uCountry'].'</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- /container -->
</td>
</tr>
</table>
</body>
</html>';
// $email = new Email();
//return $email->send($data['mailto'], 'Inquiry_Of_'.$data['Name'], $html);
return \app\common\utils\email\MailSend::send($data['mailto'], 'Inquiry_Of_'.$data['Name'], $html);
}
}
+49 -23
View File
@@ -328,38 +328,64 @@ class BrandSeriesService
// 执行抓取 // 执行抓取
$crawlService = new CrawlService(); $crawlService = new CrawlService();
//懂车帝模糊搜索返回车系地址 //懂车帝模糊搜索返回车系地址|单个车系id抓取
$href = $crawlService->modelSearch($keyword); $href = $crawlService->modelSearch($keyword);
$seriesId = $this->extractSeriesId($href); $seriesId = $this->extractSeriesId($href); //懂车帝车系id
if (empty($seriesId)) { // if (empty($seriesId)) { //没有抓取到懂车帝系列id时,按关键词为品牌名抓取所有的系列名
// $crawlSeries = $crawlService->brandSeries($keyword);
// }
//按关键词为品牌名抓取所有的系列名
$crawlSeries = $crawlService->brandSeries($keyword);
if (empty($seriesId) && empty($crawlSeries)) {
throw new \Exception('抓取失败,未找到该系列车型'); throw new \Exception('抓取失败,未找到该系列车型');
} }
//抓取品牌与系列 $brandSerieses = [];
$brandSeries = $crawlService->crawlBrandSeries($seriesId); if(empty($seriesId) && !empty($crawlSeries) || count($crawlSeries)>1){ //关键词为品牌名时抓取所有车系的懂车帝id
$href = $crawlService->modelSearch($crawlSeries[0]);
$seriesId = $this->extractSeriesId($href); //懂车帝车系id
// 验证品牌是否存在 //抓取品牌与系列
$brand = Brand::where('brand_name', $brandSeries['brand'])->first(); $brandSeries = $crawlService->crawlBrandSeries($seriesId);
// 获取品牌信息 foreach ($crawlSeries as $item) {
$series = BrandSeries::where('series_name', $brandSeries['series'])->first(); // $brandSerieses[] = $crawlService->brandSeries($keyword);
$result['brand'] = ['brand_name' => $brandSeries['brand'], 'exists' => $brand?1:0]; $brandSerieses[] = ['brand' => $brandSeries['brand'], 'series' => $item];
if($brand){
$result['brand']['id'] = $brand['id'];
}
$result['series'] = ['series_name' => $brandSeries['series'], 'exists' => $series?1:0];
if($series){
$result['series']['id'] = $series['id'];
if(empty($result['brand']['id'])){
$result['brand']['id'] = $series['brand_id'];
} }
}elseif (!empty($seriesId) && empty($crawlSeries)){
//抓取品牌与系列
$brandSerieses[] = $crawlService->crawlBrandSeries($seriesId);
} }
$result = [];
try { try {
//获取车型数据 foreach ($brandSerieses as $brandSeries) {
$result['model'] = $this->crawlSeriesModel($seriesId); $item = [];
return $result; // 验证品牌是否存在
$brand = Brand::where('brand_name', $brandSeries['brand'])->first();
// 获取品牌信息
$series = BrandSeries::where('series_name', $brandSeries['series'])->first();
$item['brand'] = ['brand_name' => $brandSeries['brand'], 'exists' => $brand ? 1 : 0];
if ($brand) {
$item['brand']['id'] = $brand['id'];
}
$item['series'] = ['series_name' => $brandSeries['series'], 'exists' => $series ? 1 : 0];
if ($series) {
$item['series']['id'] = $series['id'];
if (empty($item['brand']['id'])) {
$item['brand']['id'] = $series['brand_id'];
}
}
//获取车型数据
$item['model'] = [];
if (count($brandSerieses)<2) {
$item['model'] = $this->crawlSeriesModel($seriesId);
}
$result[] = $item;
}
return ['is_series' => (count($brandSerieses)<2)?1:0, 'data' => $result];
} catch (\Exception $e) { } catch (\Exception $e) {
throw new \Exception($e->getMessage()); throw new \Exception($e->getMessage());
} }
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\InquiryTransmitConfig;
class InquiryTransmitConfigLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = InquiryTransmitConfig::class;
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\InquiryTransmit;
class InquiryTransmitLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = InquiryTransmit::class;
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class InquiryTransmit extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'inquiry_transmit';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'date', 'email', 'name', 'tel', 'company', 'country', 'domain', 'form_url', 'message', 'ip', 'ip_location', 'ilang', 'mailto', 'navigator_language', 'navigator_platform', 'screen_width', 'screen_height', 'uCountry', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
@@ -0,0 +1,49 @@
<?php
namespace app\common\model;
use support\Model;
class InquiryTransmitConfig extends Model
{
/**
* The connection name for the model.
*
* @var string|null
*/
protected $connection = 'mysql';
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'inquiry_transmit_config';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'domain', 'mailto', 'lang', 'status', 'remark', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+1 -1
View File
@@ -9,7 +9,7 @@ class CrawlService
{ {
public function brandSeries($brand) public function brandSeries($brand)
{ {
$url = "https://www.dongchedi.com/search?currTab=6&keyword=".$brand; $url = "https://www.dongchedi.com/search?currTab=6&keyword=".$brand.'&from=search_tab_er&api_param={"en_qc":+0}';
$ql = QueryList::get($url ); $ql = QueryList::get($url );
$series = []; $series = [];
$ql->find('div.card-name')->texts()->map(function($item) use(&$series) { $ql->find('div.card-name')->texts()->map(function($item) use(&$series) {
@@ -0,0 +1,67 @@
<?php
namespace app\common\service;
use app\common\validate\InquiryTransmitConfigValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\InquiryTransmitConfig;
use support\Request;
class InquiryTransmitConfigService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = InquiryTransmitConfig::class;
/**
* 验证器
*/
protected $validator = InquiryTransmitConfigValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['where']['status'] = 1;
$config['order']['id'] = 'desc';
break;
case 'index':
$config['where']['status'] = 1;
$config['order']['id'] = 'desc';
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
}
@@ -0,0 +1,92 @@
<?php
namespace app\common\service;
use app\common\validate\InquiryTransmitValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\InquiryTransmit;
use support\Request;
class InquiryTransmitService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = InquiryTransmit::class;
/**
* 验证器
*/
protected $validator = InquiryTransmitValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/** 创建 */
public function serviceStore(Request $request, array $data = [], string $domain = '')
{
$data['email'] = $data['Email'];
$data['name'] = $data['Name'];
$data['tel'] = $data['Tel'];
$data['company'] = $data['Company'];
$data['country'] = $data['Country'];
$data['form_Url'] = $data['Form Url'];
$data['message'] = $data['Message'];
$data['ip_location'] = $data['IpLocation'];
$data['ip'] = $data['Ip'];
$data['domain'] = $domain;
return InquiryTransmit::create($data);
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['order']['id'] = 'desc';
break;
case 'index':
// 域名
if (isset($p['domain']) && !empty($p['domain'])) {
$config['where']['domain'] = $p['domain'];
}
// 关键字
if (isset($p['keyword']) && $p['keyword']) {
$k = '%' . $p['keyword'] . '%';
foreach (['email', 'name', 'tel', 'country', 'message', 'form_url'] as $col) {
$config['orWhere'][] = [$col, 'like', $k];
}
}
$config['order']['id'] = 'desc';
break;
case 'show':
$config['where']['id'] = $p['id'];
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
}
+4 -1
View File
@@ -434,9 +434,12 @@ class Emails
$channel = 12; $channel = 12;
$channelInfo = InquiryChannel::where('id', $channel)->first(); $channelInfo = InquiryChannel::where('id', $channel)->first();
// 从库实时获取
$users = \app\common\model\User::where('corp_id', 3)->where('status', 1)->orderBy('id')->pluck('id')->toArray();
// 初始化 UID 轮询器 // 初始化 UID 轮询器
$uidRotator = new \app\common\utils\RedisUidRotator('email_parsed_result_20250804'); $uidRotator = new \app\common\utils\RedisUidRotator('email_parsed_result_20250804');
$uidRotator->setUids([64, 63, 93, 108, 132, 145, 150, 143, 156, 157, 158], false); $uidRotator->setUids($users, false);
EmailParsedResult::where('status', 0) EmailParsedResult::where('status', 0)
->chunkById($batchSize, function ($records) use ($delaySeconds, $throttleAfter, $throttleSeconds, $enterUser, $channelInfo, $uidRotator) { ->chunkById($batchSize, function ($records) use ($delaySeconds, $throttleAfter, $throttleSeconds, $enterUser, $channelInfo, $uidRotator) {
@@ -0,0 +1,29 @@
<?php
namespace app\common\validate;
use think\Validate;
class InquiryTransmitConfigValidate extends Validate
{
// 规则
protected $rule = [
];
// 提示
protected $message = [
];
// 场景
protected $scene = [
'store' => [
],
'update' => [
]
];
}
@@ -0,0 +1,29 @@
<?php
namespace app\common\validate;
use think\Validate;
class InquiryTransmitValidate extends Validate
{
// 规则
protected $rule = [
];
// 提示
protected $message = [
];
// 场景
protected $scene = [
'store' => [
],
'update' => [
]
];
}
@@ -435,6 +435,11 @@ class CustomerController extends BaseController
{ {
$id = Util::getParam($request, 'id'); $id = Util::getParam($request, 'id');
$scene = Util::getParam($request, 'scene',''); $scene = Util::getParam($request, 'scene','');
$status_desc = Util::getParam($request, 'status_desc','');
if($scene == 'force' && empty($status_desc)){
return $this->error( '移除原因不能为空');
}
$userId = $request->user->uid; $userId = $request->user->uid;
$where = [ $where = [
@@ -450,6 +455,7 @@ class CustomerController extends BaseController
if ($scene == 'force') { if ($scene == 'force') {
//status=5:业务员自己删除 //status=5:业务员自己删除
$customer->status = 5; $customer->status = 5;
$customer->status_desc = $status_desc;
$res = $customer->save(); $res = $customer->save();
}else{ }else{
$customer->user_id = 0; $customer->user_id = 0;
@@ -144,6 +144,12 @@ class CustomerController extends BaseController
{ {
try { try {
$param = $request->all(); $param = $request->all();
$scene = $param['scene'] ?? '';
$status_desc = $param['status_desc'] ?? '';
if($scene != 'force' && empty($param['status_desc'])){
return $this->error( '移除原因不能为空');
}
if (empty($param['customer_id'])) { if (empty($param['customer_id'])) {
return $this->error('customer_id'.trans('not_null')); return $this->error('customer_id'.trans('not_null'));
} }
@@ -153,13 +159,12 @@ class CustomerController extends BaseController
if(!$query->exists()){ if(!$query->exists()){
return $this->error( trans('fail')); return $this->error( trans('fail'));
} }
$scene = $param['scene'] ?? '';
if ($scene === 'list') { //管理端客户列表移除 if ($scene === 'list') { //管理端客户列表移除
$res = $query->update(['status' => 4, 'deleted_at' => time()]); $res = $query->update(['status' => 4, 'status_desc' => $status_desc, 'deleted_at' => time()]);
} elseif ($scene === 'force') { //强删除 } elseif ($scene === 'force') { //强删除
$res = $query->forceDelete(); $res = $query->forceDelete();
} else { //公海软删除 } else { //公海软删除
$res = $query->update(['status' => 3, 'deleted_at' => time()]); $res = $query->update(['status' => 3, 'status_desc' => $status_desc, 'deleted_at' => time()]);
} }
return $this->success($res); return $this->success($res);
+2 -1
View File
@@ -21,12 +21,13 @@ class CustomerLogic
return $res; return $res;
} }
public function forceDelete(int $id) public function forceDelete(int $id ,string $status_desc = '')
{ {
$customer = Customer::withTrashed()->find($id); $customer = Customer::withTrashed()->find($id);
//强制删除 //强制删除
// $res = $customer->forceDelete(); // $res = $customer->forceDelete();
$customer->status = 3; $customer->status = 3;
$customer->status_desc = $status_desc;
$res = $customer->save(); $res = $customer->save();
return $res; return $res;
} }
+1 -1
View File
@@ -325,7 +325,7 @@ class CustomerService
$audit->status = 3; $audit->status = 3;
$audit->save(); $audit->save();
} }
return $logic->forceDelete($params['id']); return $logic->forceDelete($params['id'],$audit->remark);
} else { } else {
if(!empty($audit)){ if(!empty($audit)){
$audit->status = 2; $audit->status = 2;
+2 -1
View File
@@ -47,7 +47,8 @@
"phpmailer/phpmailer": "^6.10", "phpmailer/phpmailer": "^6.10",
"webman/gateway-worker": "^1.0", "webman/gateway-worker": "^1.0",
"mpdf/mpdf": "^8.2", "mpdf/mpdf": "^8.2",
"jaeger/querylist": "^4.4" "jaeger/querylist": "^4.4",
"zoujingli/ip2region": "3.0"
}, },
"suggest": { "suggest": {
"ext-event": "For better performance. " "ext-event": "For better performance. "
Generated
+79 -3
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "865e38bae84e7de0c95098cc87135440", "content-hash": "ac2fa5a625069729975d072614509e1f",
"packages": [ "packages": [
{ {
"name": "aliyuncs/oss-sdk-php", "name": "aliyuncs/oss-sdk-php",
@@ -18,7 +18,13 @@
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aliyun/aliyun-oss-php-sdk/zipball/483dd0b8bff5d47f0e4ffc99f6077a295c5ccbb5", "url": "https://api.github.com/repos/aliyun/aliyun-oss-php-sdk/zipball/483dd0b8bff5d47f0e4ffc99f6077a295c5ccbb5",
"reference": "483dd0b8bff5d47f0e4ffc99f6077a295c5ccbb5", "reference": "483dd0b8bff5d47f0e4ffc99f6077a295c5ccbb5",
"shasum": "" "shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
}, },
"require": { "require": {
"php": ">=5.3" "php": ">=5.3"
@@ -7129,7 +7135,77 @@
"type": "patreon" "type": "patreon"
} }
], ],
"time": "2025-11-12T01:36:20+00:00" "time": "2025-06-12T13:34:04+00:00"
},
{
"name": "zoujingli/ip2region",
"version": "v3.0.0",
"source": {
"type": "git",
"url": "https://github.com/zoujingli/ip2region.git",
"reference": "fd31d7a023f28f4e2f3e63f46f1fd51f8b2672f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zoujingli/ip2region/zipball/fd31d7a023f28f4e2f3e63f46f1fd51f8b2672f6",
"reference": "fd31d7a023f28f4e2f3e63f46f1fd51f8b2672f6",
"shasum": ""
},
"require": {
"php": ">=5.4"
},
"suggest": {
"ext-opcache": "启用 OPcache 以获得更好的性能"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
},
"autoload": {
"files": [
"ip2region.php"
],
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Anyon",
"email": "zoujingli@qq.com",
"homepage": "https://thinkadmin.top"
}
],
"description": "Ip2Region v3.0 for PHP - 企业级 IP 地理位置查询库,支持 IPv4/IPv6,分片文件管理,智能缓存,零依赖",
"homepage": "https://github.com/zoujingli/Ip2Region",
"keywords": [
"IP",
"IP定位",
"Ip2Region",
"geoip",
"geolocation",
"ipv4",
"ipv6",
"location",
"企业级",
"分片数据库",
"压缩",
"地理位置",
"地理位置查询",
"缓存",
"高性能"
],
"support": {
"issues": "https://github.com/zoujingli/ip2region/issues",
"source": "https://github.com/zoujingli/ip2region/tree/v3.0.0"
},
"time": "2025-09-15T12:27:22+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],
+23
View File
@@ -39,6 +39,11 @@ Route::any('/upload', [app\common\controller\UploadController::class, 'uploadFil
Route::any('/upload/image', [app\common\controller\UploadController::class, 'uploadImage']); Route::any('/upload/image', [app\common\controller\UploadController::class, 'uploadImage']);
Route::any('/upload/delete', [app\common\controller\UploadController::class, 'deleteFile']); Route::any('/upload/delete', [app\common\controller\UploadController::class, 'deleteFile']);
// 询单转发
Route::group('/inquiry/transmit', function () {
Route::any('/form', [app\admin\controller\InquiryTransmitController::class, 'form']);
});
// 公共 // 公共
Route::group('/common', function () { Route::group('/common', function () {
Route::group('/auth', function () { Route::group('/auth', function () {
@@ -215,6 +220,24 @@ Route::group('/admin', function () {
}); });
}); });
// 询单转发
Route::group('/inquiry', function () {
Route::group('/transmit', function () {
Route::any('/list', [app\admin\controller\InquiryTransmitController::class, 'index']);
Route::any('/show', [app\admin\controller\InquiryTransmitController::class, 'show']);
Route::any('/store', [app\admin\controller\InquiryTransmitController::class, 'store']);
Route::any('/update', [app\admin\controller\InquiryTransmitController::class, 'update']);
Route::any('/destroy', [app\admin\controller\InquiryTransmitController::class, 'destroy']);
});
Route::group('/config', function () {
Route::any('/list', [app\admin\controller\InquiryTransmitConfigController::class, 'index']);
Route::any('/show', [app\admin\controller\InquiryTransmitConfigController::class, 'show']);
Route::any('/store', [app\admin\controller\InquiryTransmitConfigController::class, 'store']);
Route::any('/update', [app\admin\controller\InquiryTransmitConfigController::class, 'update']);
Route::any('/destroy', [app\admin\controller\InquiryTransmitConfigController::class, 'destroy']);
});
});
// crm // crm
Route::group('/crm', function () { Route::group('/crm', function () {
Route::group('/customer', function () { Route::group('/customer', function () {