AiController.php 20 KB

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