|
|
@@ -38,12 +38,20 @@ class ImageToImageJob{
|
|
|
if (is_array($result) && isset($result['code']) && $result['code'] !== 0) {
|
|
|
throw new \Exception($result['msg'] ?? '图生图失败');
|
|
|
}
|
|
|
- echo "🎉 任务 {$taskId} 执行完成,图片生成成功!\n";
|
|
|
+ if (is_array($result) && !empty($result['success_report'])) {
|
|
|
+ echo $result['success_report'] . "\n";
|
|
|
+ } else {
|
|
|
+ $imgPath = is_array($result) ? ($result['path'] ?? '') : $result;
|
|
|
+ echo "🎉 任务 {$taskId} 执行完成,图片生成成功!\n";
|
|
|
+ if ($imgPath !== '') {
|
|
|
+ echo "生成图片:{$imgPath}\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
echo "结束时间:" . date('Y-m-d H:i:s') . "\n";
|
|
|
$job->delete();
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
- echo "图生图失败: " . $e->getMessage() . "\n";
|
|
|
+ echo $e->getMessage() . "\n";
|
|
|
\think\Log::error('[ImageToImageJob] ' . $e->getMessage());
|
|
|
$job->delete();
|
|
|
}
|
|
|
@@ -170,6 +178,7 @@ class ImageToImageJob{
|
|
|
* 业务分支:
|
|
|
* - ProductImageGeneration:产品图创作(前端传 base64,先调模型成功后再落盘入库)
|
|
|
* - ProductTemplateReplace:产品替换(读取已有产品图/模板图路径,生成效果图并返回前端展示)
|
|
|
+ * - ProductWhiteBackground:产品白底抠图(单张 product_img,结果写入 product_white_background 表)
|
|
|
*
|
|
|
* 入参说明($data):
|
|
|
* - prompt: 提示词
|
|
|
@@ -187,7 +196,7 @@ class ImageToImageJob{
|
|
|
*/
|
|
|
public function get_img_to_img($data)
|
|
|
{
|
|
|
- // 1) 基础参数解析
|
|
|
+ //接收参数
|
|
|
$prompt = trim($data['prompt'] ?? '');
|
|
|
$size = trim($data['size'] ?? '');
|
|
|
$statusVal = trim($data['status_val'] ?? '');
|
|
|
@@ -200,19 +209,9 @@ class ImageToImageJob{
|
|
|
$width = trim((string)($data['width'] ?? ''));
|
|
|
$model = trim($data['model'] ?? '');
|
|
|
$sysId = $data['sys_id'];
|
|
|
- $now = date('Y-m-d H:i:s');
|
|
|
-
|
|
|
- // texttoimage:前端传 old_img(base64/路径),size 为空时用 width x height
|
|
|
- if ($statusType === 'texttoimage') {
|
|
|
- if ($size === '' && $width !== '' && $height !== '') {
|
|
|
- $size = $width . 'x' . $height;
|
|
|
- }
|
|
|
- if ($size === '') {
|
|
|
- $size = '1:1';
|
|
|
- }
|
|
|
- }
|
|
|
+ $sysrq = date('Y-m-d H:i:s');
|
|
|
|
|
|
- // 失败统一回写任务状态,避免多处重复代码
|
|
|
+ /** 失败回调:写入 Redis 任务状态为「失败」,并返回统一错误结构 */
|
|
|
$fail = function ($msg) use ($data) {
|
|
|
if (!empty($data['task_id'])) {
|
|
|
try {
|
|
|
@@ -224,30 +223,13 @@ class ImageToImageJob{
|
|
|
'completed_at' => date('Y-m-d H:i:s')
|
|
|
], JSON_UNESCAPED_UNICODE), ['EX' => 300]);
|
|
|
} catch (\Exception $e) {
|
|
|
- // Redis 不可用时不阻断主流程
|
|
|
+ // Redis 不可用时仍返回错误,不阻断主流程
|
|
|
}
|
|
|
}
|
|
|
return ['code' => 1, 'msg' => $msg];
|
|
|
};
|
|
|
|
|
|
- // 成功统一回写任务状态
|
|
|
- $complete = function ($imgPath) use ($data) {
|
|
|
- if (!empty($data['task_id'])) {
|
|
|
- try {
|
|
|
- $redis = getTaskRedis();
|
|
|
- $redis->set("img_to_img_task:" . $data['task_id'], json_encode([
|
|
|
- 'status' => ImageService::TASK_STATUS_COMPLETED,
|
|
|
- 'image' => $imgPath,
|
|
|
- 'image_url' => $imgPath,
|
|
|
- 'completed_at' => date('Y-m-d H:i:s')
|
|
|
- ], JSON_UNESCAPED_UNICODE), ['EX' => 300]);
|
|
|
- } catch (\Exception $e) {
|
|
|
- // Redis 不可用时不阻断主流程
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- // 2) 解析输入图片(按页面类型分支)
|
|
|
+ // ========== 第二步:按 status_type 解析图片、拼提示词 ==========
|
|
|
$productBase64 = null;
|
|
|
$productMime = 'image/png';
|
|
|
$templateBase64 = null;
|
|
|
@@ -256,43 +238,14 @@ class ImageToImageJob{
|
|
|
$templateExt = 'png';
|
|
|
$sourceProductPath = '';
|
|
|
$sourceTemplatePath = '';
|
|
|
+ $texttoimageSaveDir = '';
|
|
|
+ $texttoimageDateSegment = '';
|
|
|
+ $whiteBgSaveDir = '';
|
|
|
+ $whiteBgDateSegment = '';
|
|
|
+ $productDbPath = '';
|
|
|
+ $promptContent = '';
|
|
|
|
|
|
- if ($statusType === 'ProductImageGeneration') {
|
|
|
- // 产品图创作:前端直接传 base64,先解析,等模型成功后再入库/落盘
|
|
|
- preg_match('/data:image\/(png|jpg|jpeg);base64,([^"]+)/', $productImg, $pm);
|
|
|
- if (empty($pm)) {
|
|
|
- return $fail('产品图未找到图片数据');
|
|
|
- }
|
|
|
- $productBase64 = preg_replace('/\s+/', '', $pm[2]);
|
|
|
- $productMime = ($pm[1] === 'jpg' ? 'image/jpeg' : 'image/' . $pm[1]);
|
|
|
- $productExt = $pm[1];
|
|
|
-
|
|
|
- preg_match('/data:image\/(png|jpg|jpeg);base64,([^"]+)/', $templateImg, $tm);
|
|
|
- if (empty($tm)) {
|
|
|
- return $fail('模板图未找到图片数据');
|
|
|
- }
|
|
|
- $templateBase64 = preg_replace('/\s+/', '', $tm[2]);
|
|
|
- $templateMime = ($tm[1] === 'jpg' ? 'image/jpeg' : 'image/' . $tm[1]);
|
|
|
- $templateExt = $tm[1];
|
|
|
- } elseif ($statusType === 'ProductTemplateReplace') {
|
|
|
- // 产品替换:优先转为 OSS 完整 URL 再读取,兼容库中存相对路径
|
|
|
- $productImgSource = Common::ossFullUrl((string)$productImg);
|
|
|
- $templateImgSource = Common::ossFullUrl((string)$templateImg);
|
|
|
-
|
|
|
- try {
|
|
|
- $productImgRaw = AIGatewayService::file_get_contents($productImgSource);
|
|
|
- $productBase64 = $productImgRaw['base64Data'];
|
|
|
- $productMime = $productImgRaw['mimeType'];
|
|
|
-
|
|
|
- $templateImgRaw = AIGatewayService::file_get_contents($templateImgSource);
|
|
|
- $templateBase64 = $templateImgRaw['base64Data'];
|
|
|
- $templateMime = $templateImgRaw['mimeType'];
|
|
|
- } catch (\Exception $e) {
|
|
|
- // 回传清晰错误,方便定位是产品图还是模板图读取失败
|
|
|
- return $fail('读取产品图/模板图失败: ' . $e->getMessage());
|
|
|
- }
|
|
|
- } elseif ($statusType === 'texttoimage') {
|
|
|
- // 前端传 old_img(原图 base64 或路径),ref_img(参考图,可选)
|
|
|
+ if ($statusType === 'texttoimage') {
|
|
|
$inputOldImg = $oldImg !== '' ? $oldImg : $productImg;
|
|
|
$inputRefImg = $refImg !== '' ? $refImg : $templateImg;
|
|
|
|
|
|
@@ -304,8 +257,7 @@ class ImageToImageJob{
|
|
|
} elseif ($inputOldImg !== '') {
|
|
|
$sourceProductPath = ltrim(str_replace('\\', '/', $inputOldImg), '/');
|
|
|
try {
|
|
|
- $productImgSource = Common::ossFullUrl((string)$inputOldImg);
|
|
|
- $productImgRaw = AIGatewayService::file_get_contents($productImgSource);
|
|
|
+ $productImgRaw = AIGatewayService::file_get_contents(Common::ossFullUrl((string)$inputOldImg));
|
|
|
$productBase64 = $productImgRaw['base64Data'];
|
|
|
$productMime = $productImgRaw['mimeType'];
|
|
|
} catch (\Exception $e) {
|
|
|
@@ -324,8 +276,7 @@ class ImageToImageJob{
|
|
|
} else {
|
|
|
$sourceTemplatePath = ltrim(str_replace('\\', '/', $inputRefImg), '/');
|
|
|
try {
|
|
|
- $templateImgSource = Common::ossFullUrl((string)$inputRefImg);
|
|
|
- $templateImgRaw = AIGatewayService::file_get_contents($templateImgSource);
|
|
|
+ $templateImgRaw = AIGatewayService::file_get_contents(Common::ossFullUrl((string)$inputRefImg));
|
|
|
$templateBase64 = $templateImgRaw['base64Data'];
|
|
|
$templateMime = $templateImgRaw['mimeType'];
|
|
|
} catch (\Exception $e) {
|
|
|
@@ -333,62 +284,165 @@ class ImageToImageJob{
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
- return $fail('当前页面未进行配置,请联系管理员开通权限');
|
|
|
- }
|
|
|
|
|
|
- // 3) 调模型生成图像
|
|
|
- if ($statusType === 'texttoimage') {
|
|
|
- $promptContent = $prompt !== '' ? $prompt : '请根据原图生成高质量图片';
|
|
|
-
|
|
|
- // 原图/参考图先落盘并上传 OSS(即使 AI 失败也能在 OSS 看到 texttoimage 目录)
|
|
|
+ $texttoimageDateSegment = date('Y-m-d');
|
|
|
$rootPath = str_replace('\\', '/', ROOT_PATH);
|
|
|
- $dateSegment = date('Y-m-d');
|
|
|
- $saveDir = rtrim($rootPath, '/') . '/public/uploads/texttoimage/' . $dateSegment . '/';
|
|
|
- if (!is_dir($saveDir)) {
|
|
|
- mkdir($saveDir, 0755, true);
|
|
|
+ $texttoimageSaveDir = rtrim($rootPath, '/') . '/public/uploads/texttoimage/' . $texttoimageDateSegment . '/';
|
|
|
+ if (!is_dir($texttoimageSaveDir)) {
|
|
|
+ mkdir($texttoimageSaveDir, 0755, true);
|
|
|
}
|
|
|
+
|
|
|
if ($sourceProductPath === '' && !empty($productBase64)) {
|
|
|
$sourceFile = 'source-' . uniqid() . '.' . $productExt;
|
|
|
$sourceImageData = base64_decode($productBase64);
|
|
|
- if ($sourceImageData === false || !file_put_contents($saveDir . $sourceFile, $sourceImageData)) {
|
|
|
+ if ($sourceImageData === false || !file_put_contents($texttoimageSaveDir . $sourceFile, $sourceImageData)) {
|
|
|
return $fail('原图保存失败');
|
|
|
}
|
|
|
- $sourceProductPath = 'uploads/texttoimage/' . $dateSegment . '/' . $sourceFile;
|
|
|
- if (!Common::uploadLocalFileToOss((string)($saveDir . $sourceFile), (string)$sourceProductPath)) {
|
|
|
+ $sourceProductPath = 'uploads/texttoimage/' . $texttoimageDateSegment . '/' . $sourceFile;
|
|
|
+ if (!Common::uploadLocalFileToOss((string)($texttoimageSaveDir . $sourceFile), (string)$sourceProductPath)) {
|
|
|
echo 'OSS 上传原图失败: ' . $sourceProductPath . "\n";
|
|
|
}
|
|
|
}
|
|
|
if ($sourceTemplatePath === '' && !empty($templateBase64)) {
|
|
|
$refFile = 'ref-' . uniqid() . '.' . $templateExt;
|
|
|
$refImageData = base64_decode($templateBase64);
|
|
|
- if ($refImageData !== false && file_put_contents($saveDir . $refFile, $refImageData)) {
|
|
|
- $sourceTemplatePath = 'uploads/texttoimage/' . $dateSegment . '/' . $refFile;
|
|
|
- if (!Common::uploadLocalFileToOss((string)($saveDir . $refFile), (string)$sourceTemplatePath)) {
|
|
|
+ if ($refImageData !== false && file_put_contents($texttoimageSaveDir . $refFile, $refImageData)) {
|
|
|
+ $sourceTemplatePath = 'uploads/texttoimage/' . $texttoimageDateSegment . '/' . $refFile;
|
|
|
+ if (!Common::uploadLocalFileToOss((string)($texttoimageSaveDir . $refFile), (string)$sourceTemplatePath)) {
|
|
|
echo 'OSS 上传参考图失败: ' . $sourceTemplatePath . "\n";
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
+
|
|
|
+ $promptContent = $prompt !== '' ? $prompt : '请根据原图生成高质量图片';
|
|
|
+
|
|
|
+ } elseif ($statusType === 'ProductImageGeneration') {
|
|
|
+ preg_match('/data:image\/(png|jpg|jpeg);base64,([^"]+)/', $productImg, $pm);
|
|
|
+ if (empty($pm)) {
|
|
|
+ return $fail('产品图未找到图片数据');
|
|
|
+ }
|
|
|
+ $productBase64 = preg_replace('/\s+/', '', $pm[2]);
|
|
|
+ $productMime = ($pm[1] === 'jpg' ? 'image/jpeg' : 'image/' . $pm[1]);
|
|
|
+ $productExt = $pm[1];
|
|
|
+
|
|
|
+ preg_match('/data:image\/(png|jpg|jpeg);base64,([^"]+)/', $templateImg, $tm);
|
|
|
+ if (empty($tm)) {
|
|
|
+ return $fail('模板图未找到图片数据');
|
|
|
+ }
|
|
|
+ $templateBase64 = preg_replace('/\s+/', '', $tm[2]);
|
|
|
+ $templateMime = ($tm[1] === 'jpg' ? 'image/jpeg' : 'image/' . $tm[1]);
|
|
|
+ $templateExt = $tm[1];
|
|
|
+
|
|
|
+ $defaultPrompt = '请完成产品模板替换:
|
|
|
+ 1. 从产品图提取产品主体、品牌名称、核心文案;
|
|
|
+ 2. 从模板图继承版式布局、文字排版、色彩风格、背景元素;
|
|
|
+ 3. 将模板图中的产品和文字替换为产品图的内容;
|
|
|
+ 4. 最终生成的图片与模板图视觉风格统一,仅替换产品和文字。';
|
|
|
+ $promptContent = $prompt.$defaultPrompt;
|
|
|
+
|
|
|
+ } elseif ($statusType === 'ProductTemplateReplace') {
|
|
|
+ $productImgRaw = AIGatewayService::file_get_contents(Common::ossFullUrl((string)$productImg));
|
|
|
+ $productBase64 = $productImgRaw['base64Data'];
|
|
|
+ $productMime = $productImgRaw['mimeType'];
|
|
|
+
|
|
|
+ $templateImgRaw = AIGatewayService::file_get_contents(Common::ossFullUrl((string)$templateImg));
|
|
|
+ $templateBase64 = $templateImgRaw['base64Data'];
|
|
|
+ $templateMime = $templateImgRaw['mimeType'];
|
|
|
+
|
|
|
$defaultPrompt = '请完成产品模板替换:
|
|
|
1. 从产品图提取产品主体、品牌名称、核心文案;
|
|
|
2. 从模板图继承版式布局、文字排版、色彩风格、背景元素;
|
|
|
3. 将模板图中的产品和文字替换为产品图的内容;
|
|
|
4. 最终生成的图片与模板图视觉风格统一,仅替换产品和文字。';
|
|
|
- $promptContent = $prompt ? $prompt . "\n\n" . $defaultPrompt : $defaultPrompt;
|
|
|
+ $promptContent = $prompt.$defaultPrompt;
|
|
|
+
|
|
|
+ } elseif ($statusType === 'ProductWhiteBackground') {
|
|
|
+ //处理图片抠图
|
|
|
+ if (preg_match('/data:image\/(png|jpg|jpeg|webp);base64,(.+)$/is', $productImg, $pm)) {
|
|
|
+ $productBase64 = preg_replace('/\s+/', '', $pm[2]);
|
|
|
+ $ext = strtolower($pm[1]);
|
|
|
+ $productMime = ($ext === 'jpg' ? 'image/jpeg' : 'image/' . $ext);
|
|
|
+ $productExt = ($ext === 'jpeg' ? 'jpg' : $ext);
|
|
|
+ } elseif ($productImg !== '') {
|
|
|
+ try {
|
|
|
+ $productImgRaw = AIGatewayService::file_get_contents(Common::ossFullUrl((string)$productImg));
|
|
|
+ $productBase64 = $productImgRaw['base64Data'];
|
|
|
+ $productMime = $productImgRaw['mimeType'];
|
|
|
+ $productDbPath = ltrim(str_replace('\\', '/', $productImg), '/');
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ return $fail('读取图片失败: ' . $e->getMessage());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return $fail('图片不能为空');
|
|
|
+ }
|
|
|
+
|
|
|
+ $whiteBgDateSegment = date('Y-m-d');
|
|
|
+ $rootPath = str_replace('\\', '/', ROOT_PATH);
|
|
|
+ $whiteBgSaveDir = rtrim($rootPath, '/') . '/public/uploads/whitebackground/' . $whiteBgDateSegment . '/';
|
|
|
+ if (!is_dir($whiteBgSaveDir)) {
|
|
|
+ mkdir($whiteBgSaveDir, 0755, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($productDbPath === '' && !empty($productBase64)) {
|
|
|
+ $sourceFile = 'source-' . uniqid() . '.' . $productExt;
|
|
|
+ $sourceImageData = base64_decode($productBase64);
|
|
|
+ if ($sourceImageData === false || !file_put_contents($whiteBgSaveDir . $sourceFile, $sourceImageData)) {
|
|
|
+ return $fail('产品图保存失败');
|
|
|
+ }
|
|
|
+ $productDbPath = 'uploads/whitebackground/' . $whiteBgDateSegment . '/' . $sourceFile;
|
|
|
+ if (!Common::uploadLocalFileToOss((string)($whiteBgSaveDir . $sourceFile), (string)$productDbPath)) {
|
|
|
+ echo 'OSS 上传产品原图失败: ' . $productDbPath . "\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $promptContent = $prompt !== ''
|
|
|
+ ? $prompt
|
|
|
+ : '提取图中产品,更换成白底,保留原始色彩,不改变产品镜头朝向和主要结构,像一个全新的没有瑕疵的产品,并精修产品,清除所有指纹,灰尘与瑕疵';
|
|
|
+
|
|
|
+ } else {
|
|
|
+ return $fail('当前页面未进行配置,请联系管理员开通权限');
|
|
|
}
|
|
|
+
|
|
|
+ //调模型生成图像(统一入口,只调一次)
|
|
|
$aiGateway = new AIGatewayService();
|
|
|
- // texttoimage:原图 + 提示词走图生图(gemini-3-pro-image-preview)
|
|
|
- $res = $aiGateway->buildRequestData(
|
|
|
- $statusVal,
|
|
|
- $model,
|
|
|
- $promptContent,
|
|
|
- $size,
|
|
|
- (string)$productBase64,
|
|
|
- $productMime,
|
|
|
- (string)$templateBase64,
|
|
|
- $templateMime
|
|
|
- );
|
|
|
+
|
|
|
+ /** 成功回调:写入 Redis(含实际模型),供前端轮询 */
|
|
|
+ $complete = function ($imgPath) use ($data, $aiGateway) {
|
|
|
+ if (!empty($data['task_id'])) {
|
|
|
+ try {
|
|
|
+ $meta = $aiGateway->getLastImg2ImgSuccessMeta();
|
|
|
+ $payload = [
|
|
|
+ 'status' => ImageService::TASK_STATUS_COMPLETED,
|
|
|
+ 'image' => $imgPath,
|
|
|
+ 'image_url' => $imgPath,
|
|
|
+ 'completed_at' => date('Y-m-d H:i:s'),
|
|
|
+ ];
|
|
|
+ if (!empty($meta)) {
|
|
|
+ $payload['model'] = $meta['model_name'];
|
|
|
+ $payload['model_info'] = $meta;
|
|
|
+ }
|
|
|
+ $redis = getTaskRedis();
|
|
|
+ $redis->set("img_to_img_task:" . $data['task_id'], json_encode($payload, JSON_UNESCAPED_UNICODE), ['EX' => 300]);
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ // Redis 不可用时图片已生成,不影响 return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ try {
|
|
|
+ $res = $aiGateway->buildRequestData(
|
|
|
+ $statusVal,
|
|
|
+ $model,
|
|
|
+ $promptContent,
|
|
|
+ $size,
|
|
|
+ (string)$productBase64,
|
|
|
+ $productMime,
|
|
|
+ (string)$templateBase64,
|
|
|
+ $templateMime
|
|
|
+ );
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ return $fail($e->getMessage());
|
|
|
+ }
|
|
|
|
|
|
$generatedBase64 = $aiGateway->extractImageBase64FromResponse($res);
|
|
|
if (!$generatedBase64) {
|
|
|
@@ -400,9 +454,43 @@ class ImageToImageJob{
|
|
|
return $fail('图片Base64解码失败');
|
|
|
}
|
|
|
|
|
|
- // 4) 按业务分支落盘入库
|
|
|
- if ($statusType === 'ProductImageGeneration') {
|
|
|
- // 4.1 产品图创作:保存产品图/模板图/生成图 -> 插入 product_image_generate
|
|
|
+ //存入数据库操作(按 status_type 分支)
|
|
|
+ if ($statusType === 'texttoimage') {
|
|
|
+ $fileName = 'text2img-' . date('YmdHis') . '-' . uniqid() . '.png';
|
|
|
+ $fullPath = $texttoimageSaveDir . $fileName;
|
|
|
+ if (!file_put_contents($fullPath, $generatedImageData)) {
|
|
|
+ return $fail('图片保存失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ $dbImgPath = 'uploads/texttoimage/' . $texttoimageDateSegment . '/' . $fileName;
|
|
|
+ if (!Common::uploadLocalFileToOss((string)$fullPath, (string)$dbImgPath)) {
|
|
|
+ echo 'OSS 上传生成图失败: ' . $dbImgPath . "\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ $sizeForDb = 0;
|
|
|
+ if ($width !== '' && is_numeric($width)) {
|
|
|
+ $sizeForDb = (float)$width;
|
|
|
+ } elseif ($size !== '' && is_numeric($size)) {
|
|
|
+ $sizeForDb = (float)$size;
|
|
|
+ }
|
|
|
+
|
|
|
+ Db::name('ai_text_image')->insert([
|
|
|
+ 'old_img' => $sourceProductPath,
|
|
|
+ 'ref_img' => $sourceTemplatePath,
|
|
|
+ 'new_img' => $dbImgPath,
|
|
|
+ 'prompt' => $prompt,
|
|
|
+ 'size' => $sizeForDb,
|
|
|
+ 'sys_id' => $sysId,
|
|
|
+ 'sys_rq' => $sysrq,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $complete($dbImgPath);
|
|
|
+ return [
|
|
|
+ 'path' => $dbImgPath,
|
|
|
+ 'success_report' => $aiGateway->formatImg2ImgSuccessReport($dbImgPath),
|
|
|
+ ];
|
|
|
+
|
|
|
+ } elseif ($statusType === 'ProductImageGeneration') {
|
|
|
$rootPath = str_replace('\\', '/', ROOT_PATH);
|
|
|
$saveDir = rtrim($rootPath, '/') . '/public/uploads/Product/' . date('Y-m-d') . '/';
|
|
|
if (!is_dir($saveDir)) {
|
|
|
@@ -441,14 +529,46 @@ class ImageToImageJob{
|
|
|
'status_val' => $statusVal,
|
|
|
'size' => $size,
|
|
|
'sys_id' => $sysId,
|
|
|
- 'createTime' => $now,
|
|
|
+ 'createTime' => $sysrq,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $complete($generatedDbPath);
|
|
|
+ return [
|
|
|
+ 'path' => $generatedDbPath,
|
|
|
+ 'success_report' => $aiGateway->formatImg2ImgSuccessReport($generatedDbPath),
|
|
|
+ ];
|
|
|
+
|
|
|
+ } elseif ($statusType === 'ProductWhiteBackground') {
|
|
|
+ //处理图片抠图
|
|
|
+ $fileName = 'white-' . date('YmdHis') . '-' . uniqid() . '.png';
|
|
|
+ $fullPath = $whiteBgSaveDir . $fileName;
|
|
|
+ if (!file_put_contents($fullPath, $generatedImageData)) {
|
|
|
+ return $fail('白底图保存失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ $generatedDbPath = 'uploads/whitebackground/' . $whiteBgDateSegment . '/' . $fileName;
|
|
|
+ if (!Common::uploadLocalFileToOss((string)$fullPath, (string)$generatedDbPath)) {
|
|
|
+ echo 'OSS 上传白底图失败: ' . $generatedDbPath . "\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ Db::name('ai_white_background')->insert([
|
|
|
+ 'product_img' => $productDbPath,
|
|
|
+ 'generated_image' => $generatedDbPath,
|
|
|
+ 'prompt' => $prompt,
|
|
|
+ 'model' => $model,
|
|
|
+ 'size' => $size,
|
|
|
+ 'status_val' => $statusVal,
|
|
|
+ 'sys_id' => $sysId,
|
|
|
+ 'createTime' => $sysrq,
|
|
|
]);
|
|
|
|
|
|
$complete($generatedDbPath);
|
|
|
- return $generatedDbPath;
|
|
|
- } else if ($statusType === 'ProductTemplateReplace') {
|
|
|
+ return [
|
|
|
+ 'path' => $generatedDbPath,
|
|
|
+ 'success_report' => $aiGateway->formatImg2ImgSuccessReport($generatedDbPath),
|
|
|
+ ];
|
|
|
|
|
|
- // 4.2 产品替换:生成图落盘到 merchant/newimg -> 回写 product + product_image
|
|
|
+ } elseif ($statusType === 'ProductTemplateReplace') {
|
|
|
$product = Db::name('product')->where('id', $data['id'])->find();
|
|
|
if (empty($product)) {
|
|
|
return $fail('产品不存在');
|
|
|
@@ -486,47 +606,11 @@ class ImageToImageJob{
|
|
|
]);
|
|
|
|
|
|
$complete($dbImgPath);
|
|
|
- return $dbImgPath;
|
|
|
- } elseif ($statusType === 'texttoimage') {
|
|
|
- $rootPath = str_replace('\\', '/', ROOT_PATH);
|
|
|
- $dateSegment = date('Y-m-d');
|
|
|
- $saveDir = rtrim($rootPath, '/') . '/public/uploads/texttoimage/' . $dateSegment . '/';
|
|
|
- if (!is_dir($saveDir)) {
|
|
|
- mkdir($saveDir, 0755, true);
|
|
|
- }
|
|
|
-
|
|
|
- $fileName = 'text2img-' . date('YmdHis') . '-' . uniqid() . '.png';
|
|
|
- $fullPath = $saveDir . $fileName;
|
|
|
-
|
|
|
- if (!file_put_contents($fullPath, $generatedImageData)) {
|
|
|
- return $fail('图片保存失败');
|
|
|
- }
|
|
|
-
|
|
|
- $dbImgPath = 'uploads/texttoimage/' . $dateSegment . '/' . $fileName;
|
|
|
- if (!Common::uploadLocalFileToOss((string)$fullPath, (string)$dbImgPath)) {
|
|
|
- echo 'OSS 上传生成图失败: ' . $dbImgPath . "\n";
|
|
|
- }
|
|
|
-
|
|
|
- // size 字段为 double:优先存 width,其次 size 数值
|
|
|
- $sizeForDb = 0;
|
|
|
- if ($width !== '' && is_numeric($width)) {
|
|
|
- $sizeForDb = (float)$width;
|
|
|
- } elseif ($size !== '' && is_numeric($size)) {
|
|
|
- $sizeForDb = (float)$size;
|
|
|
- }
|
|
|
-
|
|
|
- Db::name('ai_text_image')->insert([
|
|
|
- 'old_img' => $sourceProductPath,
|
|
|
- 'ref_img' => $sourceTemplatePath,
|
|
|
- 'new_img' => $dbImgPath,
|
|
|
- 'prompt' => $prompt,
|
|
|
- 'size' => $sizeForDb,
|
|
|
- 'sys_id' => $sysId,
|
|
|
- 'sys_rq' => $now,
|
|
|
- ]);
|
|
|
+ return [
|
|
|
+ 'path' => $dbImgPath,
|
|
|
+ 'success_report' => $aiGateway->formatImg2ImgSuccessReport($dbImgPath),
|
|
|
+ ];
|
|
|
|
|
|
- $complete($dbImgPath);
|
|
|
- return $dbImgPath;
|
|
|
} else {
|
|
|
return $fail('当前页面未进行配置,请联系管理员开通权限');
|
|
|
}
|
|
|
@@ -534,61 +618,61 @@ class ImageToImageJob{
|
|
|
|
|
|
public function ImageToImage($fileName, $outputDirRaw, $new_image_url, $width, $height)
|
|
|
{
|
|
|
- $rootPath = str_replace('\\', '/', ROOT_PATH);
|
|
|
- $outputDir = rtrim($rootPath . 'public/' . ltrim($outputDirRaw, '/'), '/') . '/';
|
|
|
- $dateDir = date('Y-m-d') . '/';
|
|
|
- $fullBaseDir = $outputDir . $dateDir;
|
|
|
-
|
|
|
- // 创建主目录和 imgtoimg 子目录
|
|
|
- if (!is_dir($fullBaseDir)) {
|
|
|
- mkdir($fullBaseDir, 0755, true);
|
|
|
- }
|
|
|
-
|
|
|
- $imgtoimgDir = $fullBaseDir . '1024x1303/';
|
|
|
- if (!is_dir($imgtoimgDir)) {
|
|
|
- mkdir($imgtoimgDir, 0755, true);
|
|
|
- }
|
|
|
-
|
|
|
- // 查询数据库原图记录
|
|
|
- $record = Db::name('text_to_image')
|
|
|
- ->where('old_image_url', 'like', "%{$fileName}")
|
|
|
- ->order('id desc')
|
|
|
- ->find();
|
|
|
-
|
|
|
- if (!$record) {
|
|
|
- return json(['code' => 1, 'msg' => '没有找到匹配的图像记录']);
|
|
|
- }
|
|
|
-
|
|
|
- // 调用 AI 图生图 API
|
|
|
- $ai = new AIGatewayService();
|
|
|
- $res = $ai->txt2imgWithControlNet('', $new_image_url);
|
|
|
- if (!isset($res['code']) || $res['code'] !== 0) {
|
|
|
- return json(['code' => 1, 'msg' => $res['msg'] ?? '图像生成失败']);
|
|
|
- }
|
|
|
-
|
|
|
- // 生成保存文件路径
|
|
|
- $originalBaseName = pathinfo($new_image_url, PATHINFO_FILENAME);
|
|
|
- $finalFileName = $originalBaseName . '.png';
|
|
|
- $savePath = $imgtoimgDir . $finalFileName;
|
|
|
-
|
|
|
- // 写入图像文件
|
|
|
- if (!file_put_contents($savePath, base64_decode($res['data']['base64']))) {
|
|
|
- return json(['code' => 1, 'msg' => '图像保存失败,请检查目录权限']);
|
|
|
- }
|
|
|
- // 图生图结果同步 OSS(失败不阻断)
|
|
|
- $relativeImgPath = rtrim($outputDirRaw, '/') . '/' . $dateDir . '1024x1303/' . $finalFileName;
|
|
|
- Common::uploadLocalFileToOss((string)$savePath, (string)$relativeImgPath);
|
|
|
- // 构造相对路径用于数据库
|
|
|
-
|
|
|
- // 更新数据库记录
|
|
|
- Db::name('text_to_image')->where('id', $record['id'])->update([
|
|
|
- 'imgtoimg_url' => $relativeImgPath,
|
|
|
- 'status_name' => '图生图',
|
|
|
- 'error_msg' => '',
|
|
|
- 'update_time' => date('Y-m-d H:i:s')
|
|
|
- ]);
|
|
|
-
|
|
|
- // 返回成功响应
|
|
|
- return "成功";
|
|
|
+ // $rootPath = str_replace('\\', '/', ROOT_PATH);
|
|
|
+ // $outputDir = rtrim($rootPath . 'public/' . ltrim($outputDirRaw, '/'), '/') . '/';
|
|
|
+ // $dateDir = date('Y-m-d') . '/';
|
|
|
+ // $fullBaseDir = $outputDir . $dateDir;
|
|
|
+
|
|
|
+ // // 创建主目录和 imgtoimg 子目录
|
|
|
+ // if (!is_dir($fullBaseDir)) {
|
|
|
+ // mkdir($fullBaseDir, 0755, true);
|
|
|
+ // }
|
|
|
+
|
|
|
+ // $imgtoimgDir = $fullBaseDir . '1024x1303/';
|
|
|
+ // if (!is_dir($imgtoimgDir)) {
|
|
|
+ // mkdir($imgtoimgDir, 0755, true);
|
|
|
+ // }
|
|
|
+
|
|
|
+ // // 查询数据库原图记录
|
|
|
+ // $record = Db::name('text_to_image')
|
|
|
+ // ->where('old_image_url', 'like', "%{$fileName}")
|
|
|
+ // ->order('id desc')
|
|
|
+ // ->find();
|
|
|
+
|
|
|
+ // if (!$record) {
|
|
|
+ // return json(['code' => 1, 'msg' => '没有找到匹配的图像记录']);
|
|
|
+ // }
|
|
|
+
|
|
|
+ // // 调用 AI 图生图 API
|
|
|
+ // $ai = new AIGatewayService();
|
|
|
+ // $res = $ai->txt2imgWithControlNet('', $new_image_url);
|
|
|
+ // if (!isset($res['code']) || $res['code'] !== 0) {
|
|
|
+ // return json(['code' => 1, 'msg' => $res['msg'] ?? '图像生成失败']);
|
|
|
+ // }
|
|
|
+
|
|
|
+ // // 生成保存文件路径
|
|
|
+ // $originalBaseName = pathinfo($new_image_url, PATHINFO_FILENAME);
|
|
|
+ // $finalFileName = $originalBaseName . '.png';
|
|
|
+ // $savePath = $imgtoimgDir . $finalFileName;
|
|
|
+
|
|
|
+ // // 写入图像文件
|
|
|
+ // if (!file_put_contents($savePath, base64_decode($res['data']['base64']))) {
|
|
|
+ // return json(['code' => 1, 'msg' => '图像保存失败,请检查目录权限']);
|
|
|
+ // }
|
|
|
+ // // 图生图结果同步 OSS(失败不阻断)
|
|
|
+ // $relativeImgPath = rtrim($outputDirRaw, '/') . '/' . $dateDir . '1024x1303/' . $finalFileName;
|
|
|
+ // Common::uploadLocalFileToOss((string)$savePath, (string)$relativeImgPath);
|
|
|
+ // // 构造相对路径用于数据库
|
|
|
+
|
|
|
+ // // 更新数据库记录
|
|
|
+ // Db::name('text_to_image')->where('id', $record['id'])->update([
|
|
|
+ // 'imgtoimg_url' => $relativeImgPath,
|
|
|
+ // 'status_name' => '图生图',
|
|
|
+ // 'error_msg' => '',
|
|
|
+ // 'update_time' => date('Y-m-d H:i:s')
|
|
|
+ // ]);
|
|
|
+
|
|
|
+ // // 返回成功响应
|
|
|
+ // return "成功";
|
|
|
}
|
|
|
}
|