bool, 'message'=>string, 'data'=>string] */ public function handleTextToText($status_val, string $prompt, string $model): array { $ai = new AIGatewayService(); $gptRes = $ai->buildRequestData($status_val, $model, $prompt); $gptText = ''; if (isset($gptRes['candidates'][0]['content']['parts'][0]['text'])) { $gptText = $gptRes['candidates'][0]['content']['parts'][0]['text']; } elseif (isset($gptRes['choices'][0]['message']['content'])) { $gptText = trim($gptRes['choices'][0]['message']['content']); } if (isset($gptRes['error']) || isset($gptRes['code']) && $gptRes['code'] !== 0) { return [ 'success' => false, 'message' => $gptRes['msg'] ?? $gptRes['error']['message'] ?? '生成失败', 'data' => '' ]; } return [ 'success' => true, 'message' => '生成成功', 'data' => $gptText ]; } /** * 文生图:创建任务并推送到队列 * @param array $params 含 id、model 等 * @return array ['success'=>bool, 'message'=>string, 'task_id'=>string] */ public function handleTextToImg(array $params): array { return $this->submitTaskToQueue( $params, self::KEY_TEXT_TO_IMAGE, '正在生成图片中,请稍等.....' ); } /** * 图生图:创建任务并推送到队列(产品图+模板图) * @param array $params 含 id、product_img、template_img、prompt、model 等 * @return array ['success'=>bool, 'message'=>string, 'task_id'=>string] */ public function handleImgToImg(array $params): array { return $this->submitTaskToQueue( $params, self::KEY_IMG_TO_IMG, '正在生成图片中,请稍等.....' ); } /** * 通用:创建任务 ID、写入 Redis、推送到队列 */ private function submitTaskToQueue(array $params, string $redisKeyPrefix, string $message): array { $taskId = ($params['id'] ?? '0') . '-' . date('YmdHis') . '-' . mt_rand(1000, 9999); $params['task_id'] = $taskId; $redis = getTaskRedis(); $redis->set($redisKeyPrefix . $taskId, json_encode([ 'status' => 'pending', 'created_at' => date('Y-m-d H:i:s') ]), ['EX' => self::TASK_TTL]); Queue::push('app\job\ImageArrJob', $params, self::QUEUE_ARRIMAGE); return [ 'success' => true, 'message' => $message, 'task_id' => $taskId ]; } /** * 批量图像任务:支持链式任务和单类型任务 * @param array $params 含 batch、num、type、old_image_file 等 * @return bool */ public function handleImage(array $params): bool { if (!isset($params['batch']) || !is_array($params['batch'])) { return false; } $arr = $this->buildBatchItems($params); $insertData = $this->buildQueueLogData($params, count($arr)); if (empty($params['type'])) { $this->dispatchFullChainTask($arr, $insertData); } else { $result = $this->dispatchSingleTypeTask($arr, $params, $insertData); if (!$result) { return false; } } return true; } /** 构建批量任务项 */ private function buildBatchItems(array $params): array { $arr = []; foreach ($params['batch'] as $v) { $baseItem = [ 'sourceDir' => $this->sourceDir($v, 1), 'outputDir' => $this->sourceDir($v, 2), 'file_name' => $this->sourceDir($v, 3), 'type' => $params['type'] ?? '', 'selectedOption' => $params['selectedOption'] ?? '', 'txttotxt_selectedOption' => $params['txttotxt_selectedOption'] ?? '', 'imgtotxt_selectedOption' => $params['imgtotxt_selectedOption'] ?? '', 'prompt' => '', 'width' => $params['width'] ?? 0, 'height' => $params['height'] ?? 0, 'executeKeywords' => $params['executeKeywords'] ?? '', 'sys_id' => $params['sys_id'] ?? '' ]; $num = (int)($params['num'] ?? 1); $arr = array_merge($arr, array_fill(0, max(1, $num), $baseItem)); } return $arr; } /** 构建队列日志插入数据 */ private function buildQueueLogData(array $params, int $imageCount): array { return [ 'create_time' => date('Y-m-d H:i:s'), 'old_image_file' => $params['old_image_file'] ?? '', 'status' => '等待中', 'image_count' => $imageCount, 'params' => json_encode($params, JSON_UNESCAPED_UNICODE) ]; } /** 派发一键链式任务:图生文→文生文→文生图→图生图→高清放大 */ private function dispatchFullChainTask(array $arr, array $insertData): void { $params = json_decode($insertData['params'], true) ?: []; $insertData['model'] = 'gpt-4-vision-preview,gpt-4,' . ($params['selectedOption'] ?? ''); $insertData['model_name'] = '文生图'; $taskId = Db::name('queue_logs')->insertGetId($insertData); $arr = array_map(function ($item) use ($taskId) { $item['type'] = '图生文'; $item['chain_next'] = ['文生文', '文生图', '图生图', '高清放大']; $item['task_id'] = $taskId; return $item; }, $arr); Queue::push('app\job\ImageArrJob', ['task_id' => $taskId, 'data' => $arr], self::QUEUE_ARRIMAGE); } /** 派发单类型任务 */ private function dispatchSingleTypeTask(array $arr, array $params, array $insertData): bool { $typeConfig = $this->getTypeConfig($params['type'], $params); if (!$typeConfig) { return false; } $insertData['model'] = $typeConfig['model']; $insertData['model_name'] = $typeConfig['model_name']; $taskId = Db::name('queue_logs')->insertGetId($insertData); $arr = array_map(function ($item) use ($params, $taskId) { $item['type'] = $params['type']; $item['task_id'] = $taskId; return $item; }, $arr); Queue::push('app\job\ImageArrJob', ['task_id' => $taskId, 'data' => $arr], self::QUEUE_ARRIMAGE); return true; } /** 任务类型对应的 model 配置 */ private function getTypeConfig(string $type, array $params = []): ?array { $configs = [ '图生文' => ['model' => 'gpt-4-vision-preview', 'model_name' => '图生文'], '文生文' => ['model_key' => 'txttotxt_selectedOption', 'model_name' => '文生文'], '文生图' => ['model_key' => 'selectedOption', 'model_name' => '文生图'], '图生图' => ['model' => 'realisticVisionV51_v51VAE-inpainting.safetensors [f0d4872d24]', 'model_name' => '图生图'], '高清放大' => ['model' => '高清放大', 'model_name' => '高清放大'] ]; $cfg = $configs[$type] ?? null; if (!$cfg) { return null; } if (isset($cfg['model_key'])) { $cfg['model'] = $params[$cfg['model_key']] ?? ''; unset($cfg['model_key']); } return $cfg; } /** * 解析图像路径 * @param string $filePath 如 uploads/operate/ai/Preview/20240610/xxx.png * @param int $type 1=源目录 2=输出目录 3=文件名 * @return string|null */ public function sourceDir(string $filePath, int $type): ?string { $pathParts = explode('/', $filePath); $filename = array_pop($pathParts); $baseParts = $pathParts; $date = ''; foreach ($pathParts as $index => $part) { if (preg_match('/^\d{8}$/', $part)) { $date = $part; unset($baseParts[$index]); break; } } $basePath = implode('/', $baseParts); switch ($type) { case 1: return $basePath; case 2: return '/' . str_replace('/Preview/', '/dall-e/', $basePath) . $date; case 3: return $filename; default: return null; } } }