|
@@ -33,7 +33,8 @@ class ProductSurveillanceJobs implements ShouldQueue
|
|
|
public $tries = 3; // 限制重试次数
|
|
public $tries = 3; // 限制重试次数
|
|
|
public $timeout = 600; // 10分钟超时
|
|
public $timeout = 600; // 10分钟超时
|
|
|
|
|
|
|
|
- protected $message_data;
|
|
|
|
|
|
|
+ protected $message_data;
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Create a new job instance.
|
|
* Create a new job instance.
|
|
|
*
|
|
*
|
|
@@ -214,9 +215,11 @@ class ProductSurveillanceJobs implements ShouldQueue
|
|
|
$config_violation_product_content,
|
|
$config_violation_product_content,
|
|
|
$cleaned_violation_product_content,
|
|
$cleaned_violation_product_content,
|
|
|
$config_violation_store_content,
|
|
$config_violation_store_content,
|
|
|
- $cleaned_violation_store_content
|
|
|
|
|
|
|
+ $cleaned_violation_store_content,
|
|
|
|
|
+ $start_time_string,
|
|
|
|
|
+ $end_time_string
|
|
|
);
|
|
);
|
|
|
- // echo $email_content_html; exit;
|
|
|
|
|
|
|
+ // echo $email_content_html;exit;
|
|
|
//发送预警通知
|
|
//发送预警通知
|
|
|
foreach ($email_notice_list as $email_notice) {
|
|
foreach ($email_notice_list as $email_notice) {
|
|
|
$message_data = [
|
|
$message_data = [
|
|
@@ -227,16 +230,12 @@ class ProductSurveillanceJobs implements ShouldQueue
|
|
|
}
|
|
}
|
|
|
//记录日志
|
|
//记录日志
|
|
|
} catch (\Exception $e) {
|
|
} catch (\Exception $e) {
|
|
|
- print_r($e->getMessage());
|
|
|
|
|
- exit;
|
|
|
|
|
Log::info('job_error', '低价商品数据数据-监控采集清洗情况队列失败', ['data' => $this->message_data, 'error' => $e->getMessage()]);
|
|
Log::info('job_error', '低价商品数据数据-监控采集清洗情况队列失败', ['data' => $this->message_data, 'error' => $e->getMessage()]);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 构建美观的邮件HTML内容
|
|
|
|
|
|
|
+ * 构建美观的邮件HTML内容(内联样式版本)
|
|
|
*/
|
|
*/
|
|
|
private function buildEmailHtml(
|
|
private function buildEmailHtml(
|
|
|
$config_collect,
|
|
$config_collect,
|
|
@@ -246,182 +245,76 @@ class ProductSurveillanceJobs implements ShouldQueue
|
|
|
$config_violation_product,
|
|
$config_violation_product,
|
|
|
$cleaned_violation_product,
|
|
$cleaned_violation_product,
|
|
|
$config_violation_store,
|
|
$config_violation_store,
|
|
|
- $cleaned_violation_store
|
|
|
|
|
|
|
+ $cleaned_violation_store,
|
|
|
|
|
+ $start_time_string,
|
|
|
|
|
+ $end_time_string
|
|
|
) {
|
|
) {
|
|
|
$html = '
|
|
$html = '
|
|
|
- <!DOCTYPE html>
|
|
|
|
|
- <html>
|
|
|
|
|
- <head>
|
|
|
|
|
- <meta charset="UTF-8">
|
|
|
|
|
- <style>
|
|
|
|
|
- body {
|
|
|
|
|
- font-family: "Microsoft YaHei", Arial, sans-serif;
|
|
|
|
|
- background-color: #f5f7fa;
|
|
|
|
|
- margin: 0;
|
|
|
|
|
- padding: 20px;
|
|
|
|
|
- }
|
|
|
|
|
- .email-container {
|
|
|
|
|
- max-width: 800px;
|
|
|
|
|
- margin: 0 auto;
|
|
|
|
|
- background-color: #ffffff;
|
|
|
|
|
- border-radius: 12px;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- box-shadow: 0 2px 12px rgba(0,0,0,0.1);
|
|
|
|
|
- }
|
|
|
|
|
- .email-header {
|
|
|
|
|
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
|
|
- color: white;
|
|
|
|
|
- padding: 30px 20px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- }
|
|
|
|
|
- .email-header h1 {
|
|
|
|
|
- margin: 0;
|
|
|
|
|
- font-size: 24px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- }
|
|
|
|
|
- .email-header p {
|
|
|
|
|
- margin: 10px 0 0;
|
|
|
|
|
- opacity: 0.9;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- }
|
|
|
|
|
- .email-body {
|
|
|
|
|
- padding: 30px 25px;
|
|
|
|
|
- }
|
|
|
|
|
- .section {
|
|
|
|
|
- margin-bottom: 25px;
|
|
|
|
|
- border-left: 4px solid;
|
|
|
|
|
- padding: 12px 20px;
|
|
|
|
|
- background-color: #f8f9fc;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- }
|
|
|
|
|
- .section-title {
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- margin-bottom: 12px;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- }
|
|
|
|
|
- .section-title .icon {
|
|
|
|
|
- font-size: 20px;
|
|
|
|
|
- }
|
|
|
|
|
- .section-content {
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- color: #555;
|
|
|
|
|
- line-height: 1.8;
|
|
|
|
|
- word-break: break-all;
|
|
|
|
|
- }
|
|
|
|
|
- .product-item {
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- background: #e8ecf1;
|
|
|
|
|
- padding: 4px 12px;
|
|
|
|
|
- border-radius: 20px;
|
|
|
|
|
- margin: 4px 6px 4px 0;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
- }
|
|
|
|
|
- .product-item strong {
|
|
|
|
|
- color: #667eea;
|
|
|
|
|
- }
|
|
|
|
|
- .empty-data {
|
|
|
|
|
- color: #999;
|
|
|
|
|
- font-style: italic;
|
|
|
|
|
- }
|
|
|
|
|
- .separator {
|
|
|
|
|
- border-top: 1px dashed #e0e4e8;
|
|
|
|
|
- margin: 20px 0;
|
|
|
|
|
- }
|
|
|
|
|
- .email-footer {
|
|
|
|
|
- background-color: #f8f9fc;
|
|
|
|
|
- padding: 15px 25px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- color: #999;
|
|
|
|
|
- border-top: 1px solid #eef2f6;
|
|
|
|
|
- }
|
|
|
|
|
- .badge {
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- background: #667eea;
|
|
|
|
|
- color: white;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- padding: 2px 8px;
|
|
|
|
|
- border-radius: 12px;
|
|
|
|
|
- margin-left: 8px;
|
|
|
|
|
- }
|
|
|
|
|
- .stats-number {
|
|
|
|
|
- font-weight: 700;
|
|
|
|
|
- color: #667eea;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- }
|
|
|
|
|
- </style>
|
|
|
|
|
- </head>
|
|
|
|
|
- <body>
|
|
|
|
|
- <div class="email-container">
|
|
|
|
|
- <div class="email-header">
|
|
|
|
|
- <h1>数据监控报告</h1>
|
|
|
|
|
- <p>采集与清洗情况通知</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="email-body">
|
|
|
|
|
- ' . $this->buildSection('🎯 配置采集商品任务', 'config_collect', $config_collect, '#667eea') . '
|
|
|
|
|
- ' . $this->buildSection('✅ 实际采集到的商品', 'actual_collect', $actual_collect, '#48bb78') . '
|
|
|
|
|
-
|
|
|
|
|
- <div class="separator"></div>
|
|
|
|
|
-
|
|
|
|
|
- ' . $this->buildSection('⚙️ 配置的低价清洗商品', 'config_low', $config_low, '#ed8936') . '
|
|
|
|
|
- ' . $this->buildSection('🔍 实际清洗出来的低价商品', 'cleaned_low', $cleaned_low, '#ed8936') . '
|
|
|
|
|
-
|
|
|
|
|
- <div class="separator"></div>
|
|
|
|
|
-
|
|
|
|
|
- ' . $this->buildSection('⚠️ 配置的违规清洗商品', 'config_violation_product', $config_violation_product, '#e53e3e') . '
|
|
|
|
|
- ' . $this->buildSection('🚫 实际清洗出来的违规商品', 'cleaned_violation_product', $cleaned_violation_product, '#e53e3e') . '
|
|
|
|
|
-
|
|
|
|
|
- <div class="separator"></div>
|
|
|
|
|
-
|
|
|
|
|
- ' . $this->buildSection('🏪 配置的违规店铺', 'config_violation_store', $config_violation_store, '#9b59b6') . '
|
|
|
|
|
- ' . $this->buildSection('🚷 实际清洗出来的违规店铺', 'cleaned_violation_store', $cleaned_violation_store, '#9b59b6') . '
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="email-footer">
|
|
|
|
|
- <p>此邮件由系统自动发送,请勿直接回复 | 数据统计时间:' . date('Y-m-d H:i:s') . '</p>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </body>
|
|
|
|
|
- </html>';
|
|
|
|
|
|
|
+ <!DOCTYPE html>
|
|
|
|
|
+ <html>
|
|
|
|
|
+ <head>
|
|
|
|
|
+ <meta charset="UTF-8">
|
|
|
|
|
+ </head>
|
|
|
|
|
+ <body style="font-family: \'Microsoft YaHei\', Arial, sans-serif; background-color: #f5f7fa; margin: 0; padding: 20px;">
|
|
|
|
|
+ <div style="max-width: 800px; margin: 0 auto; background-color: #ffffff; border-radius: 12px; overflow: hidden;">
|
|
|
|
|
+ <div style="background: #667eea; color: white; padding: 30px 20px; text-align: center;">
|
|
|
|
|
+ <h1 style="margin: 0; font-size: 24px; font-weight: 600;">数据监控报告</h1>
|
|
|
|
|
+ <p style="margin: 10px 0 0; opacity: 0.9; font-size: 14px;">' . $start_time_string . ' - ' . $end_time_string . ' 采集与清洗情况通知</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style="padding: 30px 25px;">
|
|
|
|
|
+ ' . $this->buildSectionInline('🎯 配置采集商品任务', $config_collect, '#667eea') . '
|
|
|
|
|
+ ' . $this->buildSectionInline('✅ 实际采集到的商品', $actual_collect, '#48bb78') . '
|
|
|
|
|
+ <div style="border-top: 1px dashed #e0e4e8; margin: 20px 0;"></div>
|
|
|
|
|
+ ' . $this->buildSectionInline('⚙️ 配置的低价清洗商品', $config_low, '#ed8936') . '
|
|
|
|
|
+ ' . $this->buildSectionInline('🔍 实际清洗出来的低价商品', $cleaned_low, '#ed8936') . '
|
|
|
|
|
+ <div style="border-top: 1px dashed #e0e4e8; margin: 20px 0;"></div>
|
|
|
|
|
+ ' . $this->buildSectionInline('⚠️ 配置的违规清洗商品', $config_violation_product, '#e53e3e') . '
|
|
|
|
|
+ ' . $this->buildSectionInline('🚫 实际清洗出来的违规商品', $cleaned_violation_product, '#e53e3e') . '
|
|
|
|
|
+ <div style="border-top: 1px dashed #e0e4e8; margin: 20px 0;"></div>
|
|
|
|
|
+ ' . $this->buildSectionInline('🏪 配置的违规店铺', $config_violation_store, '#9b59b6') . '
|
|
|
|
|
+ ' . $this->buildSectionInline('🚷 实际清洗出来的违规店铺', $cleaned_violation_store, '#9b59b6') . '
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div style="background-color: #f8f9fc; padding: 15px 25px; text-align: center; font-size: 12px; color: #999; border-top: 1px solid #eef2f6;">
|
|
|
|
|
+ <p style="margin: 0;">此邮件由系统自动发送,请勿直接回复 | 数据统计时间:' . date('Y-m-d H:i:s') . '</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </body>
|
|
|
|
|
+ </html>';
|
|
|
|
|
|
|
|
return $html;
|
|
return $html;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 构建单个数据区块
|
|
|
|
|
|
|
+ * 构建单个数据区块(内联样式版本)
|
|
|
*/
|
|
*/
|
|
|
- private function buildSection($title, $type, $content, $color)
|
|
|
|
|
|
|
+ private function buildSectionInline($title, $content, $color)
|
|
|
{
|
|
{
|
|
|
- $icon = $this->getIconByType($type);
|
|
|
|
|
- $formattedContent = $this->formatContent($content);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ $formattedContent = $this->formatContentInline($content);
|
|
|
|
|
+
|
|
|
return '
|
|
return '
|
|
|
- <div class="section" style="border-left-color: ' . $color . ';">
|
|
|
|
|
- <div class="section-title">
|
|
|
|
|
- <span class="icon"></span>
|
|
|
|
|
- <span>' . $title . '</span>
|
|
|
|
|
|
|
+ <div style="margin-bottom: 25px; border-left: 4px solid ' . $color . '; padding: 12px 20px; background-color: #f8f9fc; border-radius: 8px;">
|
|
|
|
|
+ <div style="font-size: 16px; font-weight: 600; margin-bottom: 12px;">
|
|
|
|
|
+ ' . htmlspecialchars($title) . '
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="section-content">
|
|
|
|
|
|
|
+ <div style="font-size: 14px; color: #555; line-height: 1.8; word-break: break-all;">
|
|
|
' . $formattedContent . '
|
|
' . $formattedContent . '
|
|
|
</div>
|
|
</div>
|
|
|
</div>';
|
|
</div>';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 格式化内容,将分号分隔的数据转换为美观的标签形式
|
|
|
|
|
|
|
+ * 格式化内容(内联样式版本)
|
|
|
*/
|
|
*/
|
|
|
- private function formatContent($content)
|
|
|
|
|
|
|
+ private function formatContentInline($content)
|
|
|
{
|
|
{
|
|
|
// 如果内容为空或者只包含分号
|
|
// 如果内容为空或者只包含分号
|
|
|
if (empty($content) || trim($content) === ';' || trim($content) === '无;') {
|
|
if (empty($content) || trim($content) === ';' || trim($content) === '无;') {
|
|
|
- return '<span class="empty-data">📭 暂无数据</span>';
|
|
|
|
|
|
|
+ return '<span style="color: #999; font-style: italic;">📭 暂无数据</span>';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 检查是否包含"无;"的情况
|
|
// 检查是否包含"无;"的情况
|
|
|
if (strpos($content, '无;') !== false && strlen($content) <= 6) {
|
|
if (strpos($content, '无;') !== false && strlen($content) <= 6) {
|
|
|
- return '<span class="empty-data">📭 暂无数据</span>';
|
|
|
|
|
|
|
+ return '<span style="color: #999; font-style: italic;">📭 暂无数据</span>';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 移除末尾的分号
|
|
// 移除末尾的分号
|
|
@@ -442,39 +335,18 @@ class ProductSurveillanceJobs implements ShouldQueue
|
|
|
if (preg_match('/^(.+):数量(\d+)$/u', $item, $matches)) {
|
|
if (preg_match('/^(.+):数量(\d+)$/u', $item, $matches)) {
|
|
|
$productName = trim($matches[1]);
|
|
$productName = trim($matches[1]);
|
|
|
$count = $matches[2];
|
|
$count = $matches[2];
|
|
|
- $html .= '<span class="product-item"><strong>' . htmlspecialchars($productName) . '</strong> <span class="stats-number">' . $count . '</span> 件</span>';
|
|
|
|
|
|
|
+ $html .= '<span style="display: inline-block; background: #e8ecf1; padding: 4px 12px; border-radius: 20px; margin: 4px 6px 4px 0; font-size: 13px;"><strong style="color: #667eea;">' . htmlspecialchars($productName) . '</strong> <strong style="font-weight: 700; color: #667eea; font-size: 16px;">' . $count . '</strong> 件</span>';
|
|
|
} else {
|
|
} else {
|
|
|
// 普通商品名
|
|
// 普通商品名
|
|
|
- $html .= '<span class="product-item">' . htmlspecialchars($item) . '</span>';
|
|
|
|
|
|
|
+ $html .= '<span style="display: inline-block; background: #e8ecf1; padding: 4px 12px; border-radius: 20px; margin: 4px 6px 4px 0; font-size: 13px;">' . htmlspecialchars($item) . '</span>';
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return $html ?: '<span class="empty-data">📭 暂无数据</span>';
|
|
|
|
|
|
|
+ return $html ?: '<span style="color: #999; font-style: italic;">📭 暂无数据</span>';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 根据类型获取对应的图标
|
|
|
|
|
- */
|
|
|
|
|
- private function getIconByType($type)
|
|
|
|
|
- {
|
|
|
|
|
- $icons = [
|
|
|
|
|
- 'config_collect' => '📋',
|
|
|
|
|
- 'actual_collect' => '📦',
|
|
|
|
|
- 'config_low' => '💰',
|
|
|
|
|
- 'cleaned_low' => '💎',
|
|
|
|
|
- 'config_violation_product' => '⚠️',
|
|
|
|
|
- 'cleaned_violation_product' => '🚫',
|
|
|
|
|
- 'config_violation_store' => '🏪',
|
|
|
|
|
- 'cleaned_violation_store' => '🔨',
|
|
|
|
|
- ];
|
|
|
|
|
-
|
|
|
|
|
- return $icons[$type] ?? '📌';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
public function failed(\Throwable $exception)
|
|
public function failed(\Throwable $exception)
|
|
|
{
|
|
{
|
|
|
Log::info('job_error', '低价商品数据数据-监控采集清洗情况队列完全失败', ['data' => $this->message_data, 'error' => $exception->getMessage()]);
|
|
Log::info('job_error', '低价商品数据数据-监控采集清洗情况队列完全失败', ['data' => $this->message_data, 'error' => $exception->getMessage()]);
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
+}
|