#feature: update log format
This commit is contained in:
@@ -1,73 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\File;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
class CleanScheduledTaskLogsCommand extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'logs:clean-scheduled-tasks {--days=7 : 保留最近几天的日志}';
|
|
||||||
|
|
||||||
protected $description = '清理定时任务日志文件,删除指定天数之前的日志';
|
|
||||||
|
|
||||||
public function handle(): int
|
|
||||||
{
|
|
||||||
$days = (int) $this->option('days');
|
|
||||||
$logPath = storage_path('logs/scheduled-tasks');
|
|
||||||
|
|
||||||
if (!File::exists($logPath)) {
|
|
||||||
Log::info('日志目录不存在,无需清理');
|
|
||||||
return Command::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
$cutoffDate = Carbon::now()->subDays($days);
|
|
||||||
Log::info("开始清理 {$days} 天前的定时任务日志...");
|
|
||||||
Log::info("截止日期: {$cutoffDate->format('Y-m-d')}");
|
|
||||||
|
|
||||||
$files = File::files($logPath);
|
|
||||||
$deletedCount = 0;
|
|
||||||
$totalSize = 0;
|
|
||||||
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$filename = $file->getFilename();
|
|
||||||
|
|
||||||
// 匹配日志文件名格式: task-name-YYYY-MM-DD.log
|
|
||||||
if (preg_match('/-(\d{4}-\d{2}-\d{2})\.log$/', $filename, $matches)) {
|
|
||||||
$fileDate = Carbon::parse($matches[1]);
|
|
||||||
|
|
||||||
if ($fileDate->lt($cutoffDate)) {
|
|
||||||
$fileSize = $file->getSize();
|
|
||||||
$totalSize += $fileSize;
|
|
||||||
|
|
||||||
File::delete($file->getPathname());
|
|
||||||
$deletedCount++;
|
|
||||||
|
|
||||||
Log::info("已删除: {$filename} (" . $this->formatBytes($fileSize) . ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($deletedCount > 0) {
|
|
||||||
Log::info("清理完成!共删除 {$deletedCount} 个日志文件,释放空间: " . $this->formatBytes($totalSize));
|
|
||||||
} else {
|
|
||||||
Log::info('没有需要清理的日志文件');
|
|
||||||
}
|
|
||||||
|
|
||||||
return Command::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatBytes(int $bytes): string
|
|
||||||
{
|
|
||||||
if ($bytes >= 1073741824) {
|
|
||||||
return number_format($bytes / 1073741824, 2) . ' GB';
|
|
||||||
} elseif ($bytes >= 1048576) {
|
|
||||||
return number_format($bytes / 1048576, 2) . ' MB';
|
|
||||||
} elseif ($bytes >= 1024) {
|
|
||||||
return number_format($bytes / 1024, 2) . ' KB';
|
|
||||||
}
|
|
||||||
return $bytes . ' B';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,11 +17,11 @@ class GitMonitorCacheCommand extends Command
|
|||||||
$cache = $monitor->refreshReleaseCache(true);
|
$cache = $monitor->refreshReleaseCache(true);
|
||||||
|
|
||||||
if (empty($cache)) {
|
if (empty($cache)) {
|
||||||
Log::warning('未获取到任何 release 版本信息,请检查配置。');
|
Log::channel('git-monitor')->warning('未获取到任何 release 版本信息,请检查配置。');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info(sprintf(
|
Log::channel('git-monitor')->info(sprintf(
|
||||||
'已缓存 %d 个仓库的 release 分支信息。',
|
'已缓存 %d 个仓库的 release 分支信息。',
|
||||||
count($cache['repositories'] ?? [])
|
count($cache['repositories'] ?? [])
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ class GitMonitorCheckCommand extends Command
|
|||||||
|
|
||||||
foreach ($results as $repo => $result) {
|
foreach ($results as $repo => $result) {
|
||||||
if (isset($result['error'])) {
|
if (isset($result['error'])) {
|
||||||
Log::error(sprintf('[%s] %s', $repo, $result['error']));
|
Log::channel('git-monitor')->error(sprintf('[%s] %s', $repo, $result['error']));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info(sprintf(
|
Log::channel('git-monitor')->info(sprintf(
|
||||||
'[%s] 分支 %s 已对齐 %s,扫描 %d 个提交。',
|
'[%s] 分支 %s 已对齐 %s,扫描 %d 个提交。',
|
||||||
$repo,
|
$repo,
|
||||||
$result['branch'],
|
$result['branch'],
|
||||||
@@ -38,9 +38,9 @@ class GitMonitorCheckCommand extends Command
|
|||||||
));
|
));
|
||||||
|
|
||||||
if (!empty($result['issues']['develop_merges'])) {
|
if (!empty($result['issues']['develop_merges'])) {
|
||||||
Log::warning(sprintf(' - 检测到 %d 个 develop merge:', count($result['issues']['develop_merges'])));
|
Log::channel('git-monitor')->warning(sprintf(' - 检测到 %d 个 develop merge:', count($result['issues']['develop_merges'])));
|
||||||
foreach ($result['issues']['develop_merges'] as $commit) {
|
foreach ($result['issues']['develop_merges'] as $commit) {
|
||||||
Log::warning(sprintf(
|
Log::channel('git-monitor')->warning(sprintf(
|
||||||
' • %s %s (%s)',
|
' • %s %s (%s)',
|
||||||
substr($commit['hash'], 0, 8),
|
substr($commit['hash'], 0, 8),
|
||||||
$commit['subject'],
|
$commit['subject'],
|
||||||
@@ -50,9 +50,9 @@ class GitMonitorCheckCommand extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($result['issues']['missing_functions'])) {
|
if (!empty($result['issues']['missing_functions'])) {
|
||||||
Log::warning(sprintf(' - 检测到 %d 个疑似缺失函数的提交:', count($result['issues']['missing_functions'])));
|
Log::channel('git-monitor')->warning(sprintf(' - 检测到 %d 个疑似缺失函数的提交:', count($result['issues']['missing_functions'])));
|
||||||
foreach ($result['issues']['missing_functions'] as $issue) {
|
foreach ($result['issues']['missing_functions'] as $issue) {
|
||||||
Log::warning(sprintf(
|
Log::channel('git-monitor')->warning(sprintf(
|
||||||
' • %s %s (%s)',
|
' • %s %s (%s)',
|
||||||
substr($issue['commit']['hash'], 0, 8),
|
substr($issue['commit']['hash'], 0, 8),
|
||||||
$issue['commit']['subject'],
|
$issue['commit']['subject'],
|
||||||
@@ -60,7 +60,7 @@ class GitMonitorCheckCommand extends Command
|
|||||||
));
|
));
|
||||||
foreach ($issue['details'] as $detail) {
|
foreach ($issue['details'] as $detail) {
|
||||||
$functions = implode(', ', array_slice($detail['functions'], 0, 5));
|
$functions = implode(', ', array_slice($detail['functions'], 0, 5));
|
||||||
Log::warning(sprintf(' %s => %s', $detail['file'], $functions));
|
Log::channel('git-monitor')->warning(sprintf(' %s => %s', $detail['file'], $functions));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,29 +14,29 @@ class JenkinsMonitorCommand extends Command
|
|||||||
|
|
||||||
public function handle(JenkinsMonitorService $service): void
|
public function handle(JenkinsMonitorService $service): void
|
||||||
{
|
{
|
||||||
Log::info('开始检查 Jenkins 构建...');
|
Log::channel('jenkins-monitor')->info('开始检查 Jenkins 构建...');
|
||||||
|
|
||||||
$results = $service->checkAllProjects();
|
$results = $service->checkAllProjects();
|
||||||
|
|
||||||
if (isset($results['skipped'])) {
|
if (isset($results['skipped'])) {
|
||||||
Log::warning('跳过检查: ' . ($results['reason'] ?? 'unknown'));
|
Log::channel('jenkins-monitor')->warning('跳过检查: ' . ($results['reason'] ?? 'unknown'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($results as $slug => $result) {
|
foreach ($results as $slug => $result) {
|
||||||
if (isset($result['skipped'])) {
|
if (isset($result['skipped'])) {
|
||||||
Log::info(sprintf('[%s] 跳过: %s', $slug, $result['reason'] ?? 'unknown'));
|
Log::channel('jenkins-monitor')->info(sprintf('[%s] 跳过: %s', $slug, $result['reason'] ?? 'unknown'));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$newBuilds = $result['new_builds'] ?? [];
|
$newBuilds = $result['new_builds'] ?? [];
|
||||||
if (empty($newBuilds)) {
|
if (empty($newBuilds)) {
|
||||||
Log::info(sprintf('[%s] 无新构建', $slug));
|
Log::channel('jenkins-monitor')->info(sprintf('[%s] 无新构建', $slug));
|
||||||
} else {
|
} else {
|
||||||
Log::info(sprintf('[%s] 发现 %d 个新构建: #%s', $slug, count($newBuilds), implode(', #', $newBuilds)));
|
Log::channel('jenkins-monitor')->info(sprintf('[%s] 发现 %d 个新构建: #%s', $slug, count($newBuilds), implode(', #', $newBuilds)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info('检查完成');
|
Log::channel('jenkins-monitor')->info('检查完成');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ class LogAnalysisCommand extends Command
|
|||||||
): int {
|
): int {
|
||||||
// 检查配置
|
// 检查配置
|
||||||
if (!$slsService->isConfigured()) {
|
if (!$slsService->isConfigured()) {
|
||||||
Log::error('SLS 服务未配置,请检查 .env 中的 SLS_* 配置项');
|
Log::channel('log-analysis')->error('SLS 服务未配置,请检查 .env 中的 SLS_* 配置项');
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$aiService->isConfigured()) {
|
if (!$aiService->isConfigured()) {
|
||||||
Log::error('AI 服务未配置,请在页面上配置 AI 提供商或设置 .env 中的 AI_* 配置项');
|
Log::channel('log-analysis')->error('AI 服务未配置,请在页面上配置 AI 提供商或设置 .env 中的 AI_* 配置项');
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ class LogAnalysisCommand extends Command
|
|||||||
$to = $this->parseTime($this->option('to') ?? 'now');
|
$to = $this->parseTime($this->option('to') ?? 'now');
|
||||||
|
|
||||||
if ($from >= $to) {
|
if ($from >= $to) {
|
||||||
Log::error('开始时间必须早于结束时间');
|
Log::channel('log-analysis')->error('开始时间必须早于结束时间');
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,11 +56,10 @@ class LogAnalysisCommand extends Command
|
|||||||
|
|
||||||
$query = $this->option('query');
|
$query = $this->option('query');
|
||||||
|
|
||||||
Log::info("开始分析日志...");
|
Log::channel('log-analysis')->info("开始分析日志...");
|
||||||
Log::info(" 时间范围: {$from->format('Y-m-d H:i:s')} ~ {$to->format('Y-m-d H:i:s')}");
|
Log::channel('log-analysis')->info(" 时间范围: {$from->format('Y-m-d H:i:s')} ~ {$to->format('Y-m-d H:i:s')}");
|
||||||
Log::info(" 查询语句: " . ($query ?: '*'));
|
Log::channel('log-analysis')->info(" 查询语句: " . ($query ?: '*'));
|
||||||
Log::info(" 分析模式: {$mode->label()}");
|
Log::channel('log-analysis')->info(" 分析模式: {$mode->label()}");
|
||||||
$this->newLine();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$result = $analysisService->analyze(
|
$result = $analysisService->analyze(
|
||||||
@@ -75,17 +74,17 @@ class LogAnalysisCommand extends Command
|
|||||||
if ($outputPath = $this->option('output')) {
|
if ($outputPath = $this->option('output')) {
|
||||||
$json = json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
$json = json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
file_put_contents($outputPath, $json);
|
file_put_contents($outputPath, $json);
|
||||||
Log::info("报告已保存到: {$outputPath}");
|
Log::channel('log-analysis')->info("报告已保存到: {$outputPath}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 推送到钉钉
|
// 推送到钉钉
|
||||||
if ($this->option('push')) {
|
if ($this->option('push')) {
|
||||||
Log::info("正在推送到钉钉...");
|
Log::channel('log-analysis')->info("正在推送到钉钉...");
|
||||||
$pushed = $analysisService->pushToNotification($result);
|
$pushed = $analysisService->pushToNotification($result);
|
||||||
if ($pushed) {
|
if ($pushed) {
|
||||||
Log::info("已推送到钉钉");
|
Log::channel('log-analysis')->info("已推送到钉钉");
|
||||||
} else {
|
} else {
|
||||||
Log::warning("钉钉推送失败");
|
Log::channel('log-analysis')->warning("钉钉推送失败");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ class LogAnalysisCommand extends Command
|
|||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::error("分析失败: {$e->getMessage()}");
|
Log::channel('log-analysis')->error("分析失败: {$e->getMessage()}");
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,57 +132,45 @@ class LogAnalysisCommand extends Command
|
|||||||
*/
|
*/
|
||||||
private function displaySummary(array $result): void
|
private function displaySummary(array $result): void
|
||||||
{
|
{
|
||||||
$this->newLine();
|
Log::channel('log-analysis')->info('=== 分析摘要 ===');
|
||||||
$this->info('=== 分析摘要 ===');
|
Log::channel('log-analysis')->info("总日志数: {$result['metadata']['total_logs']}");
|
||||||
$this->line("总日志数: {$result['metadata']['total_logs']}");
|
Log::channel('log-analysis')->info("分析应用数: {$result['metadata']['apps_analyzed']}");
|
||||||
$this->line("分析应用数: {$result['metadata']['apps_analyzed']}");
|
Log::channel('log-analysis')->info("执行时间: {$result['metadata']['execution_time_ms']}ms");
|
||||||
$this->line("执行时间: {$result['metadata']['execution_time_ms']}ms");
|
|
||||||
$this->newLine();
|
|
||||||
|
|
||||||
if (empty($result['results'])) {
|
if (empty($result['results'])) {
|
||||||
$this->warn('未找到匹配的日志');
|
Log::channel('log-analysis')->warning('未找到匹配的日志');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($result['results'] as $appName => $appResult) {
|
foreach ($result['results'] as $appName => $appResult) {
|
||||||
$this->line("【{$appName}】");
|
Log::channel('log-analysis')->info("【{$appName}】");
|
||||||
|
|
||||||
if (isset($appResult['error'])) {
|
if (isset($appResult['error'])) {
|
||||||
$this->error(" 分析失败: {$appResult['error']}");
|
Log::channel('log-analysis')->error(" 分析失败: {$appResult['error']}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$impact = $appResult['impact'] ?? 'unknown';
|
$impact = $appResult['impact'] ?? 'unknown';
|
||||||
$impactColor = match ($impact) {
|
|
||||||
'high' => 'red',
|
|
||||||
'medium' => 'yellow',
|
|
||||||
'low' => 'green',
|
|
||||||
default => 'white',
|
|
||||||
};
|
|
||||||
|
|
||||||
$this->line(" 日志数: {$appResult['log_count']}");
|
Log::channel('log-analysis')->info(" 日志数: {$appResult['log_count']}");
|
||||||
$this->line(" 代码上下文: " . ($appResult['has_code_context'] ? '是' : '否'));
|
Log::channel('log-analysis')->info(" 代码上下文: " . ($appResult['has_code_context'] ? '是' : '否'));
|
||||||
$this->line(" 影响级别: <fg={$impactColor}>{$impact}</>");
|
Log::channel('log-analysis')->info(" 影响级别: {$impact}");
|
||||||
$this->line(" 摘要: " . ($appResult['summary'] ?? 'N/A'));
|
Log::channel('log-analysis')->info(" 摘要: " . ($appResult['summary'] ?? 'N/A'));
|
||||||
|
|
||||||
$anomalies = $appResult['core_anomalies'] ?? [];
|
$anomalies = $appResult['core_anomalies'] ?? [];
|
||||||
if (!empty($anomalies)) {
|
if (!empty($anomalies)) {
|
||||||
$this->line(" 异常数: " . count($anomalies));
|
Log::channel('log-analysis')->info(" 异常数: " . count($anomalies));
|
||||||
|
|
||||||
$table = [];
|
|
||||||
foreach (array_slice($anomalies, 0, 5) as $anomaly) {
|
foreach (array_slice($anomalies, 0, 5) as $anomaly) {
|
||||||
$table[] = [
|
Log::channel('log-analysis')->info(sprintf(
|
||||||
|
" - [%s] %s (数量: %d) - %s",
|
||||||
$anomaly['type'] ?? 'N/A',
|
$anomaly['type'] ?? 'N/A',
|
||||||
$anomaly['classification'] ?? 'N/A',
|
$anomaly['classification'] ?? 'N/A',
|
||||||
$anomaly['count'] ?? 1,
|
$anomaly['count'] ?? 1,
|
||||||
mb_substr($anomaly['possible_cause'] ?? 'N/A', 0, 40),
|
mb_substr($anomaly['possible_cause'] ?? 'N/A', 0, 40)
|
||||||
];
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->table(['类型', '分类', '数量', '可能原因'], $table);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->newLine();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ class ScheduledTaskRefreshCommand extends Command
|
|||||||
public function handle(ScheduledTaskService $taskService): int
|
public function handle(ScheduledTaskService $taskService): int
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Log::info('开始刷新定时任务列表...');
|
Log::channel('scheduled-tasks')->info('开始刷新定时任务列表...');
|
||||||
|
|
||||||
$tasks = $taskService->getAllTasks();
|
$tasks = $taskService->getAllTasks();
|
||||||
|
|
||||||
Log::info(sprintf('成功刷新 %d 个定时任务', count($tasks)));
|
Log::channel('scheduled-tasks')->info(sprintf('成功刷新 %d 个定时任务', count($tasks)));
|
||||||
|
|
||||||
// 记录任务列表到日志
|
// 显示任务列表
|
||||||
foreach ($tasks as $task) {
|
foreach ($tasks as $task) {
|
||||||
Log::info(sprintf(
|
Log::channel('scheduled-tasks')->info(sprintf(
|
||||||
' - %s: %s (%s) [%s]',
|
' - %s: %s (%s) [%s]',
|
||||||
$task['name'],
|
$task['name'],
|
||||||
$task['description'],
|
$task['description'],
|
||||||
@@ -34,7 +34,7 @@ class ScheduledTaskRefreshCommand extends Command
|
|||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::error("刷新失败: {$e->getMessage()}");
|
Log::channel('scheduled-tasks')->error("刷新失败: {$e->getMessage()}");
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ class JiraService
|
|||||||
$endOfWeek = $now->copy()->subWeek()->endOfWeek();
|
$endOfWeek = $now->copy()->subWeek()->endOfWeek();
|
||||||
|
|
||||||
$workLogs = $this->getWorkLogs($username, $startOfWeek, $endOfWeek);
|
$workLogs = $this->getWorkLogs($username, $startOfWeek, $endOfWeek);
|
||||||
$organizedTasks = $this->organizeTasksForReport($workLogs);
|
$organizedTasks = $this->organizeTasksForReport($workLogs, $username);
|
||||||
|
|
||||||
$nextWeekTasks = $this->getNextWeekTasks($username);
|
$nextWeekTasks = $this->getNextWeekTasks($username);
|
||||||
|
|
||||||
@@ -307,8 +307,11 @@ class JiraService
|
|||||||
'created',
|
'created',
|
||||||
'fixVersions',
|
'fixVersions',
|
||||||
'labels',
|
'labels',
|
||||||
|
'assignee', // 经办人
|
||||||
'customfield_10004', // Sprint字段
|
'customfield_10004', // Sprint字段
|
||||||
'customfield_10900', // Bug发现阶段
|
'customfield_10900', // Bug发现阶段
|
||||||
|
'customfield_11000', // 开发人
|
||||||
|
'customfield_11301', // 实际修复人
|
||||||
'customfield_12700', // Bug错误类型
|
'customfield_12700', // Bug错误类型
|
||||||
'customfield_10115', // Bug修复描述
|
'customfield_10115', // Bug修复描述
|
||||||
'customfield_14305', // 需求类型
|
'customfield_14305', // 需求类型
|
||||||
@@ -369,6 +372,11 @@ class JiraService
|
|||||||
// 提取需求类型
|
// 提取需求类型
|
||||||
$requirementType = $this->extractRequirementType($issue);
|
$requirementType = $this->extractRequirementType($issue);
|
||||||
|
|
||||||
|
// 提取经办人、开发人、实际修复人
|
||||||
|
$assignee = $this->extractAssignee($issue);
|
||||||
|
$developer = $this->extractDeveloper($issue);
|
||||||
|
$actualFixer = $this->extractActualFixer($issue);
|
||||||
|
|
||||||
$workLogs->push([
|
$workLogs->push([
|
||||||
'id' => $worklog->id ?? '',
|
'id' => $worklog->id ?? '',
|
||||||
'project' => $issue->fields->project->name ?? '',
|
'project' => $issue->fields->project->name ?? '',
|
||||||
@@ -385,6 +393,9 @@ class JiraService
|
|||||||
'bug_type' => $bugType,
|
'bug_type' => $bugType,
|
||||||
'bug_description' => $bugDescription,
|
'bug_description' => $bugDescription,
|
||||||
'requirement_type' => $requirementType,
|
'requirement_type' => $requirementType,
|
||||||
|
'assignee' => $assignee,
|
||||||
|
'developer' => $developer,
|
||||||
|
'actual_fixer' => $actualFixer,
|
||||||
'date' => $worklogDate->format('Y-m-d'),
|
'date' => $worklogDate->format('Y-m-d'),
|
||||||
'time' => $worklogDate->format('H:i'),
|
'time' => $worklogDate->format('H:i'),
|
||||||
'hours' => round(($worklog->timeSpentSeconds ?? 0) / 3600, 2),
|
'hours' => round(($worklog->timeSpentSeconds ?? 0) / 3600, 2),
|
||||||
@@ -593,6 +604,60 @@ class JiraService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取经办人
|
||||||
|
*/
|
||||||
|
private function extractAssignee($issue): ?string
|
||||||
|
{
|
||||||
|
// 从assignee字段获取经办人
|
||||||
|
if (isset($issue->fields->assignee)) {
|
||||||
|
$assignee = $issue->fields->assignee;
|
||||||
|
|
||||||
|
// 处理对象类型
|
||||||
|
if (is_object($assignee)) {
|
||||||
|
return $assignee->name ?? $assignee->key ?? null;
|
||||||
|
} elseif (is_string($assignee)) {
|
||||||
|
return $assignee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取开发人
|
||||||
|
*/
|
||||||
|
private function extractDeveloper($issue): ?string
|
||||||
|
{
|
||||||
|
// 从customfield_11000获取开发人
|
||||||
|
if (isset($issue->fields->customFields['customfield_11000'])) {
|
||||||
|
$developer = $issue->fields->customFields['customfield_11000'];
|
||||||
|
|
||||||
|
if (is_string($developer) && !empty($developer)) {
|
||||||
|
return $developer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取实际修复人
|
||||||
|
*/
|
||||||
|
private function extractActualFixer($issue): ?string
|
||||||
|
{
|
||||||
|
// 从customfield_11301获取实际修复人
|
||||||
|
if (isset($issue->fields->customFields['customfield_11301'])) {
|
||||||
|
$fixer = $issue->fields->customFields['customfield_11301'];
|
||||||
|
|
||||||
|
if (is_string($fixer) && !empty($fixer)) {
|
||||||
|
return $fixer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清理摘要中的图片链接
|
* 清理摘要中的图片链接
|
||||||
*/
|
*/
|
||||||
@@ -610,29 +675,27 @@ class JiraService
|
|||||||
*/
|
*/
|
||||||
private function isTaskCompleted(string $status): bool
|
private function isTaskCompleted(string $status): bool
|
||||||
{
|
{
|
||||||
// 只有已完成或已取消才打勾
|
// 定义"进行中"的状态列表
|
||||||
$completedStatuses = [
|
// 如果状态不在这个列表中,则认为任务已完成
|
||||||
'已完成',
|
$inProgressStatuses = [
|
||||||
'完成',
|
'需求已确认',
|
||||||
'Done',
|
'开发中',
|
||||||
'Closed',
|
'需求调研中',
|
||||||
'Resolved',
|
'需求已调研',
|
||||||
];
|
'需求已评审',
|
||||||
$cancelledStatuses = [
|
'需求已排期',
|
||||||
'已取消',
|
'待提测',
|
||||||
'取消',
|
'需求设计中',
|
||||||
'Cancelled',
|
|
||||||
'Canceled',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return in_array($status, $completedStatuses, true)
|
// 如果状态不在"进行中"列表中,则标记为已完成
|
||||||
|| in_array($status, $cancelledStatuses, true);
|
return !in_array($status, $inProgressStatuses, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组织任务数据用于周报生成
|
* 组织任务数据用于周报生成
|
||||||
*/
|
*/
|
||||||
private function organizeTasksForReport(Collection $workLogs): Collection
|
private function organizeTasksForReport(Collection $workLogs, string $username): Collection
|
||||||
{
|
{
|
||||||
$organized = collect([
|
$organized = collect([
|
||||||
'sprints' => collect(),
|
'sprints' => collect(),
|
||||||
@@ -671,6 +734,21 @@ class JiraService
|
|||||||
$isSubtask = in_array($issueType, ['Sub-task', 'sub-task', '子任务']);
|
$isSubtask = in_array($issueType, ['Sub-task', 'sub-task', '子任务']);
|
||||||
|
|
||||||
if ($isBug && $workLog['bug_stage']) {
|
if ($isBug && $workLog['bug_stage']) {
|
||||||
|
// Bug过滤逻辑:必须经办人、实际修复人或开发人是当前用户
|
||||||
|
$assignee = $workLog['assignee'] ?? null;
|
||||||
|
$developer = $workLog['developer'] ?? null;
|
||||||
|
$actualFixer = $workLog['actual_fixer'] ?? null;
|
||||||
|
|
||||||
|
// 检查是否有任一字段匹配当前用户
|
||||||
|
$isUserRelated = ($assignee === $username)
|
||||||
|
|| ($developer === $username)
|
||||||
|
|| ($actualFixer === $username);
|
||||||
|
|
||||||
|
// 如果不是当前用户相关的Bug,跳过
|
||||||
|
if (!$isUserRelated) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Bug按发现阶段分类
|
// Bug按发现阶段分类
|
||||||
$stage = $workLog['bug_stage'];
|
$stage = $workLog['bug_stage'];
|
||||||
if (!$organized['bugs']->has($stage)) {
|
if (!$organized['bugs']->has($stage)) {
|
||||||
|
|||||||
@@ -127,6 +127,38 @@ return [
|
|||||||
'path' => storage_path('logs/laravel.log'),
|
'path' => storage_path('logs/laravel.log'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'jenkins-monitor' => [
|
||||||
|
'driver' => 'daily',
|
||||||
|
'path' => storage_path('logs/scheduled-tasks/jenkins-monitor.log'),
|
||||||
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
|
'days' => 7,
|
||||||
|
'replace_placeholders' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
'git-monitor' => [
|
||||||
|
'driver' => 'daily',
|
||||||
|
'path' => storage_path('logs/scheduled-tasks/git-monitor.log'),
|
||||||
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
|
'days' => 7,
|
||||||
|
'replace_placeholders' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
'log-analysis' => [
|
||||||
|
'driver' => 'daily',
|
||||||
|
'path' => storage_path('logs/scheduled-tasks/log-analysis.log'),
|
||||||
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
|
'days' => 7,
|
||||||
|
'replace_placeholders' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
'scheduled-tasks' => [
|
||||||
|
'driver' => 'daily',
|
||||||
|
'path' => storage_path('logs/scheduled-tasks/scheduled-tasks.log'),
|
||||||
|
'level' => env('LOG_LEVEL', 'debug'),
|
||||||
|
'days' => 7,
|
||||||
|
'replace_placeholders' => true,
|
||||||
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ Schedule::command('git-monitor:check')
|
|||||||
->withoutOverlapping()
|
->withoutOverlapping()
|
||||||
->runInBackground()
|
->runInBackground()
|
||||||
->description('git-monitor-check')
|
->description('git-monitor-check')
|
||||||
->appendOutputTo(storage_path('logs/scheduled-tasks/git-monitor-' . date('Y-m-d') . '.log'))
|
|
||||||
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('git-monitor-check'));
|
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('git-monitor-check'));
|
||||||
|
|
||||||
// Git Monitor - 每天凌晨 2 点刷新 release 缓存
|
// Git Monitor - 每天凌晨 2 点刷新 release 缓存
|
||||||
@@ -34,7 +33,6 @@ Schedule::command('git-monitor:cache')
|
|||||||
->dailyAt('02:00')
|
->dailyAt('02:00')
|
||||||
->withoutOverlapping()
|
->withoutOverlapping()
|
||||||
->description('git-monitor-cache')
|
->description('git-monitor-cache')
|
||||||
->appendOutputTo(storage_path('logs/scheduled-tasks/git-monitor-' . date('Y-m-d') . '.log'))
|
|
||||||
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('git-monitor-cache'));
|
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('git-monitor-cache'));
|
||||||
|
|
||||||
// SLS 日志分析 - 每天凌晨 2 点执行
|
// SLS 日志分析 - 每天凌晨 2 点执行
|
||||||
@@ -43,7 +41,6 @@ Schedule::command('log-analysis:run --from="-24h" --to="now" --query="ERROR or W
|
|||||||
->withoutOverlapping()
|
->withoutOverlapping()
|
||||||
->runInBackground()
|
->runInBackground()
|
||||||
->description('daily-log-analysis')
|
->description('daily-log-analysis')
|
||||||
->appendOutputTo(storage_path('logs/scheduled-tasks/log-analysis-' . date('Y-m-d') . '.log'))
|
|
||||||
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('daily-log-analysis'))
|
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('daily-log-analysis'))
|
||||||
->onFailure(fn() => Log::error('每日日志分析定时任务执行失败'));
|
->onFailure(fn() => Log::error('每日日志分析定时任务执行失败'));
|
||||||
|
|
||||||
@@ -53,7 +50,6 @@ Schedule::command('log-analysis:run --from="-6h" --to="now" --query="ERROR or WA
|
|||||||
->withoutOverlapping()
|
->withoutOverlapping()
|
||||||
->runInBackground()
|
->runInBackground()
|
||||||
->description('frequent-log-analysis')
|
->description('frequent-log-analysis')
|
||||||
->appendOutputTo(storage_path('logs/scheduled-tasks/log-analysis-' . date('Y-m-d') . '.log'))
|
|
||||||
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('frequent-log-analysis'))
|
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('frequent-log-analysis'))
|
||||||
->onFailure(fn() => Log::error('SLS 日志分析定时任务执行失败'));
|
->onFailure(fn() => Log::error('SLS 日志分析定时任务执行失败'));
|
||||||
|
|
||||||
@@ -63,7 +59,6 @@ Schedule::command('jenkins:monitor')
|
|||||||
->withoutOverlapping()
|
->withoutOverlapping()
|
||||||
->runInBackground()
|
->runInBackground()
|
||||||
->description('jenkins-monitor')
|
->description('jenkins-monitor')
|
||||||
->appendOutputTo(storage_path('logs/scheduled-tasks/jenkins-monitor-' . date('Y-m-d') . '.log'))
|
|
||||||
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('jenkins-monitor'));
|
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('jenkins-monitor'));
|
||||||
|
|
||||||
// 定时任务刷新 - 每天凌晨 3 点刷新定时任务列表
|
// 定时任务刷新 - 每天凌晨 3 点刷新定时任务列表
|
||||||
@@ -71,13 +66,4 @@ Schedule::command('scheduled-task:refresh')
|
|||||||
->dailyAt('03:00')
|
->dailyAt('03:00')
|
||||||
->withoutOverlapping()
|
->withoutOverlapping()
|
||||||
->description('scheduled-task-refresh')
|
->description('scheduled-task-refresh')
|
||||||
->appendOutputTo(storage_path('logs/scheduled-tasks/scheduled-tasks-' . date('Y-m-d') . '.log'))
|
|
||||||
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('scheduled-task-refresh'));
|
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('scheduled-task-refresh'));
|
||||||
|
|
||||||
// 日志清理 - 每天凌晨 4 点清理 7 天前的定时任务日志
|
|
||||||
Schedule::command('logs:clean-scheduled-tasks --days=7')
|
|
||||||
->dailyAt('04:00')
|
|
||||||
->withoutOverlapping()
|
|
||||||
->description('logs-cleanup')
|
|
||||||
->appendOutputTo(storage_path('logs/scheduled-tasks/scheduled-tasks-' . date('Y-m-d') . '.log'))
|
|
||||||
->when(fn() => \App\Services\ScheduledTaskService::isEnabled('logs-cleanup'));
|
|
||||||
|
|||||||
Reference in New Issue
Block a user