AiController.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. <?php
  2. namespace App\Http\Controllers\V1\Ai;
  3. use App\Auth;
  4. use App\Http\Controllers\V1\Controller;
  5. use App\libs\helpers\Helper;
  6. use App\libs\helpers\LogHelper;
  7. use App\libs\helpers\Response;
  8. use App\Models\Config;
  9. use App\Models\Keyword;
  10. use App\Models\TaskList;
  11. use App\Models\User;
  12. use App\Models\UserRole;
  13. use Illuminate\Http\Request;
  14. use Illuminate\Support\Facades\Log;
  15. use App\Exceptions\ApiException;
  16. use Overtrue\Pinyin\Pinyin;
  17. class AiController extends Controller
  18. {
  19. public $code = '_Ai';
  20. /**
  21. * 用户提交任务
  22. */
  23. public function submitTask(Request $request): \Illuminate\Http\JsonResponse
  24. {
  25. try {
  26. (int) $plot = Config::query()->where('key', 'plot')->value('value');
  27. if (!$plot) {
  28. return Response::fail('系统错误,请联系管理员#');
  29. }
  30. if (Auth::$user->diamond < $plot) {
  31. return Response::fail('钻石不够了,请先充值');
  32. }
  33. $roleId = $request->post('roleId', 0);
  34. if ($roleId) {
  35. $role = UserRole::query()->find($roleId);
  36. if (!$role) {
  37. return Response::fail('没有该角色');
  38. }
  39. } else {
  40. $role = UserRole::query()->create([
  41. 'user_id' => Auth::$userId,
  42. 'name' => $request->post('name', ''),
  43. 'sex' => $request->post('sex', ''),
  44. 'age' => $request->post('age', ''),
  45. 'star' => $request->post('star', ''),
  46. 'level' => $request->post('level', ''),
  47. 'is_piny' => $request->post('is_piny', 1),
  48. ]);
  49. if (!$role) {
  50. return Response::fail('提交失败,请稍后再试!');
  51. }
  52. }
  53. User::query()->where('id', Auth::$userId)->decrement('diamond', $plot);
  54. $task = TaskList::query()->create([
  55. 'user_id' => Auth::$userId,
  56. 'role_id' => $role->id,
  57. 'image' => $request->post('image', ''),
  58. 'surplus_diamond' => Auth::$user->diamond - $plot,
  59. 'nickname' => 'AI绘本生成',
  60. 'plot' => $plot,
  61. ]);
  62. if (!$task) {
  63. return Response::fail('任务提交失败');
  64. }
  65. return Response::success(['id' => $task->id], '提交成功,请稍等');
  66. } catch (\Exception $exception) {
  67. LogHelper::exceptionLog($exception, $this->code);
  68. return Response::fail($exception->getMessage());
  69. }
  70. }
  71. /**
  72. * 获取任务详情.
  73. *
  74. * @return \Illuminate\Http\JsonResponse
  75. */
  76. public function getTaskDetail(Request $request)
  77. {
  78. try {
  79. $id = $request->get('id', 0);
  80. return Response::success(TaskList::query()->where('user_id', Auth::$userId)->find($id));
  81. } catch (\Exception $exception) {
  82. LogHelper::exceptionLog($exception, $this->code);
  83. return Response::fail($exception->getMessage());
  84. }
  85. }
  86. /**
  87. * 获取字数.
  88. *
  89. * @return int|mixed
  90. */
  91. private function getBuildCountString($level = 1)
  92. {
  93. $string = Config::query()->where('key', 'class_count')->first()->toArray();
  94. foreach ($string['value'] ?? [] as $value) {
  95. if ($level == $value['lv']) {
  96. return $value['count'];
  97. }
  98. }
  99. return 200;
  100. }
  101. /**
  102. * 生成故事.
  103. */
  104. public function buildStory(Request $request): \Illuminate\Http\JsonResponse
  105. {
  106. try {
  107. $task = TaskList::query()->with('role')->where('state', 0)->inRandomOrder()->first();
  108. if (!$task) {
  109. return Response::fail('暂无任务');
  110. }
  111. $key = [];
  112. // $keyword = Keyword::query()->pluck('keyword');
  113. // foreach ($keyword as $value) {
  114. // $key[] = $value;
  115. // }
  116. // $keyword = implode(',', $key);
  117. $keyword = Config::query()->where('key', 'prompt_gushi')->value('value');
  118. $task->state = 1;
  119. $task->save();
  120. Log::warning('这个是当前的次数:' . $this->getBuildCountString($task->level));
  121. $prompt = "一个{$task->role->sex},叫{$task->role->name},{$task->role->age}岁。{$keyword},要求不低于{$this->getBuildCountString($task->role->level)}字。备注:不要加什么特殊符号只需要正常的逗号句号叹号这些。";
  122. $messages[] = ['role' => 'user', 'content' => $prompt];
  123. $data = [
  124. 'messages' => $messages,
  125. ];
  126. $postData = json_encode($data);
  127. $complete = $this->host($postData);
  128. $task->init_content = $complete['result'];
  129. $task->save();
  130. return Response::success($complete);
  131. } catch (\Exception $exception) {
  132. LogHelper::exceptionLog($exception, $this->code);
  133. return Response::fail($exception->getMessage());
  134. }
  135. }
  136. /**
  137. * 生成标题.
  138. *
  139. * @return \Illuminate\Http\JsonResponse
  140. */
  141. public function buildTitle(Request $request)
  142. {
  143. try {
  144. $task = TaskList::query()->with('role')->where('state', 1)->inRandomOrder()->first();
  145. if (!$task) {
  146. return Response::fail('暂无任务');
  147. }
  148. $task->state = 2;
  149. $task->save();
  150. $prompt = $task->init_content . '。将这段童话故事生成一个标题,标题不能有特殊符号,只需要纯文字标题即可,不需要其他任何文字,标题文字控制在10个字以内。不能超过10个字';
  151. $messages[] = ['role' => 'user', 'content' => $prompt];
  152. $data = [
  153. 'messages' => $messages,
  154. ];
  155. $postData = json_encode($data);
  156. $complete = $this->host($postData);
  157. // $task->state = 2;
  158. $task->title = $complete['result'];
  159. $task->save();
  160. return Response::success($complete);
  161. } catch (\Exception $exception) {
  162. LogHelper::exceptionLog($exception, $this->code);
  163. return Response::fail($exception->getMessage());
  164. }
  165. }
  166. /**
  167. * 生成关键词.
  168. *
  169. * @return \Illuminate\Http\JsonResponse
  170. */
  171. public function buildKeyword(Request $request)
  172. {
  173. try {
  174. $task = TaskList::query()->with('role')->where('state', 2)->inRandomOrder()->first();
  175. if (!$task) {
  176. return Response::fail('暂无任务');
  177. }
  178. $keyword = Config::query()->where('key', 'prompt_keyword')->value('value');
  179. $prompt = $task->init_content . $keyword;
  180. $messages[] = ['role' => 'user', 'content' => $prompt];
  181. $data = [
  182. 'messages' => $messages,
  183. ];
  184. $task->state = 3;
  185. $task->save();
  186. $postData = json_encode($data);
  187. $complete = $this->host($postData);
  188. // $task->state = 3;
  189. $task->keyword = $complete['result'];
  190. $task->save();
  191. return Response::success($complete);
  192. } catch (\Exception $exception) {
  193. LogHelper::exceptionLog($exception, $this->code);
  194. return Response::fail($exception->getMessage());
  195. }
  196. }
  197. /**
  198. * 翻译英文.
  199. */
  200. private function toEnglish($prompt)
  201. {
  202. $keyword = Config::query()->where('key', 'prompt_style')->value('value');
  203. $prompt = $prompt . $keyword;
  204. $promptTo = '我希望你能担任英语翻译、拼写校对和修辞改进的角色。我会将翻译的结果用于如stable diffusion、midjourney等绘画场景生成图片,语言要求尽量优美。我会用任何语言和你交流,你会识别语言,将其翻译为英语并仅回答翻译的最终结果,不要写解释。我的第一句话是:' . $prompt . '。请立刻翻译,不要回复其它内容。';
  205. $messages[] = ['role' => 'user', 'content' => $promptTo];
  206. $data = [
  207. 'messages' => $messages,
  208. ];
  209. $postData = json_encode($data);
  210. $complete = $this->host($postData);
  211. return $complete['result'];
  212. }
  213. /**
  214. * 提交sd生成插图.
  215. *
  216. * @return \Illuminate\Http\JsonResponse
  217. */
  218. public function buildSd(Request $request)
  219. {
  220. try {
  221. $task = TaskList::query()->with('role')->where('state', 3)->inRandomOrder()->first();
  222. if (!$task) {
  223. return Response::fail('暂无任务');
  224. }
  225. $task->state = 4;
  226. $task->save();
  227. $keyword = $this->toEnglish($task->keyword);
  228. $param = ['name' => '甜瓜(动漫风格)',
  229. 'init_image' => '',
  230. 'prompt' => $keyword,
  231. 'width' => '512',
  232. 'height' => '512',
  233. 'guidance_scale' => '7',
  234. 'samples' => '1',
  235. 'model_id' => '3',
  236. 'scheduler' => 'DDPMScheduler',
  237. 'type' => 'text2img',
  238. 'num_inference_steps' => '30',
  239. 'keywords' => $keyword,
  240. ];
  241. $result = (new Helper())->opensd($param);
  242. $result = json_decode($result, true);
  243. Log::warning('这个是SD回调信息:' . json_encode($result, 256));
  244. if (isset($result['data']['id'])) {
  245. $task->sd_id = $result['data']['id'];
  246. } else {
  247. $task->state = 3;
  248. }
  249. $task->save();
  250. return Response::success($result['data']);
  251. } catch (\Exception $exception) {
  252. LogHelper::exceptionLog($exception, $this->code);
  253. return Response::fail($exception->getMessage());
  254. }
  255. }
  256. /**
  257. * 查询生成进度.
  258. *
  259. * @return \Illuminate\Http\JsonResponse
  260. */
  261. public function getOpensdDetail(Request $request)
  262. {
  263. $task = TaskList::query()->where('state', 4)->inRandomOrder()->first();
  264. if (!$task) {
  265. return $this->error('暂无任务');
  266. }
  267. $res = (new Helper())->opensdDetail($task->sd_id);
  268. $res = @json_decode($res, true);
  269. if (200 != $res['code']) {
  270. return Response::fail($res['msg']);
  271. }
  272. if ('fail' == $res['data']['state']) {
  273. $task->state = 3;
  274. $task->desc = $res['data']['fail_reason'];
  275. }
  276. $task->save();
  277. if ('success' == $res['data']['state']) {
  278. $path = $this->saveImage($res['data']['gen_img']);
  279. $task->sd_image = $path;
  280. $task->state = 5;
  281. $task->save();
  282. }
  283. return $this->success('操作成功', $res['data']);
  284. }
  285. /**
  286. * 获取品牌排版.
  287. *
  288. * @return \Illuminate\Http\JsonResponse
  289. */
  290. public function buildPinyin(Request $request)
  291. {
  292. try {
  293. $task = TaskList::query()->where('state', 5)->inRandomOrder()->first();
  294. if (!$task) {
  295. return $this->error('暂无任务');
  296. }
  297. if (empty($file_name)) {
  298. $file_name = md5(time()) . '.png';
  299. }
  300. $path = public_path() . '/images/' . $file_name;
  301. $shell = 'wkhtmltoimage https://t18.9026.com/api/ai/Pinyin?id=' . $task->id . ' ' . $path;
  302. exec($shell, $result, $status);
  303. if ($status) {
  304. $this->exit_out('生成图片失败', ['生成图片失败' => [$shell, $result, $status]]);
  305. }
  306. $domain = request()->getScheme() . '://' . request()->getHost();
  307. $pdfPath = $domain . '/images/' . $file_name;
  308. $task->image_path = $pdfPath;
  309. $result = $this->extracted($task);
  310. $task->pinyin_content = $result;
  311. $task->state = 6;
  312. $task->save();
  313. return Response::success($result);
  314. } catch (\Exception $exception) {
  315. LogHelper::exceptionLog($exception, $this->code);
  316. return Response::fail($exception->getMessage());
  317. }
  318. }
  319. /**
  320. * 下载PDF.
  321. *
  322. * @return \Illuminate\Http\JsonResponse
  323. */
  324. public function downloadPdf(Request $request)
  325. {
  326. try {
  327. $id = $request->input('id', 0);
  328. $task = TaskList::query()->find($id);
  329. if (!$task) {
  330. return Response::fail('没有该任务');
  331. }
  332. if (!empty($task->pdf_path)) {
  333. return Response::success(['url' => $task->pdf_path]);
  334. }
  335. if (empty($file_name)) {
  336. $file_name = md5(time()) . '.pdf';
  337. }
  338. $path = public_path() . '/pdf/' . $file_name;
  339. $shell = 'wkhtmltopdf https://t18.9026.com/api/ai/Pinyin?id=' . $task->id . ' ' . $path;
  340. exec($shell, $result, $status);
  341. if ($status) {
  342. $this->exit_out('生成PDF失败', ['生成PDF失败' => [$shell, $result, $status]]);
  343. }
  344. $domain = request()->getScheme() . '://' . request()->getHost();
  345. $pdfPath = $domain . '/pdf/' . $file_name;
  346. $task->pdf_path = $pdfPath;
  347. $task->save();
  348. return Response::success(['url' => $pdfPath]);
  349. } catch (\Exception $exception) {
  350. LogHelper::exceptionLog($exception, $this->code);
  351. return Response::fail($exception->getMessage());
  352. }
  353. }
  354. private function splitChineseCharacters($inputString, $type = 0)
  355. {
  356. if (0 == $type) {
  357. $inputString = str_replace("\n", '', $inputString);
  358. }
  359. $inputString = str_replace('“', '', $inputString);
  360. $inputString = str_replace('”', '', $inputString);
  361. // $inputString = preg_replace('/[^\w\s-]+/u', '', $inputString);
  362. $length = mb_strlen($inputString, 'utf-8');
  363. $result = [];
  364. for ($i = 0; $i < $length; $i++) {
  365. $result[] = mb_substr($inputString, $i, 1, 'utf-8');
  366. }
  367. return $result;
  368. // return $inputString;
  369. }
  370. public function Pinyin(Request $request)
  371. {
  372. try {
  373. $id = $request->input('id');
  374. $task = TaskList::query()->find($id);
  375. $result = $this->extracted($task);
  376. return view('pdf', ['data' => $result, 'img' => $task->image, 'title' => $task->title]);
  377. } catch (\Exception $exception) {
  378. LogHelper::exceptionLog($exception, $this->code);
  379. return Response::fail($exception->getMessage());
  380. }
  381. }
  382. public function containsPunctuation($str)
  383. {
  384. // 定义要检测的符号
  385. $punctuation = '/[,。!、]/u'; // 中文逗号、中文句号、中文感叹号
  386. // 使用 preg_match 函数进行匹配
  387. return 1 === preg_match($punctuation, $str);
  388. }
  389. public function generate_pdf($html_path = '', $file_name = '')
  390. {
  391. if (empty($file_name)) {
  392. $file_name = md5(time()) . '.pdf';
  393. }
  394. $path = public_path() . '/pdf/' . $file_name;
  395. var_dump($path);
  396. exit;
  397. $shell = 'wkhtmltopdf --outline --minimum-font-size 9 ' . $html_path . ' ' . $path;
  398. exec($shell, $result, $status);
  399. if ($status) {
  400. $this->exit_out('生成PDF失败', ['生成PDF失败' => [$shell, $result, $status]]);
  401. }
  402. $domain = request()->getScheme() . '://' . request()->getHost();
  403. return $domain . '/pdf/' . $file_name;
  404. }
  405. public function exit_out($msg, $exceptionData = false, $code = 1, $data = [])
  406. {
  407. $out = ['code' => $code, 'msg' => $msg, 'data' => $data];
  408. if (false !== $exceptionData) {
  409. $this->trace([$msg => $exceptionData], 'error');
  410. }
  411. $json = json_encode($out, JSON_UNESCAPED_UNICODE);
  412. throw new ApiException($json);
  413. }
  414. public function trace($log = '', $level = 'info')
  415. {
  416. Log::log($level, $log);
  417. }
  418. /**
  419. * 保存图片.
  420. *
  421. * @return \Illuminate\Http\JsonResponse|string
  422. */
  423. private function saveImage($image_url)
  424. {
  425. $imageContent = file_get_contents($image_url);
  426. $imageDir = $_SERVER['DOCUMENT_ROOT'] . '/static/images/' . date('Ymd') . '/';
  427. $imageName = uniqid() . time() . '_image.png';
  428. $imagePath = $imageDir . $imageName;
  429. if (!is_dir($imageDir)) {
  430. mkdir($imageDir, 0777, true);
  431. }
  432. if (false !== $imageContent) {
  433. $savedImages = file_put_contents($imagePath, $imageContent);
  434. if (false !== $savedImages) {
  435. return $_SERVER['HTTP_HOST'] . '/static/images/' . date('Ymd') . '/' . $imageName;
  436. }
  437. return false;
  438. }
  439. return false;
  440. }
  441. /**
  442. * 执行请求
  443. */
  444. private function host($postData)
  445. {
  446. $ch = curl_init();
  447. curl_setopt_array($ch, [
  448. CURLOPT_URL => getenv('BAIDU_ERNIE_BOT40_URL') . '?access_token=' . Helper::getAccessToken(),
  449. CURLOPT_RETURNTRANSFER => true,
  450. CURLOPT_POST => true,
  451. CURLOPT_POSTFIELDS => $postData,
  452. ]);
  453. $res = curl_exec($ch);
  454. curl_close($ch);
  455. Log::warning('请求头:' .$postData);
  456. Log::warning('请求返回值:' .$res);
  457. return json_decode(trim($res), true);
  458. }
  459. public function extracted($result, $type = 0, $sdImage = ''): string
  460. {
  461. if (0 == $type) {
  462. $inputString = $result->init_content;
  463. $sdImage = $result->sd_image;
  464. } else {
  465. $inputString = $result;
  466. }
  467. $inputString = str_replace('“', '', $inputString);
  468. $inputString = str_replace('”', '', $inputString);
  469. $inputString = str_replace('——', '', $inputString);
  470. $inputString = str_replace('—', '', $inputString);
  471. $inputStringFor = $this->splitChineseCharacters($inputString, 1);
  472. $arr = [];
  473. $i = 0;
  474. foreach ($inputStringFor as $key => $value) {
  475. if ("\n" == $value) {
  476. $arr[] = $key - $i;
  477. $i++;
  478. }
  479. }
  480. $splitCharacters = $this->splitChineseCharacters($inputString);
  481. $pinyin = Pinyin::Sentence($inputString)->toArray();
  482. $string = '';
  483. $count = 0;
  484. foreach ($pinyin as $value) {
  485. $count += mb_strlen($value);
  486. }
  487. $img = '<img style="width: 100%;height:auto;float: right;margin-right: 10px;margin-top: 10px; margin-bottom: 5px" src="https://' . $sdImage . '"/>';
  488. $stringCount = 0;
  489. foreach ($pinyin as $key => $value) {
  490. if ($this->containsPunctuation($value)) {
  491. $value = '&nbsp;';
  492. }
  493. $stringCount += mb_strlen($value);
  494. if (in_array($count - $stringCount, [158, 159, 160, 161, 162, 163]) && !strstr($string, '<img')) {
  495. $string = $string . $img;
  496. }
  497. if (in_array($key + 1, $arr)) {
  498. // $string = $string . "<span><sup>{$value}</sup>{$splitCharacters[$key]}</span><br style='clear: both'><br style='clear: both'>";
  499. $string = $string . "<span>{$splitCharacters[$key]}</span><br style='clear: both'><br style='clear: both'>";
  500. } else {
  501. $style = '';
  502. // if (in_array($key, $arr) || 0 == $key) {
  503. // $style = 'style="margin-left: 5rem"';
  504. // }
  505. // $string = $string . '<span ' . $style . "><sup>{$value}</sup>{$splitCharacters[$key]}</span>";
  506. $string = $string . '<span ' . $style . ">{$splitCharacters[$key]}</span>";
  507. }
  508. }
  509. return $string;
  510. }
  511. }