Browse Source
feat(queue): 新增爆文复刻队列处理功能
feat(queue): 新增爆文复刻队列处理功能
- 创建 ExpiosiveReplica 队列消费者类,实现文章内容抓取与AI复刻逻辑 - 实现从URL抓取HTML并提取标题和内容的功能 - 添加配图获取及AI指令整合逻辑 - 更新 CreationTask 队列中配图分类查询方式,直接使用任务中的分类ID - 修改 ExpiosiveReplica 模型,增加 ai_time 字段的格式化方法 - 在 ExpiosiveReplicaService 中投递队列消息,并优化列表查询字段master
4 changed files with 232 additions and 5 deletions
-
4app/model/ExpiosiveReplica.php
-
7app/queue/redis/CreationTask.php
-
222app/queue/redis/ExpiosiveReplica.php
-
4app/service/ExpiosiveReplicaService.php
@ -0,0 +1,222 @@ |
|||||
|
<?php |
||||
|
namespace app\queue\redis; |
||||
|
|
||||
|
use app\dao\AiCommandDao; |
||||
|
use app\dao\EnterprisePortraitLibraryDao; |
||||
|
use app\dao\ExpiosiveReplicaDao; |
||||
|
use plugin\piadmin\app\utils\openai\OpenAiClient; |
||||
|
use Webman\RedisQueue\Consumer; |
||||
|
|
||||
|
class ExpiosiveReplica implements Consumer |
||||
|
{ |
||||
|
// 要消费的队列名
|
||||
|
public $queue = 'expiosive_replica'; |
||||
|
|
||||
|
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
|
||||
|
public $connection = 'default'; |
||||
|
|
||||
|
//抓取文章提示词
|
||||
|
private $fetchWebPrompt = '- Role: HTML内容解析专家和信息提取工程师 |
||||
|
- Background: 用户需要从提供的HTML代码中精确提取文章标题和内容。这表明用户已经获取了网页的HTML源代码,需要从中提取特定的信息,以便进行进一步的处理或分析。 |
||||
|
- Profile: 你是一位在HTML内容解析和信息提取领域有着丰富经验的专家,能够精准地从HTML代码中提取所需的信息。你熟悉HTML结构和数据提取技术,能够高效地完成任务。 |
||||
|
- Skills: 你具备HTML解析能力、数据提取技术、信息筛选技巧以及对网页结构的深刻理解。能够根据用户提供的HTML代码,准确提取文章标题和内容。 |
||||
|
- Goals: 根据用户提供的HTML代码,精确提取文章标题和内容,确保提取的信息准确无误。 |
||||
|
- Constrains: 提取的内容必须严格来自用户提供的HTML代码,确保信息的真实性和完整性。避免提取无关内容,确保提取的文章标题和内容清晰可读。 |
||||
|
- OutputFormat: 输出格式为JSON格式,将提取的文章标题放在`title`字段中,内容放在`content`字段中。 |
||||
|
- Workflow: |
||||
|
1. 接收用户提供的HTML代码。 |
||||
|
2. 解析HTML代码,定位文章标题和内容的HTML元素。 |
||||
|
3. 提取文章标题和内容,并进行格式化处理,确保信息清晰可读。 |
||||
|
4. 将提取的信息整合到JSON格式中,包括`title`和`content`字段。 |
||||
|
- Examples: |
||||
|
- 假设用户提供的HTML代码如下: |
||||
|
```html |
||||
|
<html> |
||||
|
<head> |
||||
|
<title>2025年科技行业发展趋势</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h1>2025年科技行业发展趋势</h1> |
||||
|
<p>随着科技的飞速发展,2025年科技行业呈现出许多新的趋势。</p> |
||||
|
<p>人工智能在医疗、教育和交通等领域的应用越来越广泛。</p> |
||||
|
<p>5G技术的普及也推动了物联网的发展。</p> |
||||
|
<p>此外,量子计算和区块链技术也在逐步走向商业化应用。</p> |
||||
|
</body> |
||||
|
</html> |
||||
|
``` |
||||
|
提取结果如下: |
||||
|
```json |
||||
|
{ |
||||
|
"title": "2025年科技行业发展趋势", |
||||
|
"content": "随着科技的飞速发展,2025年科技行业呈现出许多新的趋势。人工智能在医疗、教育和交通等领域的应用越来越广泛。5G技术的普及也推动了物联网的发展。此外,量子计算和区块链技术也在逐步走向商业化应用。" |
||||
|
}'; |
||||
|
|
||||
|
//爆文复刻用提示词
|
||||
|
private $defaultPrompt = '- Role: 爆文复刻专家、内容优化顾问及视觉内容整合师 |
||||
|
- Background: 用户需要对提供的标题和文章进行复刻,以满足特定的要求。用户会提供一些配图,并指定在文章合适的位置插入这些图片。这意味着用户希望在保留原文核心内容和风格的基础上,对文章进行优化和调整,使其更接近“爆文”的标准,同时通过视觉元素增强文章的吸引力和可读性。 |
||||
|
- Profile: 你是一位在爆文创作、内容优化和视觉内容整合领域有着丰富经验的专家,能够精准地把握爆文的特点和用户的需求,对文章进行高质量的复刻。你熟悉各种写作风格和内容结构,能够根据用户的要求,对文章进行有效的调整和优化,同时保留原文的核心价值和风格。 |
||||
|
- Skills: 你具备爆文创作能力、内容优化技巧、语言表达能力、视觉内容整合能力以及对用户需求的敏锐洞察力。能够根据用户提供的标题、文章和配图,结合复刻要求,对文章进行精准的调整和优化,并合理插入配图。 |
||||
|
- Goals: 根据用户提供的标题、文章和配图,按照用户的要求进行复刻,确保复刻后的文章更接近“爆文”的标准,同时保留原文的核心内容和风格,并在合适的位置插入配图,增强文章的视觉效果和可读性。 |
||||
|
- Constrains: 复刻后的文章必须严格遵循用户的要求,包括内容调整、风格优化、语言润色等。确保复刻后的文章在满足要求的同时,保持原有的核心信息和风格特点。配图插入位置应合理,与文章内容紧密相关,增强文章的可读性和视觉效果。 |
||||
|
- OutputFormat: 输出格式为完整的文章正文,包括复刻后的标题和内容。如果用户有特定的格式要求,应严格按照要求输出。配图应插入到文章的合适位置,图片数量符合用户指定的要求。 |
||||
|
- Workflow: |
||||
|
1. 接收用户提供的标题、文章和配图。 |
||||
|
2. 仔细分析用户提出的复刻要求,明确复刻的方向和重点。 |
||||
|
3. 根据复刻要求,对文章的标题和内容进行优化和调整,提升文章的吸引力和可读性。 |
||||
|
4. 确定配图的插入位置,确保图片与文章内容紧密相关,增强视觉效果。 |
||||
|
5. 将配图合理插入到文章的合适位置,完成复刻。 |
||||
|
6. 审核复刻后的文章,确保内容符合用户的要求,语言流畅且逻辑清晰,用户提出的要求仅针对提供的标题和内容,不针对复刻后的标题和内容。 |
||||
|
- Examples: |
||||
|
- 假设用户提供的标题是“如何提高工作效率”,文章内容如下: |
||||
|
``` |
||||
|
在当今快节奏的工作环境中,提高工作效率是每个职场人士都关心的问题。以下是一些实用的建议: |
||||
|
- 制定清晰的工作计划。 |
||||
|
- 优先处理重要任务。 |
||||
|
- 避免不必要的干扰。 |
||||
|
- 定期休息,保持精力充沛。 |
||||
|
``` |
||||
|
用户提供了两张配图,并要求在合适的位置插入: |
||||
|
```json |
||||
|
{ |
||||
|
"title": "工作效率翻倍的秘密:职场高手的4个绝招", |
||||
|
"content": "在快节奏的职场竞争中,工作效率就是生命线!今天,就来揭秘那些职场高手如何轻松搞定工作,效率翻倍的秘密。\n\n首先,高手们都会制定一份超清晰的工作计划,每一步都精准无误。其次,他们总是优先搞定那些最重要的任务,绝不拖泥带水。\n\n再来,他们懂得如何屏蔽那些无意义的干扰,专注就是他们的超能力。最后,别小看定期休息,这可是保持精力充沛的不二法门。赶紧试试这些绝招,让你的工作效率瞬间提升!" |
||||
|
}。 |
||||
|
我提供的JSON数据:配图(images)。创作要求(prompt),仅针对提供的内容(content),不针对创作的文章,尤其是不影响配图的插入。标题(title),内容(contnet)。数据:'; |
||||
|
|
||||
|
// 消费
|
||||
|
public function consume($data) |
||||
|
{ |
||||
|
echo "开始消费爆发复刻,数据ID为:" . $data['replica_id'] . "\n"; |
||||
|
//获取复刻任务
|
||||
|
$replicaDao = app()->make(ExpiosiveReplicaDao::class); |
||||
|
//状态改为复刻
|
||||
|
$replicaDao->update($data['replica_id'], ['status' => 2]); |
||||
|
//获取复刻任务明细
|
||||
|
$replicaTask = $replicaDao->get($data['replica_id']); |
||||
|
//解析url获取文章内容
|
||||
|
$html = $this->fetchArticleContent($replicaTask['url']); |
||||
|
$fetch_prompt = $this->fetchWebPrompt . ',需要提取的html为:' . $html; |
||||
|
$result = OpenAiClient::chat($fetch_prompt); |
||||
|
$title = $result['content']['title']; |
||||
|
$content = $result['content']['content']; |
||||
|
//拼接提示词
|
||||
|
$prompts = array_merge($this->getPortraitLibrary($replicaTask),$this->getAiCommand($replicaTask)); |
||||
|
$prompts['title'] = $title; |
||||
|
$prompts['content'] = $content; |
||||
|
$finalPrompt = $this->defaultPrompt . '\n' . json_encode($prompts, JSON_UNESCAPED_UNICODE); |
||||
|
// echo "最终提示词:\n";
|
||||
|
// var_export($finalPrompt);
|
||||
|
$airesult = OpenAiClient::chat($finalPrompt); |
||||
|
$updata = [ |
||||
|
'url_web' => $html, |
||||
|
'title' => $result['content']['title'], |
||||
|
'content' => $result['content']['content'], |
||||
|
'ai_title' => $airesult['content']['title'], |
||||
|
'ai_content' => $airesult['content']['content'], |
||||
|
'status' => 3, |
||||
|
'ai_time' => time() |
||||
|
]; |
||||
|
var_export($updata); |
||||
|
//更改任务状态为完成
|
||||
|
$replicaDao->update($data['replica_id'], $updata); |
||||
|
var_export('任务完成'); |
||||
|
} |
||||
|
|
||||
|
public function onConsumeFailure(\Throwable $e, $package) |
||||
|
{ |
||||
|
echo "执行失败\n"; |
||||
|
echo $e->getMessage() . "\n"; |
||||
|
echo $e->getFile() . "\n"; |
||||
|
echo $e->getLine() . "\n"; |
||||
|
// 无需反序列化
|
||||
|
var_export($package); |
||||
|
//记录失败
|
||||
|
$replicaDao = app()->make(ExpiosiveReplicaDao::class); |
||||
|
$replicaDao->update($package['data']['replica_id'], [ |
||||
|
'status' => 4, |
||||
|
'try_count' => $package['attempts'], |
||||
|
'status_msg' => $package['error'] |
||||
|
]); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取配图及使用数量 |
||||
|
*/ |
||||
|
private function getPortraitLibrary($replicaTask) |
||||
|
{ |
||||
|
echo "开始获取配图\n"; |
||||
|
$libraryDao = app()->make(EnterprisePortraitLibraryDao::class); |
||||
|
$library = $libraryDao->getColumn(['category_id' => $replicaTask['portrait_category_id']], 'url'); |
||||
|
echo "获取到配图:" . count($library) . "\n"; |
||||
|
var_export($library); |
||||
|
if (!$library) { |
||||
|
return [ |
||||
|
'images' => [], |
||||
|
]; |
||||
|
} |
||||
|
return [ |
||||
|
'images' => $library, |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取创作指令 |
||||
|
* type = 3 流量复刻 |
||||
|
*/ |
||||
|
private function getAiCommand($replicaTask) |
||||
|
{ |
||||
|
echo "开始获取指令:" . 3 . "\n"; |
||||
|
$aiCommandDao = app()->make(AiCommandDao::class); |
||||
|
$ai_command_id = $replicaTask['ai_command_id']; |
||||
|
$command = $aiCommandDao->getOne([ |
||||
|
'type' => 3, |
||||
|
'id' => $ai_command_id |
||||
|
]); |
||||
|
echo "获取到指令:" . $command['content'] . "\n"; |
||||
|
if (!$command) { |
||||
|
return [ |
||||
|
'prompt' => '' |
||||
|
]; |
||||
|
} |
||||
|
return [ |
||||
|
'prompt' => $command['content'] |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 从URL抓取文章内容 |
||||
|
* @param string $url |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function fetchArticleContent(string $url): string |
||||
|
{ |
||||
|
// 使用 cURL 获取网页内容
|
||||
|
$ch = curl_init(); |
||||
|
curl_setopt($ch, CURLOPT_URL, $url); |
||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); |
||||
|
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'); |
||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30); |
||||
|
$html = curl_exec($ch); |
||||
|
curl_close($ch); |
||||
|
// 移除script标签及其内容
|
||||
|
$html = preg_replace('/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/mi', '', $html); |
||||
|
// 移除style标签及其内容
|
||||
|
$html = preg_replace('/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/mi', '', $html); |
||||
|
// 移除link标签(通常用于引入CSS)
|
||||
|
$html = preg_replace('/<link\b[^>]*>/mi', '', $html); |
||||
|
// 移除meta标签
|
||||
|
$html = preg_replace('/<meta\b[^>]*>/mi', '', $html); |
||||
|
// 移除所有标签的style属性(行内样式)
|
||||
|
$html = preg_replace('/(<[^>]+) style=".*?"/i', '$1', $html); |
||||
|
// 移除所有标签的class属性
|
||||
|
$html = preg_replace('/(<[^>]+) class=".*?"/i', '$1', $html); |
||||
|
// 移除所有标签的id属性
|
||||
|
$html = preg_replace('/(<[^>]+) id=".*?"/i', '$1', $html); |
||||
|
// 移除多余的空白行
|
||||
|
$html = preg_replace('/^\s*[\r\n]/m', '', $html); |
||||
|
$html = str_replace(' ', '', $html); |
||||
|
return $html; |
||||
|
|
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue