mirror of
https://gitee.com/ulthon/ulthon_information.git
synced 2026-07-07 16:02:47 +08:00
feat(post): 新增手机图片排版与AI智能排版功能
- 新增手机图片排版功能,支持小红书/抖音尺寸输出 - 新增AI智能排版顾问,支持内容分析与优化推荐 - 新增AI供应商管理,支持多渠道配置与同步 - 新增文章输出管理页面,支持图片预览与批量下载 - 新增字体文件与排版样式配置
This commit is contained in:
215
app/common/tools/PhoneImage.php
Normal file
215
app/common/tools/PhoneImage.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\tools;
|
||||
|
||||
use app\model\Post;
|
||||
use app\model\PostOutput;
|
||||
use app\model\PostOutputFile;
|
||||
use think\facade\App;
|
||||
|
||||
class PhoneImage implements PostOutputManagerInterface
|
||||
{
|
||||
/**
|
||||
* 返回配置字段定义
|
||||
*/
|
||||
public function getConfigFields(): array
|
||||
{
|
||||
return [
|
||||
'template' => ['type' => 'select', 'options' => ['minimal', 'magazine', 'mixed'], 'default' => 'minimal'],
|
||||
'size' => ['type' => 'select', 'options' => ['xiaohongshu', 'douyin'], 'default' => 'xiaohongshu'],
|
||||
'font' => ['type' => 'select', 'options' => ['source-han-sans', 'alibaba-puhuiti', 'lxgw-wenkai'], 'default' => 'source-han-sans'],
|
||||
'font_size' => ['type' => 'number', 'default' => 14, 'min' => 10, 'max' => 24],
|
||||
'watermark' => ['type' => 'text', 'default' => ''],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证配置合法性
|
||||
*/
|
||||
public function validateConfig(array $config): bool
|
||||
{
|
||||
$fields = $this->getConfigFields();
|
||||
|
||||
if (isset($config['template']) && !in_array($config['template'], $fields['template']['options'])) {
|
||||
return false;
|
||||
}
|
||||
if (isset($config['size']) && !in_array($config['size'], $fields['size']['options'])) {
|
||||
return false;
|
||||
}
|
||||
if (isset($config['font']) && !in_array($config['font'], $fields['font']['options'])) {
|
||||
return false;
|
||||
}
|
||||
if (isset($config['font_size'])) {
|
||||
$size = intval($config['font_size']);
|
||||
if ($size < $fields['font_size']['min'] || $size > $fields['font_size']['max']) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行输出处理 - 服务端接收base64数据,保存文件,创建记录
|
||||
* 注意:HTML渲染在前端完成(html2canvas),服务端只做文件保存和记录创建
|
||||
*/
|
||||
public function process(Post $post, array $config): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回预览HTML - 预览在前端完成,服务端返回空
|
||||
*/
|
||||
public function getPreview(Post $post, array $config): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存单页图片
|
||||
* @param int $outputId 输出记录ID
|
||||
* @param int $page 页码(从1开始)
|
||||
* @param string $imageData base64编码的图片数据
|
||||
* @return PostOutputFile|false
|
||||
*/
|
||||
public function savePageImage(int $outputId, int $page, string $imageData)
|
||||
{
|
||||
$imageData = str_replace('data:image/jpeg;base64,', '', $imageData);
|
||||
$imageData = str_replace('data:image/png;base64,', '', $imageData);
|
||||
$imageData = str_replace(' ', '+', $imageData);
|
||||
$decoded = base64_decode($imageData);
|
||||
|
||||
if ($decoded === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dateDir = date('Ymd');
|
||||
$relativeDir = '/upload/post_output/' . $dateDir;
|
||||
$fileName = $outputId . '_' . $page . '.jpg';
|
||||
$relativePath = $relativeDir . '/' . $fileName;
|
||||
$fullDir = App::getRootPath() . '/public' . $relativeDir;
|
||||
$fullPath = $fullDir . '/' . $fileName;
|
||||
|
||||
if (!is_dir($fullDir)) {
|
||||
mkdir($fullDir, 0777, true);
|
||||
}
|
||||
|
||||
file_put_contents($fullPath, $decoded);
|
||||
|
||||
$imageInfo = getimagesize($fullPath);
|
||||
$width = $imageInfo[0] ?? 0;
|
||||
$height = $imageInfo[1] ?? 0;
|
||||
|
||||
$fileRecord = PostOutputFile::create([
|
||||
'output_id' => $outputId,
|
||||
'page' => $page,
|
||||
'file_path' => $relativePath,
|
||||
'file_url' => $relativePath,
|
||||
'file_size' => strlen($decoded),
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
]);
|
||||
|
||||
return $fileRecord;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包某条输出记录的所有图片为ZIP
|
||||
* @param int $outputId
|
||||
* @return string ZIP文件路径
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createZip(int $outputId): string
|
||||
{
|
||||
$output = PostOutput::find($outputId);
|
||||
if (!$output) {
|
||||
throw new \Exception('输出记录不存在');
|
||||
}
|
||||
|
||||
$files = PostOutputFile::where('output_id', $outputId)
|
||||
->order('page', 'asc')
|
||||
->select();
|
||||
|
||||
if ($files->isEmpty()) {
|
||||
throw new \Exception('没有可下载的文件');
|
||||
}
|
||||
|
||||
$tempDir = App::getRuntimePath() . 'temp';
|
||||
if (!is_dir($tempDir)) {
|
||||
mkdir($tempDir, 0777, true);
|
||||
}
|
||||
|
||||
$zipFileName = 'post_output_' . $outputId . '.zip';
|
||||
$zipPath = $tempDir . '/' . $zipFileName;
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
|
||||
throw new \Exception('无法创建ZIP文件');
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
$filePath = App::getRootPath() . '/public' . $file->file_path;
|
||||
if (file_exists($filePath)) {
|
||||
$pageName = str_pad((string) $file->page, 2, '0', STR_PAD_LEFT) . '.jpg';
|
||||
$zip->addFile($filePath, $pageName);
|
||||
}
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
return $zipPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除输出记录及所有关联文件
|
||||
* @param int $outputId
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteOutput(int $outputId): bool
|
||||
{
|
||||
$files = PostOutputFile::where('output_id', $outputId)->select();
|
||||
|
||||
foreach ($files as $file) {
|
||||
$filePath = App::getRootPath() . '/public' . $file->file_path;
|
||||
if (file_exists($filePath)) {
|
||||
@unlink($filePath);
|
||||
}
|
||||
}
|
||||
|
||||
PostOutputFile::where('output_id', $outputId)->delete();
|
||||
PostOutput::destroy($outputId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建输出记录
|
||||
* @param int $postId 文章ID
|
||||
* @param array $config 配置
|
||||
* @param int $adminId 管理员ID
|
||||
* @return PostOutput
|
||||
*/
|
||||
public function createOutput(int $postId, array $config, int $adminId): PostOutput
|
||||
{
|
||||
return PostOutput::create([
|
||||
'post_id' => $postId,
|
||||
'output_type' => 'phone_image',
|
||||
'config' => $config,
|
||||
'status' => PostOutput::STATUS_PROCESSING,
|
||||
'page_count' => 0,
|
||||
'admin_id' => $adminId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成输出记录 - 更新状态和页数
|
||||
*/
|
||||
public function completeOutput(int $outputId, int $pageCount): bool
|
||||
{
|
||||
return PostOutput::where('id', $outputId)->update([
|
||||
'status' => PostOutput::STATUS_COMPLETED,
|
||||
'page_count' => $pageCount,
|
||||
]) > 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user