feat: add github spider md push and async task logging
This commit is contained in:
@@ -6,6 +6,7 @@ namespace app\admin\controller;
|
||||
|
||||
use app\admin\BaseController;
|
||||
use app\common\helper\SeoCopyAiRuleConfigHelper;
|
||||
use app\common\helper\SpiderMdConfigHelper;
|
||||
use app\model\AdminUserModel;
|
||||
use app\model\BaoYangRuZhuModel;
|
||||
use app\model\CaiJiPeiZhiModel;
|
||||
@@ -122,6 +123,7 @@ class SystemConfig extends BaseController
|
||||
public function getSystemConfigList(Request $Request, ?AdminUserModel $AdminUserModel)
|
||||
{
|
||||
SeoCopyAiRuleConfigHelper::ensureSystemConfigDefaults();
|
||||
SpiderMdConfigHelper::ensureSystemConfigDefaults();
|
||||
|
||||
$arrData = [
|
||||
"page" => $Request->get('page', 1),
|
||||
|
||||
50
code/app/command/DomainSpiderMdPlanTaskSeedCommand.php
Normal file
50
code/app/command/DomainSpiderMdPlanTaskSeedCommand.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use app\model\PlanTaskModel;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
|
||||
class DomainSpiderMdPlanTaskSeedCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('plan:seed:domain-spider-md')
|
||||
->setDescription('补齐蜘蛛池MD自动生成计划任务 GENERATE_DOMAIN_SPIDER_MD');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$strCode = 'GENERATE_DOMAIN_SPIDER_MD';
|
||||
$arrData = [
|
||||
'pt_name' => '蜘蛛池MD自动生成',
|
||||
'pt_code' => $strCode,
|
||||
'pt_enable' => 0,
|
||||
'pt_limit' => 24 * 3600,
|
||||
'pt_last_exec' => 0,
|
||||
];
|
||||
|
||||
$PlanTaskModel = PlanTaskModel::where('pt_code', $strCode)->find();
|
||||
if ($PlanTaskModel instanceof PlanTaskModel) {
|
||||
$output->writeln('计划任务已存在,无需重复创建:' . $strCode);
|
||||
$output->writeln('pt_id:' . (int)$PlanTaskModel->pt_id);
|
||||
$output->writeln('pt_name:' . (string)$PlanTaskModel->pt_name);
|
||||
$output->writeln('pt_enable:' . (int)$PlanTaskModel->pt_enable);
|
||||
$output->writeln('pt_limit:' . (int)$PlanTaskModel->pt_limit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$intId = (int)PlanTaskModel::insertGetId($arrData);
|
||||
$output->writeln('计划任务已创建:' . $strCode);
|
||||
$output->writeln('pt_id:' . $intId);
|
||||
$output->writeln('pt_name:' . $arrData['pt_name']);
|
||||
$output->writeln('pt_enable:' . $arrData['pt_enable']);
|
||||
$output->writeln('pt_limit:' . $arrData['pt_limit']);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,9 @@ class DomainSpiderMdRunHelper
|
||||
$runs = [];
|
||||
SpiderMdConfigHelper::ensureSystemConfigDefaults();
|
||||
$gitee = self::resolveGiteeConfig();
|
||||
$github = self::resolveGithubConfig();
|
||||
unset($gitee['token']);
|
||||
unset($github['token']);
|
||||
$matches = glob(self::baseRoot() . '/*/*/summary.json');
|
||||
if (is_array($matches)) {
|
||||
rsort($matches);
|
||||
@@ -39,6 +41,7 @@ class DomainSpiderMdRunHelper
|
||||
'generated_at' => date(DATE_ATOM),
|
||||
'run_root' => self::baseRoot(),
|
||||
'gitee' => $gitee,
|
||||
'github' => $github,
|
||||
'runtime_config' => self::resolveRuntimeConfig(),
|
||||
'config_summary' => SpiderMdConfigHelper::buildConfigSummary(),
|
||||
'latest_run' => $runs[0] ?? null,
|
||||
@@ -50,19 +53,24 @@ class DomainSpiderMdRunHelper
|
||||
{
|
||||
$config = self::resolveRuntimeConfig($options);
|
||||
$giteeConfig = self::resolveGiteeConfig();
|
||||
$githubConfig = self::resolveGithubConfig();
|
||||
self::ensureDir(self::baseRoot());
|
||||
$forceRegenerate = !empty($options['force_regenerate']);
|
||||
$previous = self::findLatestSuccessfulRunSummary();
|
||||
$today = date('Ymd');
|
||||
$giteeTargetChanged = !empty($previous) ? self::giteeTargetChanged($previous, $giteeConfig) : false;
|
||||
$remoteTargetChanged = !empty($previous)
|
||||
? (self::giteeTargetChanged($previous, $giteeConfig) || self::githubTargetChanged($previous, $githubConfig))
|
||||
: false;
|
||||
$previousGiteePushStatus = trim((string)($previous['gitee_push_status'] ?? ''));
|
||||
$previousGithubPushStatus = trim((string)($previous['github_push_status'] ?? ''));
|
||||
$allowRetryToday = in_array($previousGiteePushStatus, ['failed', 'partial_failed'], true);
|
||||
$allowRetryToday = $allowRetryToday || in_array($previousGithubPushStatus, ['failed', 'partial_failed'], true);
|
||||
|
||||
if (
|
||||
!$forceRegenerate
|
||||
&& !empty($previous)
|
||||
&& substr((string)($previous['generated_at'] ?? ''), 0, 10) === date('Y-m-d')
|
||||
&& !$giteeTargetChanged
|
||||
&& !$remoteTargetChanged
|
||||
&& !$allowRetryToday
|
||||
) {
|
||||
self::log($logger, '今日已生成过蜘蛛池 MD,跳过本轮;如需全新生成请使用强制重生成');
|
||||
@@ -72,11 +80,11 @@ class DomainSpiderMdRunHelper
|
||||
return $previous;
|
||||
}
|
||||
|
||||
if ($forceRegenerate || empty($previous) || $giteeTargetChanged) {
|
||||
if ($forceRegenerate || empty($previous) || $remoteTargetChanged) {
|
||||
if ($forceRegenerate) {
|
||||
self::clearAllRuns();
|
||||
} elseif ($giteeTargetChanged) {
|
||||
self::log($logger, '检测到 Gitee 推送目标已变更,本轮按全新生成处理');
|
||||
} elseif ($remoteTargetChanged) {
|
||||
self::log($logger, '检测到远端推送目标已变更,本轮按全新生成处理');
|
||||
}
|
||||
$dateDir = self::baseRoot() . '/' . $today;
|
||||
self::ensureDir($dateDir);
|
||||
@@ -216,11 +224,17 @@ class DomainSpiderMdRunHelper
|
||||
],
|
||||
'files' => $writtenFiles,
|
||||
'gitee' => array_diff_key($giteeConfig, ['token' => true]),
|
||||
'github' => array_diff_key($githubConfig, ['token' => true]),
|
||||
'gitee_files' => [],
|
||||
'gitee_pushed' => 0,
|
||||
'gitee_push_attempted' => 0,
|
||||
'gitee_push_status' => 'pending',
|
||||
'gitee_push_error' => '',
|
||||
'github_files' => [],
|
||||
'github_pushed' => 0,
|
||||
'github_push_attempted' => 0,
|
||||
'github_push_status' => 'pending',
|
||||
'github_push_error' => '',
|
||||
];
|
||||
|
||||
file_put_contents(
|
||||
@@ -235,6 +249,12 @@ class DomainSpiderMdRunHelper
|
||||
$summary['gitee_push_attempted'] = (int)($giteePush['attempted'] ?? 0);
|
||||
$summary['gitee_push_status'] = (string)($giteePush['status'] ?? 'skipped');
|
||||
$summary['gitee_push_error'] = (string)($giteePush['error'] ?? '');
|
||||
$githubPush = self::pushRunToGithub($summary, $logger);
|
||||
$summary['github_files'] = (array)($githubPush['items'] ?? []);
|
||||
$summary['github_pushed'] = !empty($summary['github_files']) ? 1 : 0;
|
||||
$summary['github_push_attempted'] = (int)($githubPush['attempted'] ?? 0);
|
||||
$summary['github_push_status'] = (string)($githubPush['status'] ?? 'skipped');
|
||||
$summary['github_push_error'] = (string)($githubPush['error'] ?? '');
|
||||
|
||||
file_put_contents(
|
||||
$runRoot . '/gitee-links.json',
|
||||
@@ -246,6 +266,16 @@ class DomainSpiderMdRunHelper
|
||||
'items' => $summary['gitee_files'],
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL
|
||||
);
|
||||
file_put_contents(
|
||||
$runRoot . '/github-links.json',
|
||||
json_encode([
|
||||
'run_id' => $runId,
|
||||
'generated_at' => date(DATE_ATOM),
|
||||
'status' => $summary['github_push_status'],
|
||||
'error' => $summary['github_push_error'],
|
||||
'items' => $summary['github_files'],
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL
|
||||
);
|
||||
file_put_contents(
|
||||
$runRoot . '/summary.json',
|
||||
json_encode($summary, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL
|
||||
@@ -1094,6 +1124,213 @@ class DomainSpiderMdRunHelper
|
||||
return SpiderMdConfigHelper::resolveGiteeConfig();
|
||||
}
|
||||
|
||||
protected static function resolveGithubConfig(): array
|
||||
{
|
||||
return SpiderMdConfigHelper::resolveGithubConfig();
|
||||
}
|
||||
|
||||
protected static function githubTargetChanged(array $previous, array $currentConfig): bool
|
||||
{
|
||||
$previousGithub = (array)($previous['github'] ?? []);
|
||||
foreach (['enabled', 'owner', 'repo', 'branch', 'root'] as $key) {
|
||||
if ((string)($previousGithub[$key] ?? '') !== (string)($currentConfig[$key] ?? '')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function pushRunToGithub(array $summary, ?callable $logger = null): array
|
||||
{
|
||||
$config = self::resolveGithubConfig();
|
||||
if (empty($config['enabled']) || empty($config['configured'])) {
|
||||
self::log($logger, 'GitHub 未启用或未配置,本轮跳过 GitHub 推送');
|
||||
return [
|
||||
'status' => 'skipped',
|
||||
'attempted' => 0,
|
||||
'error' => '',
|
||||
'items' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$items = [];
|
||||
$dateDir = basename(dirname((string)($summary['run_root'] ?? '')));
|
||||
if (!preg_match('/^\d{8}$/', $dateDir)) {
|
||||
$dateDir = date('Ymd', strtotime((string)($summary['generated_at'] ?? 'now')));
|
||||
}
|
||||
$baseRemoteDir = trim((string)$config['root'], '/');
|
||||
$baseRemoteDir = trim($baseRemoteDir . '/' . $dateDir . '/' . (string)($summary['run_id'] ?? ''), '/');
|
||||
|
||||
$localFiles = array_merge(
|
||||
array_map(static function (array $file): string {
|
||||
return (string)($file['file_path'] ?? '');
|
||||
}, (array)($summary['files'] ?? [])),
|
||||
[
|
||||
(string)($summary['run_root'] ?? '') . '/summary.json',
|
||||
(string)($summary['run_root'] ?? '') . '/summary.html',
|
||||
(string)($summary['run_root'] ?? '') . '/github-links.json',
|
||||
]
|
||||
);
|
||||
|
||||
try {
|
||||
foreach ($localFiles as $filePath) {
|
||||
if (!is_file($filePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fileName = basename($filePath);
|
||||
$remotePath = $baseRemoteDir . '/' . $fileName;
|
||||
$response = self::upsertGithubFile($config, $remotePath, (string)file_get_contents($filePath), 'spider md update ' . $fileName);
|
||||
|
||||
$items[] = [
|
||||
'file_name' => $fileName,
|
||||
'remote_path' => $remotePath,
|
||||
'html_url' => (string)($response['html_url'] ?? self::buildGithubHtmlUrl($config, $remotePath)),
|
||||
'download_url' => (string)($response['download_url'] ?? self::buildGithubRawUrl($config, $remotePath)),
|
||||
'local_path' => $filePath,
|
||||
];
|
||||
self::log($logger, '已推送 GitHub:' . $fileName);
|
||||
}
|
||||
} catch (\Throwable $throwable) {
|
||||
$message = trim($throwable->getMessage());
|
||||
self::log($logger, 'GitHub 推送失败:' . $message);
|
||||
return [
|
||||
'status' => !empty($items) ? 'partial_failed' : 'failed',
|
||||
'attempted' => 1,
|
||||
'error' => $message,
|
||||
'items' => $items,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => 'success',
|
||||
'attempted' => 1,
|
||||
'error' => '',
|
||||
'items' => $items,
|
||||
];
|
||||
}
|
||||
|
||||
protected static function upsertGithubFile(array $config, string $remotePath, string $content, string $message): array
|
||||
{
|
||||
$existing = self::githubRequest(
|
||||
'GET',
|
||||
sprintf(
|
||||
'https://api.github.com/repos/%s/%s/contents/%s?ref=%s',
|
||||
rawurlencode((string)$config['owner']),
|
||||
rawurlencode((string)$config['repo']),
|
||||
str_replace('%2F', '/', rawurlencode($remotePath)),
|
||||
rawurlencode((string)$config['branch'])
|
||||
),
|
||||
$config,
|
||||
[],
|
||||
true
|
||||
);
|
||||
|
||||
$payload = [
|
||||
'message' => $message,
|
||||
'content' => base64_encode($content),
|
||||
'branch' => (string)$config['branch'],
|
||||
];
|
||||
|
||||
if (!empty($existing['sha'])) {
|
||||
$payload['sha'] = (string)$existing['sha'];
|
||||
}
|
||||
|
||||
return self::githubRequest(
|
||||
'PUT',
|
||||
sprintf(
|
||||
'https://api.github.com/repos/%s/%s/contents/%s',
|
||||
rawurlencode((string)$config['owner']),
|
||||
rawurlencode((string)$config['repo']),
|
||||
str_replace('%2F', '/', rawurlencode($remotePath))
|
||||
),
|
||||
$config,
|
||||
$payload
|
||||
);
|
||||
}
|
||||
|
||||
protected static function githubRequest(string $method, string $url, array $config, array $payload = [], bool $allowNotFound = false): array
|
||||
{
|
||||
if (!function_exists('curl_init')) {
|
||||
throw new \RuntimeException('当前 PHP 环境缺少 curl 扩展,无法推送 GitHub');
|
||||
}
|
||||
|
||||
$curl = curl_init();
|
||||
if ($curl === false) {
|
||||
throw new \RuntimeException('初始化 GitHub 请求失败');
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'Accept: application/vnd.github+json',
|
||||
'Authorization: Bearer ' . (string)$config['token'],
|
||||
'X-GitHub-Api-Version: 2022-11-28',
|
||||
'User-Agent: SEONexus-SpiderMd',
|
||||
];
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 45,
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
]);
|
||||
|
||||
if (!empty($payload)) {
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
|
||||
$response = curl_exec($curl);
|
||||
$errno = curl_errno($curl);
|
||||
$error = curl_error($curl);
|
||||
$status = (int)curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
||||
curl_close($curl);
|
||||
|
||||
if ($errno !== 0) {
|
||||
throw new \RuntimeException('GitHub 请求失败:' . $error);
|
||||
}
|
||||
|
||||
$decoded = json_decode((string)$response, true);
|
||||
if ($allowNotFound && $status === 404) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($status >= 400) {
|
||||
$message = is_array($decoded) ? (string)($decoded['message'] ?? json_encode($decoded, JSON_UNESCAPED_UNICODE)) : (string)$response;
|
||||
throw new \RuntimeException('GitHub 请求返回异常:' . $message);
|
||||
}
|
||||
|
||||
if (is_array($decoded) && isset($decoded['content']) && is_array($decoded['content'])) {
|
||||
$decoded['html_url'] = (string)($decoded['content']['html_url'] ?? '');
|
||||
$decoded['download_url'] = (string)($decoded['content']['download_url'] ?? '');
|
||||
$decoded['sha'] = (string)($decoded['content']['sha'] ?? ($decoded['sha'] ?? ''));
|
||||
}
|
||||
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
protected static function buildGithubHtmlUrl(array $config, string $remotePath): string
|
||||
{
|
||||
return sprintf(
|
||||
'https://github.com/%s/%s/blob/%s/%s',
|
||||
rawurlencode((string)$config['owner']),
|
||||
rawurlencode((string)$config['repo']),
|
||||
rawurlencode((string)$config['branch']),
|
||||
str_replace('%2F', '/', rawurlencode($remotePath))
|
||||
);
|
||||
}
|
||||
|
||||
protected static function buildGithubRawUrl(array $config, string $remotePath): string
|
||||
{
|
||||
return sprintf(
|
||||
'https://raw.githubusercontent.com/%s/%s/%s/%s',
|
||||
rawurlencode((string)$config['owner']),
|
||||
rawurlencode((string)$config['repo']),
|
||||
rawurlencode((string)$config['branch']),
|
||||
str_replace('%2F', '/', rawurlencode($remotePath))
|
||||
);
|
||||
}
|
||||
|
||||
protected static function readJsonFile(string $path): array
|
||||
{
|
||||
if (!is_file($path)) {
|
||||
|
||||
@@ -36,32 +36,62 @@ class SpiderMdConfigHelper
|
||||
],
|
||||
'SPIDER_MD_GITEE_ENABLED' => [
|
||||
'default' => '0',
|
||||
'description' => '蜘蛛池MD是否启用 Gitee 推送',
|
||||
'description' => '蜘蛛池MD是否启用 Gitee 推送(0=关闭,1=开启;开启后会把生成的 markdown 与 links.json 同步到 Gitee)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITEE_OWNER' => [
|
||||
'default' => '',
|
||||
'description' => '蜘蛛池MD Gitee 仓库 owner',
|
||||
'description' => '蜘蛛池MD Gitee 仓库 owner(仓库所属用户名或组织名,例如 yourname)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITEE_REPO' => [
|
||||
'default' => '',
|
||||
'description' => '蜘蛛池MD Gitee 仓库 repo',
|
||||
'description' => '蜘蛛池MD Gitee 仓库 repo(仓库名,不带 owner,例如 seo-spider-md)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITEE_BRANCH' => [
|
||||
'default' => 'master',
|
||||
'description' => '蜘蛛池MD Gitee 分支',
|
||||
'description' => '蜘蛛池MD Gitee 分支(默认 master;推送文件会写入这个分支)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITEE_ROOT' => [
|
||||
'default' => 'seo-spider-md',
|
||||
'description' => '蜘蛛池MD Gitee 远程根目录',
|
||||
'description' => '蜘蛛池MD Gitee 远程根目录(仓库内子目录,例如 seo-spider-md;留空表示直接写仓库根目录)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITEE_TOKEN' => [
|
||||
'default' => '',
|
||||
'description' => '蜘蛛池MD Gitee Token',
|
||||
'description' => '蜘蛛池MD Gitee Token(需要仓库 contents / push 权限;后台保存后会参与远程推送)',
|
||||
'secret' => true,
|
||||
],
|
||||
'SPIDER_MD_GITHUB_ENABLED' => [
|
||||
'default' => '0',
|
||||
'description' => '蜘蛛池MD是否启用 GitHub 推送(0=关闭,1=开启;开启后会把生成的 markdown 与 links.json 同步到 GitHub)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITHUB_OWNER' => [
|
||||
'default' => '',
|
||||
'description' => '蜘蛛池MD GitHub 仓库 owner(仓库所属用户名或组织名,例如 yourname)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITHUB_REPO' => [
|
||||
'default' => '',
|
||||
'description' => '蜘蛛池MD GitHub 仓库 repo(仓库名,不带 owner,例如 seo-spider-md)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITHUB_BRANCH' => [
|
||||
'default' => 'main',
|
||||
'description' => '蜘蛛池MD GitHub 分支(默认 main;推送文件会写入这个分支)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITHUB_ROOT' => [
|
||||
'default' => 'seo-spider-md',
|
||||
'description' => '蜘蛛池MD GitHub 远程根目录(仓库内子目录,例如 seo-spider-md;留空表示直接写仓库根目录)',
|
||||
'secret' => false,
|
||||
],
|
||||
'SPIDER_MD_GITHUB_TOKEN' => [
|
||||
'default' => '',
|
||||
'description' => '蜘蛛池MD GitHub Token(建议使用细粒度 Token,并授予当前仓库 Contents: Read and write 权限)',
|
||||
'secret' => true,
|
||||
],
|
||||
'SPIDER_MD_RETENTION_DAYS' => [
|
||||
@@ -197,6 +227,43 @@ class SpiderMdConfigHelper
|
||||
];
|
||||
}
|
||||
|
||||
public static function resolveGithubConfig(): array
|
||||
{
|
||||
self::ensureSystemConfigDefaults();
|
||||
|
||||
$enabledMeta = self::readValueMeta('SPIDER_MD_GITHUB_ENABLED');
|
||||
$ownerMeta = self::readValueMeta('SPIDER_MD_GITHUB_OWNER');
|
||||
$repoMeta = self::readValueMeta('SPIDER_MD_GITHUB_REPO');
|
||||
$branchMeta = self::readValueMeta('SPIDER_MD_GITHUB_BRANCH');
|
||||
$rootMeta = self::readValueMeta('SPIDER_MD_GITHUB_ROOT');
|
||||
$tokenMeta = self::readValueMeta('SPIDER_MD_GITHUB_TOKEN');
|
||||
|
||||
$enabled = (int)($enabledMeta['value'] ?? '0') === 1;
|
||||
$owner = trim((string)($ownerMeta['value'] ?? ''));
|
||||
$repo = trim((string)($repoMeta['value'] ?? ''));
|
||||
$branch = trim((string)($branchMeta['value'] ?? 'main'));
|
||||
$root = trim((string)($rootMeta['value'] ?? 'seo-spider-md'));
|
||||
$token = trim((string)($tokenMeta['value'] ?? ''));
|
||||
|
||||
return [
|
||||
'enabled' => $enabled ? 1 : 0,
|
||||
'configured' => ($enabled && $owner !== '' && $repo !== '' && $token !== '') ? 1 : 0,
|
||||
'owner' => $owner,
|
||||
'repo' => $repo,
|
||||
'branch' => $branch !== '' ? $branch : 'main',
|
||||
'root' => $root,
|
||||
'token' => $token,
|
||||
'sources' => [
|
||||
'enabled' => (string)($enabledMeta['source'] ?? 'env'),
|
||||
'owner' => (string)($ownerMeta['source'] ?? 'env'),
|
||||
'repo' => (string)($repoMeta['source'] ?? 'env'),
|
||||
'branch' => (string)($branchMeta['source'] ?? 'env'),
|
||||
'root' => (string)($rootMeta['source'] ?? 'env'),
|
||||
'token' => (string)($tokenMeta['source'] ?? 'env'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected static function readOperationalValue(string $code): string
|
||||
{
|
||||
return (string)(self::readValueMeta($code)['value'] ?? '');
|
||||
|
||||
124
code/app/task/logic/DomainImportAsyncTaskLogic.php
Normal file
124
code/app/task/logic/DomainImportAsyncTaskLogic.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\task\logic;
|
||||
|
||||
use app\common\helper\DomainImportAsyncJobHelper;
|
||||
use app\common\helper\DomainSpiderMdRunHelper;
|
||||
|
||||
class DomainImportAsyncTaskLogic
|
||||
{
|
||||
public static function handle(array $data = []): void
|
||||
{
|
||||
$jobId = trim((string)($data['job_id'] ?? ''));
|
||||
if ($jobId === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$job = DomainImportAsyncJobHelper::readJob($jobId);
|
||||
if (empty($job)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
DomainImportAsyncJobHelper::updateJob($jobId, [
|
||||
'status' => DomainImportAsyncJobHelper::STATUS_RUNNING,
|
||||
'current_step' => 'starting',
|
||||
'message' => '任务开始执行',
|
||||
]);
|
||||
|
||||
DomainImportAsyncJobHelper::appendLog($jobId, '开始执行任务:' . (string)($job['label'] ?? $job['type'] ?? 'unknown'));
|
||||
DomainImportAsyncJobHelper::updateProgress($jobId, 1, 100, 'prepare', '开始准备执行环境');
|
||||
|
||||
$type = trim((string)($job['type'] ?? ''));
|
||||
$payload = is_array($job['payload'] ?? null) ? (array)$job['payload'] : [];
|
||||
|
||||
switch ($type) {
|
||||
case 'spider_md_generate':
|
||||
$summary = self::runSpiderMdGenerate($jobId, $payload);
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException('暂未支持的异步任务类型:' . $type);
|
||||
}
|
||||
|
||||
DomainImportAsyncJobHelper::updateJob($jobId, [
|
||||
'status' => DomainImportAsyncJobHelper::STATUS_SUCCESS,
|
||||
'current_step' => 'completed',
|
||||
'message' => '任务执行完成',
|
||||
'summary' => $summary,
|
||||
]);
|
||||
DomainImportAsyncJobHelper::appendLog($jobId, '任务执行完成');
|
||||
} catch (\Throwable $e) {
|
||||
DomainImportAsyncJobHelper::appendLog($jobId, '任务执行失败:' . $e->getMessage());
|
||||
DomainImportAsyncJobHelper::updateJob($jobId, [
|
||||
'status' => DomainImportAsyncJobHelper::STATUS_FAILED,
|
||||
'current_step' => 'failed',
|
||||
'message' => '任务执行失败:' . $e->getMessage(),
|
||||
'summary' => [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected static function runSpiderMdGenerate(string $jobId, array $payload): array
|
||||
{
|
||||
DomainImportAsyncJobHelper::appendLog($jobId, '开始执行蜘蛛池MD生成');
|
||||
DomainImportAsyncJobHelper::updateProgress($jobId, 5, 100, 'running', '开始生成蜘蛛池MD');
|
||||
|
||||
$state = [
|
||||
'current' => 5,
|
||||
'total' => 100,
|
||||
'domain_total' => 0,
|
||||
];
|
||||
|
||||
$logger = static function (string $message) use ($jobId, &$state): void {
|
||||
$message = trim($message);
|
||||
if ($message === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
DomainImportAsyncJobHelper::appendLog($jobId, $message);
|
||||
|
||||
if (preg_match('/已载入\s+(\d+)\s+个域名/u', $message, $matches)) {
|
||||
$state['domain_total'] = max(0, (int)($matches[1] ?? 0));
|
||||
DomainImportAsyncJobHelper::updateProgress($jobId, 10, 100, 'running', $message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('/\[(\d+)\/(\d+)\]/', $message, $matches)) {
|
||||
$current = max(0, (int)($matches[1] ?? 0));
|
||||
$total = max(1, (int)($matches[2] ?? 1));
|
||||
$percent = 10 + (int)floor(($current / $total) * 75);
|
||||
$state['current'] = min(95, max($state['current'], $percent));
|
||||
DomainImportAsyncJobHelper::updateProgress($jobId, $state['current'], 100, 'running', $message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mb_strpos($message, '推送失败') !== false) {
|
||||
DomainImportAsyncJobHelper::updateProgress($jobId, max($state['current'], 90), 100, 'running', $message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mb_strpos($message, '已推送 GitHub') !== false || mb_strpos($message, '已推送 Gitee') !== false) {
|
||||
$state['current'] = min(98, max($state['current'], $state['current'] + 1));
|
||||
DomainImportAsyncJobHelper::updateProgress($jobId, $state['current'], 100, 'running', $message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mb_strpos($message, '蜘蛛池 MD 生成完成') !== false) {
|
||||
DomainImportAsyncJobHelper::updateProgress($jobId, 99, 100, 'completed', $message);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
$summary = DomainSpiderMdRunHelper::run($payload, $logger);
|
||||
|
||||
DomainImportAsyncJobHelper::updateProgress($jobId, 100, 100, 'completed', '蜘蛛池MD生成完成');
|
||||
|
||||
return $summary;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace app\task\module;
|
||||
|
||||
use app\common\helper\VideoMetadataMissingRefreshHelper;
|
||||
use app\common\helper\DomainSpiderMdRunHelper;
|
||||
use app\model\PlanTaskModel;
|
||||
use app\task\logic\SiteMapLogic;
|
||||
use app\task\logic\VideoSiteMapLogic;
|
||||
@@ -98,6 +99,9 @@ class PlanTask
|
||||
case 'REFRESH_VIDEO_METADATA_TASK_POOL':
|
||||
self::refreshVideoMetadataTaskPool();
|
||||
break;
|
||||
case 'GENERATE_DOMAIN_SPIDER_MD':
|
||||
self::generateDomainSpiderMd();
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
@@ -141,6 +145,13 @@ class PlanTask
|
||||
]);
|
||||
}
|
||||
|
||||
public static function generateDomainSpiderMd(): array
|
||||
{
|
||||
return DomainSpiderMdRunHelper::run([
|
||||
'force_regenerate' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
|
||||
@@ -18,5 +18,6 @@ return [
|
||||
'video:metadata:workbench' => 'app\\command\\VideoMetadataMissingWorkbenchCommand',
|
||||
'video:metadata:task-pool' => 'app\\command\\VideoMetadataMissingTaskPoolCommand',
|
||||
'plan:seed:video-metadata-missing' => 'app\\command\\VideoMetadataMissingPlanTaskSeedCommand',
|
||||
'plan:seed:domain-spider-md' => 'app\\command\\DomainSpiderMdPlanTaskSeedCommand',
|
||||
],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user