$data['log_id']]); HdLog::destroy(['id' => $data['log_id']]); } else { HdImage::whereIn('log_id', $data['log_id'])->select()->delete(); HdLog::whereIn('id', $data['log_id'])->select()->delete(); } return true; } catch (\Exception $exception) { self::setError($exception->getMessage()); return false; } } /** * @desc 保存日志 * @param $type * @param $request_id * @param $params * @param $task_id * @param $sub_task_id * @return true * @date 2024/7/6 15:57 * @author dagouzi */ public static function saveLog($type, $request_id, $params, $task_id, $sub_task_id) { $data = [ 'user_id' => self::$uid, 'request_id' => $request_id, 'type' => $type, 'params' => json_encode($params, JSON_UNESCAPED_UNICODE), 'task_id' => $task_id, 'sub_task_ids' => json_encode($sub_task_id, JSON_UNESCAPED_UNICODE) ]; $log = HdLog::create($data); foreach ($sub_task_id as $id) { $imageData = [ 'log_id' => $log->id, 'image' => '', 'sub_task_id' => $id, 'task_status' => 0, 'task_completion' => 0, ]; HdImage::create($imageData); } return true; } /** * @desc 获取模板 * @param $params * @return true * @date 2024/7/5 10:10 * @throws \GuzzleHttp\Exception\GuzzleException * @author dagouzi */ public static function templates($params) { $page_index = $params['page'] ?? 1; $page_size = max($params['page_size'] ?? 20, 100); $response = self::requestUrl([ 'page_index' => $page_index, 'page_size' => $page_size, ], self::HD_TEMPLATE_LISTS, self::$uid); self::$returnData = ['result' => $response]; return true; } /** * @desc 提交商品图图生图任务 * @param $params * @return bool * @date 2024/7/5 14:48 * @throws \Exception * @author dagouzi */ public static function segmentImage($params) { $request = []; if (!empty($params['prompt'])) { // 默认位置 $defaultLocation = [ 'horizontal_direction' => 'AlignLeft', 'horizontal_proportion' => 0.5, 'vertical_direction' => 'AlignTop', 'vertical_proportion' => 0.5 ]; // 提示词 $request['prompt'] = $params['prompt']; $request['location_desc'] = !empty($params['location_desc']) ? $params['location_desc'] : $defaultLocation; } else { $request['template_category'] = $params['template_category']; $request['template_name'] = $params['template_name']; $request['custom_template'] = $params['custom_template'] ?? true; } foreach ($params as $key => $value) { if (in_array($key, ['negative_prompt', 'resolution', 'img_count', 'size', 'style'])) { if (!empty($value)) { $request[$key] = $value; } } if ($key == 'resolution' && !empty($value)) { foreach ($value as &$valueResolution) { $valueResolution = intval($valueResolution); } $request['resolution'] = $value; } if ($key == 'ref_image' && !empty($value)) { foreach ($value as &$valueImg) { $valueImg = self::imageToStream($valueImg); } $request['ref_image'] = $value; } } $request['image'] = self::imageToStream($params['image']); $response = self::requestUrl($request, self::HD_SHOP_IMG2IMG, self::$uid); if (!$response) { throw new \Exception('提交商品图生图任务发生错误'); } self::saveLog(1, $response['request_id'], $params, $response['task_id'], $response['sub_task_ids']); self::$returnData = ['result' => $response]; return true; } /** * @desc 提交ai试衣生图任务 * @param $params * @return bool * @date 2024/7/5 14:48 * @throws \Exception * @author dagouzi */ public static function vton($params) { $request = []; // 组装数据 foreach ($params['persons'] as $item) { $request['persons'][] = self::imageToStream($item); } if (!empty($params['lower_clothes'])) { $request['lower_clothes'] = self::imageToStream($params['lower_clothes']); } if (!empty($params['upper_clothes'])) { $request['upper_clothes'] = self::imageToStream($params['upper_clothes']); } $request['img_count'] = $params['img_count'] ?? 1; $response = self::requestUrl($request, self::HD_AI_TRY, self::$uid); if (!$response) { throw new \Exception('提交ai试衣生图任务错误'); } self::saveLog(2, $response['request_id'], $params, $response['task_id'], $response['sub_task_ids']); self::$returnData = ['result' => $response]; return true; } /** * @desc 添加模特案例 * @param $params * @return bool * @date 2024/12/25 15:50 * @author dagouzi */ public static function addModelCase($params) { $data = [ 'user_id' => self::$uid, 'case_type' => 4, 'params' => json_encode([]), 'result_image' => FileService::setFileUrl($params['result_image']), ]; HdImageCases::create($data); return true; } /** * @desc 定时任务查询结果 * @return true|void * @date 2024/7/5 17:57 * @author dagouzi */ public static function cron() { try{ $now = time(); // 延迟5秒执行 $task = HdLog::where(['task_status' => 0])->where('create_time', '<', $now - 30)->findOrEmpty(); if ($task->isEmpty()) { return true; } if (empty($task->task_id)) { self::taskStatus($task, 3); return true; } self::handleResult($task->task_id, $task->type); return true; } catch (\Exception $e) { return true; } } /** * @desc 提交文生图任务 * @return bool * @date 2024/7/20 10:50 * @throws \GuzzleHttp\Exception\GuzzleException * @author dagouzi */ public static function txt2img($params) { $request = []; // 组装数据 foreach ($params as $key => $value) { if (in_array($key, ['negative_prompt', 'aspect_ratio', 'img_count'])) { if (!empty($value)) { if ($key == 'img_count') { $request[$key] = (int)$value; } else { $request[$key] = $value; } } } } $request['prompt'] = $params['prompt']; $response = self::requestUrl($request, self::HD_TXT2IMG, self::$uid); if (!$response) { throw new \Exception('提交文生图任务错误'); } $sub_task_ids = $response['sub_task_ids'] ?? ''; self::saveLog(3, $response['request_id'], $params, $response['task_id'], $sub_task_ids); self::$returnData = ['result' => $response]; return true; } /** * @desc 提交图生图任务 * @return bool * @date 2024/7/20 10:50 * @throws \GuzzleHttp\Exception\GuzzleException * @author dagouzi */ public static function img2img($params) { $request = []; // 组装数据 foreach ($params as $key => $value) { if (in_array($key, ['prompt', 'negative_prompt', 'aspect_ratio', 'img_count'])) { if (!empty($value)) { if ($key == 'img_count') { $request[$key] = (int)$value; } else { $request[$key] = $value; } } } } if(empty($params['prompt'])){ throw new \Exception('请输入图片描述'); } foreach ($params['image'] as $item) { $request['image'][] = self::imageToStream($item); } $response = self::requestUrl($request, self::HD_IMG2IMG, self::$uid); if (!$response) { throw new \Exception('提交图生图任务错误'); } self::saveLog(4, $response['request_id'], $params, $response['task_id'], $response['sub_task_ids']); self::$returnData = ['result' => $response]; return true; } /** * @desc 获取任务状态 * @param $request * @return true * @date 2024/7/18 17:15 * @throws \GuzzleHttp\Exception\GuzzleException * @author dagouzi */ public static function getTaskStatus($request) { $task_id = $request['task_id']; $type = $request['type']; $result = self::handleResult($task_id, $type); self::$returnData = ['result' => $result]; return true; } /** * @desc 处理查询结果 * @param $task_id * @param $type * @return array|mixed|true|null * @date 2024/7/18 18:30 * @throws \GuzzleHttp\Exception\GuzzleException * @author dagouzi */ private static function handleResult($task_id, $type) { $result = []; $task = HdLog::where(['task_id' => $task_id])->findOrEmpty(); $typeName = ''; //转换 switch ($type) { case 1: $scene = self::HD_SHOP_IMG2IMG_STATUS; $typeName = '商品图'; break; case 2: $scene = self::HD_AI_TRY_STATUS; $typeName = 'ai试衣'; break; case 3: $scene = self::HD_TXT2IMG_STATUS; $typeName = '文生图'; break; case 4: $scene = self::HD_IMG2IMG_STATUS; $typeName = '图生图'; break; default: throw new \Exception('参数错误'); } try { $result = self::requestUrl(['task_id' => $task_id], $scene, self::$uid); if (!$result) { //记录 失败进行恢复 throw new \Exception('获取[' . $typeName . ']结果错误'); } if (empty($result['sub_task_results'])) { self::taskStatus($task, 4, json_encode($result)); return true; } $sub_task_results = $result['sub_task_results']; $scene = match ((int)$type) { 1 => ['scene' => 'goods_image', "type" => AccountLogEnum::TOKENS_DEC_GOODS_IMAGE], 2 => ['scene' => 'model_image', "type" => AccountLogEnum::TOKENS_DEC_MODEL_IMAGE], 3 => ['scene' => 'text_to_image', "type" => AccountLogEnum::TOKENS_DEC_TEXT_TO_IMAGE], 4 => ['scene' => 'image_to_image', "type" => AccountLogEnum::TOKENS_DEC_IMAGE_TO_IMAGE], }; $unit = TokenLogService::getTypeScore($scene['scene']); foreach ($sub_task_results as $item) { // 循环每个图的生成进度 $sub_task = HdImage::where(['sub_task_id' => $item['sub_task_id']])->findOrEmpty(); $image = $item['image'] ?? ''; if($image){ $image = FileService::downloadFileBySource($image, 'image'); } if ($sub_task->isEmpty()) { $sub_task_data = [ 'log_id' => $task->id, 'image' => $image, 'sub_task_id' => $item['sub_task_id'], 'task_status' => $item['task_status'], 'task_completion' => $item['task_completion'] ]; HdImage::create($sub_task_data); } else { if (!in_array($item['task_status'], [0, 2])) { $sub_task->task_status = $item['task_status']; $sub_task->task_completion = $item['task_completion']; $sub_task->image = $image; $sub_task->save(); } } $task_status = $item['task_status']; //假设某张失败 则恢复对应的算力 if (in_array($task_status, [3, 4])) { $extra = ['图片生成失败' => 1, '算力单价' => $unit,'恢复算力'=> $unit]; if (isset($item['task_id'])){ AccountLogLogic::recordUserTokensLog(false, $task->user_id, $scene['type'], $unit, $item['task_id'],$extra); } if (isset($item['sub_task_id'])){ AccountLogLogic::recordUserTokensLog(false, $task->user_id, $scene['type'], $unit, $item['sub_task_id'],$extra); } } } $status_array = array_column($sub_task_results, 'task_status'); // 子任务的状态,0: 等待中,1: 完成,2: 处理中,3: 失败,4: 未通过审核 if (in_array(0, $status_array) || in_array(2, $status_array)) { self::taskStatus($task, 0); } else { self::taskStatus($task, 1); } } catch (\Exception $e) { self::taskStatus($task, 5, $e->getMessage()); } return ['result' => $result]; } /** * @desc 修改任务状态 * @param $task * @param $status * @return void * @date 2024/7/5 18:12 * @author dagouzi */ public static function taskStatus($task, $status = 2, $remark = '') { $task->task_status = $status; if (!empty($remark)) { $task->remark = $remark; } $task->save(); } /** * 请求上游接口与计费 * @param array $request * @param string $scene * @param int $userId * @param string $taskId * @return array * @throws \Exception */ private static function requestUrl(array $request, string $scene, int $userId): array { $requestService = \app\common\service\ToolsService::HiDream(); [$tokenScene, $tokenCode] = match ($scene) { self::HD_TXT2IMG => ['text_to_image', AccountLogEnum::TOKENS_DEC_TEXT_TO_IMAGE], self::HD_IMG2IMG => ['image_to_image', AccountLogEnum::TOKENS_DEC_IMAGE_TO_IMAGE], self::HD_SHOP_IMG2IMG => ['goods_image', AccountLogEnum::TOKENS_DEC_GOODS_IMAGE], self::HD_AI_TRY => ['model_image', AccountLogEnum::TOKENS_DEC_MODEL_IMAGE], default => ['', ''] }; if ($tokenScene) { //补充 $request['img_count'] = $request['img_count'] ?? 1; } switch ($scene) { case self::HD_TXT2IMG: $response = $requestService->txt2Img($request); break; case self::HD_IMG2IMG: $response = $requestService->img2Img($request); break; case self::HD_SHOP_IMG2IMG: $response = $requestService->shopImg2Img($request); break; case self::HD_TEMPLATE_LISTS: $response = $requestService->templateList($request); break; case self::HD_AI_TRY: $response = $requestService->vtonCreate($request); break; case self::HD_TXT2IMG_STATUS: $response = $requestService->txt2ImgStatus($request); break; case self::HD_IMG2IMG_STATUS: $response = $requestService->img2ImgStatus($request); break; case self::HD_SHOP_IMG2IMG_STATUS: $response = $requestService->shopImg2ImgStatus($request); break; case self::HD_AI_TRY_STATUS: $response = $requestService->vtonStatus($request); break; default: } if ($tokenScene && isset($response['code']) && $response['code'] == 10000) { $taskId = $response['data']['task_id']; if(!isset($response['data']['sub_task_ids'])){ throw new \Exception('目前生图正在维护中,请稍后再试'); }; //计费 $unit = TokenLogService::checkToken($userId, $tokenScene); $points = ceil($request['img_count'] * $unit); //token扣除 User::userTokensChange(self::$uid, $points); $extra = ['生成图片数' => $request['img_count'], '算力单价' => $unit, '实际消耗算力' => $points]; //扣费记录 AccountLogLogic::recordUserTokensLog(true, $userId, $tokenCode, $points, $taskId, $extra); } return $response['data'] ?? []; } /** * @desc 图片转base64 * @param $url * @return string * @date 2024/7/5 10:55 * @author dagouzi */ private static function imageToStream($url) { $img = file_get_contents($url); return base64_encode($img); } }