DeepSeekController.php 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
  1. <?php
  2. namespace App\Http\Controllers\DeepSeek;
  3. use App\Transformer\DeepSeek\DeepSeekTransformer;
  4. use App\Facade\Site;
  5. use App\Consts\ErrorConst;
  6. use App\Exceptions\ApiException;
  7. use App\Libs\ApiResponse;
  8. use App\Libs\Utils;
  9. use App\Services\DeepSeek\DeepSeekService;
  10. use App\Services\TaskCenterService;
  11. use Illuminate\Http\Request;
  12. use Illuminate\Routing\Controller as BaseController;
  13. use Illuminate\Support\Facades\DB;
  14. use Illuminate\Support\Facades\Redis;
  15. use Illuminate\Support\Facades\Validator;
  16. class DeepSeekController extends BaseController
  17. {
  18. use ApiResponse;
  19. protected $deepseekService;
  20. protected $taskCenterService;
  21. public function __construct(
  22. DeepSeekService $deepseekService,
  23. TaskCenterService $taskCenterService
  24. ) {
  25. $this->deepseekService = $deepseekService;
  26. $this->taskCenterService = $taskCenterService;
  27. }
  28. /**
  29. * 可选供应商
  30. *
  31. * @param Request $request
  32. * @return mixed
  33. */
  34. public function chatWithReasoner(Request $request) {
  35. // 忽略所有超时限制
  36. set_time_limit(0);
  37. ini_set('max_execution_time', '0');
  38. $data = $request->all();
  39. $result = $this->deepseekService->chatWithReasoner($data);
  40. return $this->success($result);
  41. }
  42. /**
  43. * 音色列表
  44. *
  45. * @param Request $request
  46. * @return mixed
  47. */
  48. public function timbreList(Request $request) {
  49. $data = $request->all();
  50. $result = $this->deepseekService->timbreList($data);
  51. return $this->success($result);
  52. }
  53. // 重置段落音频状态
  54. public function resetParagraphAudio(Request $request) {
  55. $data = $request->all();
  56. $result = $this->deepseekService->resetParagraphAudio($data);
  57. return $this->success(['success'=>$result ? 1 : 0]);
  58. }
  59. // 保存段落音频
  60. public function saveParagraphAudio(Request $request) {
  61. $data = $request->all();
  62. $result = $this->deepseekService->saveParagraphAudio($data);
  63. return $this->success(['success'=>$result ? 1 : 0]);
  64. }
  65. // 保存段落音频
  66. public function insertAudioEffect(Request $request) {
  67. $data = $request->all();
  68. $result = $this->deepseekService->insertAudioEffect($data);
  69. return $this->success(['success'=>$result ? 1 : 0]);
  70. }
  71. // 保存段落音频
  72. public function insertBgm(Request $request) {
  73. $data = $request->all();
  74. $result = $this->deepseekService->insertBgm($data);
  75. return $this->success(['success'=>$result ? 1 : 0]);
  76. }
  77. /**
  78. * 获取段落视频
  79. */
  80. public function paragraphAudios(Request $request) {
  81. $data = $request->all();
  82. $result = $this->deepseekService->getParagraphAudios($data);
  83. return $this->success($result);
  84. }
  85. /**
  86. * 新增合成任务
  87. *
  88. * @param Request $request
  89. * @return mixed
  90. */
  91. public function addGenerateTask(Request $request) {
  92. $data = $request->all();
  93. $result = $this->deepseekService->addGenerateTask($data);
  94. return $this->success(['success'=>$result ? 1 : 0]);
  95. }
  96. // 获取火山临时token
  97. public function setStsToken(Request $request) {
  98. $data = $request->all();
  99. $result = $this->deepseekService->setStsToken($data);
  100. return $this->success($result);
  101. }
  102. public function sseLink(Request $request) {
  103. $data = $request->all();
  104. $bid = getProp($data, 'bid');
  105. $version_id = getProp($data, 'version_id');
  106. $cid = getProp($data, 'cid');
  107. // 禁用输出缓冲
  108. while (ob_get_level()) ob_end_clean();
  109. // 设置SSE所需的HTTP头
  110. header('Content-Type: text/event-stream');
  111. header('Cache-Control: no-cache');
  112. header('Connection: keep-alive');
  113. header('X-Accel-Buffering: no'); // 防止Nginx缓冲
  114. // 允许跨域请求(根据需求设置)
  115. header('Access-Control-Allow-Origin: *');
  116. header('Access-Control-Allow-Credentials: true');
  117. // 设置脚本执行时间无限
  118. set_time_limit(0);
  119. // 手动刷新输出缓冲区
  120. ob_implicit_flush(true);
  121. if(ob_get_level()>0) ob_flush();
  122. flush();
  123. // 客户端ID(用于多用户区分)
  124. $clientId = $_SERVER['REMOTE_ADDR'] . ':' . $_SERVER['REMOTE_PORT'];
  125. $lastEventId = isset($_SERVER['HTTP_LAST_EVENT_ID']) ? intval($_SERVER['HTTP_LAST_EVENT_ID']) : 0;
  126. // dd($clientId, $lastEventId);
  127. // 发送初始欢迎消息
  128. $this->sendEvent([
  129. // 'message' => "欢迎使用SSE服务! 你的客户端ID: {$clientId}",
  130. // 'type' => 'info',
  131. // 'timestamp' => date('H:i:s')
  132. 'code' => 0,
  133. 'msg' => "已连接! 你的客户端ID: {$clientId}",
  134. 'data' => [],
  135. ]);
  136. if (!$bid && !$version_id && !$cid) {
  137. $this->sendEvent([
  138. // 'message' => '参数异常',
  139. // 'type' => 'error',
  140. // 'timestamp' => date('H:i:s')
  141. 'code' => -1,
  142. 'msg' => "参数异常",
  143. 'data' => [],
  144. ]);
  145. }
  146. $redis_key = "select-{$bid}-{$version_id}-{$cid}";
  147. // 主循环 - 定期发送数据
  148. while (true) {
  149. // 检查客户端是否断开连接
  150. if (connection_aborted()) {
  151. exit();
  152. }
  153. // 判断是否有待更新数据,有则查询
  154. $ids = Redis::smembers($redis_key);
  155. if (count($ids) > 0) {
  156. $count = DB::table('mp_chapter_paragraph_audios')->whereIn('id', $ids)->where(function($query) {
  157. return $query->where('generate_status', '!=', '制作中')->orWhere('error_msg', '!=', '');
  158. })->count('id');
  159. // 如果有更新数据,则发送消息
  160. if ($count > 0) {
  161. // 查询更新后的信息
  162. $paragraph_urls = DB::table('mp_chapter_paragraph_audios')->where('bid', $bid)->where('version_id', $version_id)
  163. ->where('cid', $cid)->where(function($query) {
  164. return $query->where('generate_status', '!=', '制作中')->orWhere('error_msg', '!=', '');
  165. })->select('id', 'sequence', 'generate_status', 'paragraph_audio_url', 'error_msg')
  166. ->get()->map(function ($value) {
  167. return (array)$value;
  168. })->toArray();
  169. $rem_ids = [];
  170. foreach ($paragraph_urls as $item) {
  171. $rem_ids[] = getProp($item, 'id');
  172. }
  173. // 发送消息
  174. $this->sendEvent([
  175. 'code' => 0,
  176. 'msg' => "",
  177. 'data' => $paragraph_urls,
  178. // 'message' => '数据更新',
  179. // 'type' => 'update',
  180. // 'timestamp' => date('H:i:s')
  181. ]);
  182. // 删除有数据的id
  183. Redis::srem($redis_key, $rem_ids);
  184. }
  185. }
  186. // 等待1秒
  187. sleep(1);
  188. }
  189. }
  190. private function sendEvent($data, $event = null, $id = null) {
  191. // // 事件ID
  192. // if ($id !== null) {
  193. // echo "id: {$id}\n";
  194. // }
  195. // 事件类型
  196. if ($event !== null) {
  197. echo "event: {$event}\n";
  198. }
  199. // 数据部分(JSON格式)
  200. echo "data: " . json_encode($data) . "\n\n";
  201. // 立即发送数据
  202. if(ob_get_level()>0) ob_flush();
  203. flush();
  204. }
  205. public function emotionGroups(Request $request) {
  206. $data = $request->all();
  207. $result = $this->deepseekService->emotionGroups($data);
  208. return $this->success($result);
  209. }
  210. /**
  211. * 剧本列表
  212. *
  213. * @param Request $request
  214. * @return mixed
  215. */
  216. public function scriptList(Request $request) {
  217. $data = $request->all();
  218. $result = $this->deepseekService->scriptList($data);
  219. return $this->success($result, [new DeepSeekTransformer(), 'newBuildScriptList']);
  220. }
  221. public function scripts(Request $request) {
  222. $data = $request->all();
  223. $result = $this->deepseekService->scripts($data);
  224. return $this->success($result);
  225. }
  226. // 剧本详情
  227. public function scriptInfo(Request $request) {
  228. $data = $request->all();
  229. $result = $this->deepseekService->scriptInfo($data);
  230. return $this->success($result);
  231. }
  232. /**
  233. * 创建剧本
  234. *
  235. * @param Request $request
  236. * @return mixed
  237. */
  238. public function createScript(Request $request) {
  239. $data = $request->all();
  240. $result = $this->deepseekService->createScript($data);
  241. return $this->success(['script_id' => $result]);
  242. }
  243. // 编辑剧本
  244. public function editScript(Request $request) {
  245. $data = $request->all();
  246. $result = $this->deepseekService->editScript($data);
  247. return $this->success(['success' => $result ? 1 : 0]);
  248. }
  249. // 删除剧本
  250. public function delScript(Request $request) {
  251. $data = $request->all();
  252. $result = $this->deepseekService->delScript($data);
  253. return $this->success(['success' => $result ? 1 : 0]);
  254. }
  255. /**
  256. * 创建分集组
  257. *
  258. * @param Request $request
  259. * @return mixed
  260. */
  261. public function createEpisode(Request $request) {
  262. $data = $request->all();
  263. $result = $this->deepseekService->createEpisode($data);
  264. return $this->success(['group_id' => $result]);
  265. }
  266. // 编辑剧本
  267. public function editEpisode(Request $request) {
  268. $data = $request->all();
  269. $result = $this->deepseekService->editEpisode($data);
  270. return $this->success(['success' => $result ? 1 : 0]);
  271. }
  272. // 删除剧本
  273. public function delEpisode(Request $request) {
  274. $data = $request->all();
  275. $result = $this->deepseekService->delEpisode($data);
  276. return $this->success(['success' => $result ? 1 : 0]);
  277. }
  278. /**
  279. * 与DeepSeek对话(带文件上传)
  280. * @param Request $request
  281. * @return mixed
  282. * @throws ApiException
  283. */
  284. public function chatWithFile(Request $request) {
  285. // 忽略所有超时限制
  286. set_time_limit(0);
  287. ini_set('max_execution_time', '0');
  288. $data = $request->all();
  289. $result = $this->deepseekService->chatWithFile($data);
  290. return $this->success($result);
  291. }
  292. /**
  293. * 与DeepSeek对话(带文件上传 - 流式输出版本)
  294. * @param Request $request
  295. * @return \Symfony\Component\HttpFoundation\StreamedResponse
  296. * @throws ApiException
  297. */
  298. public function chatWithFileStream(Request $request) {
  299. // 忽略所有超时限制
  300. set_time_limit(0);
  301. ini_set('max_execution_time', '0');
  302. $data = $request->all();
  303. return response()->stream(function () use ($data) {
  304. // 禁用所有输出缓冲
  305. if (ob_get_level()) {
  306. ob_end_clean();
  307. }
  308. // 设置输出缓冲为关闭
  309. ini_set('output_buffering', 'off');
  310. ini_set('zlib.output_compression', 'off');
  311. // 立即刷新
  312. if (function_exists('apache_setenv')) {
  313. apache_setenv('no-gzip', '1');
  314. }
  315. try {
  316. $generator = $this->deepseekService->chatWithFileStream($data);
  317. foreach ($generator as $chunk) {
  318. // 发送 SSE 格式的数据
  319. $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
  320. echo "data: {$jsonData}\n\n";
  321. // 强制刷新输出缓冲区
  322. if (ob_get_level() > 0) {
  323. ob_flush();
  324. }
  325. flush();
  326. // 检查客户端是否断开连接
  327. if (connection_aborted()) {
  328. break;
  329. }
  330. }
  331. // 发送结束标记
  332. echo "data: [DONE]\n\n";
  333. if (ob_get_level() > 0) {
  334. ob_flush();
  335. }
  336. flush();
  337. } catch (\Exception $e) {
  338. // 发送错误信息
  339. $error = [
  340. 'type' => 'error',
  341. 'msg' => $e->getMessage(),
  342. 'code' => $e->getCode()
  343. ];
  344. $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
  345. echo "data: {$jsonError}\n\n";
  346. if (ob_get_level() > 0) {
  347. ob_flush();
  348. }
  349. flush();
  350. }
  351. }, 200, [
  352. 'Content-Type' => 'text/event-stream',
  353. 'Cache-Control' => 'no-cache',
  354. 'Connection' => 'keep-alive',
  355. 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
  356. 'Access-Control-Allow-Origin' => '*',
  357. 'Access-Control-Allow-Credentials' => 'true'
  358. ]);
  359. }
  360. public function generateEpisodes(Request $request) {
  361. // 忽略所有超时限制
  362. set_time_limit(0);
  363. ini_set('max_execution_time', '0');
  364. $data = $request->all();
  365. return response()->stream(function () use ($data) {
  366. // 禁用所有输出缓冲
  367. if (ob_get_level()) {
  368. ob_end_clean();
  369. }
  370. // 设置输出缓冲为关闭
  371. ini_set('output_buffering', 'off');
  372. ini_set('zlib.output_compression', 'off');
  373. // 立即刷新
  374. if (function_exists('apache_setenv')) {
  375. apache_setenv('no-gzip', '1');
  376. }
  377. try {
  378. $generator = $this->deepseekService->generateEpisodes($data);
  379. foreach ($generator as $chunk) {
  380. // 发送 SSE 格式的数据
  381. $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
  382. echo "data: {$jsonData}\n\n";
  383. // 强制刷新输出缓冲区
  384. if (ob_get_level() > 0) {
  385. ob_flush();
  386. }
  387. flush();
  388. // 检查客户端是否断开连接
  389. if (connection_aborted()) {
  390. break;
  391. }
  392. }
  393. // 发送结束标记
  394. echo "data: [DONE]\n\n";
  395. if (ob_get_level() > 0) {
  396. ob_flush();
  397. }
  398. flush();
  399. } catch (\Exception $e) {
  400. // 发送错误信息
  401. $error = [
  402. 'type' => 'error',
  403. 'msg' => $e->getMessage(),
  404. 'code' => $e->getCode()
  405. ];
  406. $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
  407. echo "data: {$jsonError}\n\n";
  408. if (ob_get_level() > 0) {
  409. ob_flush();
  410. }
  411. flush();
  412. }
  413. }, 200, [
  414. 'Content-Type' => 'text/event-stream',
  415. 'Cache-Control' => 'no-cache',
  416. 'Connection' => 'keep-alive',
  417. 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
  418. 'Access-Control-Allow-Origin' => '*',
  419. 'Access-Control-Allow-Credentials' => 'true'
  420. ]);
  421. }
  422. public function getEpisodeContent(Request $request) {
  423. $data = $request->all();
  424. $result = $this->deepseekService->getEpisodeContent($data);
  425. return $this->success($result);
  426. }
  427. // 保存剧本
  428. public function saveEpisodeContent(Request $request) {
  429. $data = $request->all();
  430. $result = $this->deepseekService->saveEpisodeContent($data);
  431. return $this->success(['success'=>$result ? 1 : 0]);
  432. }
  433. // 导出剧本
  434. public function exportScript(Request $request) {
  435. $data = $request->all();
  436. // 验证必要参数
  437. if (empty($data['script_id'])) {
  438. return $this->error('20003:缺少剧本ID参数');
  439. }
  440. // 直接调用导出服务,该方法会直接输出文件并退出
  441. $this->deepseekService->exportScript($data);
  442. }
  443. public function getBookContent(Request $request) {
  444. $data = $request->all();
  445. // 直接调用导出服务,该方法会直接输出文件并退出
  446. $result = $this->deepseekService->getBookContent($data);
  447. return $this->success($result);
  448. }
  449. public function createGenerateText(Request $request) {
  450. // 忽略所有超时限制
  451. set_time_limit(0);
  452. ini_set('max_execution_time', '0');
  453. $data = $request->all();
  454. $result = $this->deepseekService->createGenerateText($data);
  455. return $this->success($result);
  456. }
  457. /**
  458. * 通用文生文(非流式版本)
  459. * @param Request $request
  460. * @return mixed
  461. */
  462. public function newGenerateText(Request $request) {
  463. // 忽略所有超时限制
  464. set_time_limit(0);
  465. ini_set('max_execution_time', '0');
  466. $data = $request->all();
  467. // stream=1:SSE 流式输出(格式与 generateText 一致:yield type=content/reasoning/done)
  468. if ((int)getProp($data, 'stream', 0) === 1) {
  469. return response()->stream(function () use ($data) {
  470. if (ob_get_level()) {
  471. ob_end_clean();
  472. }
  473. ini_set('output_buffering', 'off');
  474. ini_set('zlib.output_compression', 'off');
  475. if (function_exists('apache_setenv')) {
  476. apache_setenv('no-gzip', '1');
  477. }
  478. try {
  479. $result = $this->deepseekService->newGenerateText($data);
  480. foreach ($result as $chunk) {
  481. echo "data: " . json_encode($chunk, JSON_UNESCAPED_UNICODE) . "\n\n";
  482. if (ob_get_level() > 0) {
  483. ob_flush();
  484. }
  485. flush();
  486. if (connection_aborted()) {
  487. break;
  488. }
  489. }
  490. } catch (\Throwable $e) {
  491. echo "data: " . json_encode([
  492. 'type' => 'error',
  493. 'message' => $e->getMessage(),
  494. ], JSON_UNESCAPED_UNICODE) . "\n\n";
  495. if (ob_get_level() > 0) {
  496. ob_flush();
  497. }
  498. flush();
  499. }
  500. }, 200, [
  501. 'Content-Type' => 'text/event-stream',
  502. 'Cache-Control' => 'no-cache',
  503. 'Connection' => 'keep-alive',
  504. 'X-Accel-Buffering' => 'no',
  505. ]);
  506. }
  507. $result = $this->deepseekService->newGenerateText($data);
  508. return $this->success($result);
  509. }
  510. /**
  511. * 获取剧本资产生成任务列表
  512. * @param Request $request
  513. * @return mixed
  514. */
  515. public function getScriptGenerateTasks(Request $request) {
  516. $data = $request->all();
  517. $result = $this->deepseekService->getScriptGenerateTasks($data);
  518. return $this->success($result, [new DeepSeekTransformer(), 'newBuildScriptGenerateTaskList']);
  519. }
  520. // 保存剧本沟通记录
  521. public function saveScriptChatHistory(Request $request) {
  522. $data = $request->all();
  523. $result = $this->deepseekService->saveScriptChatHistory($data);
  524. return $this->success(['success'=>$result ? 1 : 0]);
  525. }
  526. /**
  527. * 获取剧本的历史对话记录
  528. * @param Request $request
  529. * @return mixed
  530. */
  531. public function getScriptChatHistory(Request $request) {
  532. $data = $request->all();
  533. $result = $this->deepseekService->getScriptChatHistory($data);
  534. return $this->success($result);
  535. }
  536. // 新建对话
  537. public function addChat(Request $request) {
  538. // 忽略所有超时限制
  539. set_time_limit(0);
  540. ini_set('max_execution_time', '0');
  541. $data = $request->all();
  542. return response()->stream(function () use ($data) {
  543. // 禁用所有输出缓冲
  544. if (ob_get_level()) {
  545. ob_end_clean();
  546. }
  547. // 设置输出缓冲为关闭
  548. ini_set('output_buffering', 'off');
  549. ini_set('zlib.output_compression', 'off');
  550. // 立即刷新
  551. if (function_exists('apache_setenv')) {
  552. apache_setenv('no-gzip', '1');
  553. }
  554. try {
  555. $generator = $this->deepseekService->addChat($data);
  556. foreach ($generator as $chunk) {
  557. // 发送 SSE 格式的数据
  558. $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
  559. echo "data: {$jsonData}\n\n";
  560. // 强制刷新输出缓冲区
  561. if (ob_get_level() > 0) {
  562. ob_flush();
  563. }
  564. flush();
  565. // 检查客户端是否断开连接
  566. if (connection_aborted()) {
  567. break;
  568. }
  569. }
  570. // 发送结束标记
  571. echo "data: [DONE]\n\n";
  572. if (ob_get_level() > 0) {
  573. ob_flush();
  574. }
  575. flush();
  576. } catch (\Exception $e) {
  577. // 发送错误信息
  578. $error = [
  579. 'type' => 'error',
  580. 'msg' => $e->getMessage(),
  581. 'code' => $e->getCode()
  582. ];
  583. $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
  584. echo "data: {$jsonError}\n\n";
  585. if (ob_get_level() > 0) {
  586. ob_flush();
  587. }
  588. flush();
  589. }
  590. }, 200, [
  591. 'Content-Type' => 'text/event-stream',
  592. 'Cache-Control' => 'no-cache',
  593. 'Connection' => 'keep-alive',
  594. 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
  595. 'Access-Control-Allow-Origin' => '*',
  596. 'Access-Control-Allow-Credentials' => 'true'
  597. ]);
  598. }
  599. // 调整大纲
  600. public function reGenerateAnime(Request $request) {
  601. // 忽略所有超时限制
  602. set_time_limit(0);
  603. ini_set('max_execution_time', '0');
  604. $data = $request->all();
  605. return response()->stream(function () use ($data) {
  606. // 禁用所有输出缓冲
  607. if (ob_get_level()) {
  608. ob_end_clean();
  609. }
  610. // 设置输出缓冲为关闭
  611. ini_set('output_buffering', 'off');
  612. ini_set('zlib.output_compression', 'off');
  613. // 立即刷新
  614. if (function_exists('apache_setenv')) {
  615. apache_setenv('no-gzip', '1');
  616. }
  617. try {
  618. $generator = $this->deepseekService->reGenerateAnime($data);
  619. foreach ($generator as $chunk) {
  620. // 发送 SSE 格式的数据
  621. $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
  622. echo "data: {$jsonData}\n\n";
  623. // 强制刷新输出缓冲区
  624. if (ob_get_level() > 0) {
  625. ob_flush();
  626. }
  627. flush();
  628. // 检查客户端是否断开连接
  629. if (connection_aborted()) {
  630. break;
  631. }
  632. }
  633. // 发送结束标记
  634. echo "data: [DONE]\n\n";
  635. if (ob_get_level() > 0) {
  636. ob_flush();
  637. }
  638. flush();
  639. } catch (\Exception $e) {
  640. // 发送错误信息
  641. $error = [
  642. 'type' => 'error',
  643. 'msg' => $e->getMessage(),
  644. 'code' => $e->getCode()
  645. ];
  646. $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
  647. echo "data: {$jsonError}\n\n";
  648. if (ob_get_level() > 0) {
  649. ob_flush();
  650. }
  651. flush();
  652. }
  653. }, 200, [
  654. 'Content-Type' => 'text/event-stream',
  655. 'Cache-Control' => 'no-cache',
  656. 'Connection' => 'keep-alive',
  657. 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
  658. 'Access-Control-Allow-Origin' => '*',
  659. 'Access-Control-Allow-Credentials' => 'true'
  660. ]);
  661. }
  662. public function chat(Request $request) {
  663. // 忽略所有超时限制
  664. set_time_limit(0);
  665. ini_set('max_execution_time', '0');
  666. $data = $request->all();
  667. return response()->stream(function () use ($data) {
  668. // 禁用所有输出缓冲
  669. if (ob_get_level()) {
  670. ob_end_clean();
  671. }
  672. // 设置输出缓冲为关闭
  673. ini_set('output_buffering', 'off');
  674. ini_set('zlib.output_compression', 'off');
  675. // 立即刷新
  676. if (function_exists('apache_setenv')) {
  677. apache_setenv('no-gzip', '1');
  678. }
  679. try {
  680. $generator = $this->deepseekService->chat($data);
  681. foreach ($generator as $chunk) {
  682. // 发送 SSE 格式的数据
  683. $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
  684. echo "data: {$jsonData}\n\n";
  685. // 强制刷新输出缓冲区
  686. if (ob_get_level() > 0) {
  687. ob_flush();
  688. }
  689. flush();
  690. // 检查客户端是否断开连接
  691. if (connection_aborted()) {
  692. break;
  693. }
  694. }
  695. // 发送结束标记
  696. echo "data: [DONE]\n\n";
  697. if (ob_get_level() > 0) {
  698. ob_flush();
  699. }
  700. flush();
  701. } catch (\Exception $e) {
  702. // 发送错误信息
  703. $error = [
  704. 'type' => 'error',
  705. 'msg' => $e->getMessage(),
  706. 'code' => $e->getCode()
  707. ];
  708. $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
  709. echo "data: {$jsonError}\n\n";
  710. if (ob_get_level() > 0) {
  711. ob_flush();
  712. }
  713. flush();
  714. }
  715. }, 200, [
  716. 'Content-Type' => 'text/event-stream',
  717. 'Cache-Control' => 'no-cache',
  718. 'Connection' => 'keep-alive',
  719. 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
  720. 'Access-Control-Allow-Origin' => '*',
  721. 'Access-Control-Allow-Credentials' => 'true'
  722. ]);
  723. }
  724. // 新建对话
  725. public function addChatForAce(Request $request) {
  726. // 忽略所有超时限制
  727. set_time_limit(0);
  728. ini_set('max_execution_time', '0');
  729. $data = $request->all();
  730. return response()->stream(function () use ($data) {
  731. // 禁用所有输出缓冲
  732. if (ob_get_level()) {
  733. ob_end_clean();
  734. }
  735. // 设置输出缓冲为关闭
  736. ini_set('output_buffering', 'off');
  737. ini_set('zlib.output_compression', 'off');
  738. // 立即刷新
  739. if (function_exists('apache_setenv')) {
  740. apache_setenv('no-gzip', '1');
  741. }
  742. try {
  743. $generator = $this->deepseekService->addChatForAce($data);
  744. foreach ($generator as $chunk) {
  745. // 发送 SSE 格式的数据
  746. $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
  747. echo "data: {$jsonData}\n\n";
  748. // 强制刷新输出缓冲区
  749. if (ob_get_level() > 0) {
  750. ob_flush();
  751. }
  752. flush();
  753. // 检查客户端是否断开连接
  754. if (connection_aborted()) {
  755. break;
  756. }
  757. }
  758. // 发送结束标记
  759. echo "data: [DONE]\n\n";
  760. if (ob_get_level() > 0) {
  761. ob_flush();
  762. }
  763. flush();
  764. } catch (\Exception $e) {
  765. // 发送错误信息
  766. $error = [
  767. 'type' => 'error',
  768. 'msg' => $e->getMessage(),
  769. 'code' => $e->getCode()
  770. ];
  771. $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
  772. echo "data: {$jsonError}\n\n";
  773. if (ob_get_level() > 0) {
  774. ob_flush();
  775. }
  776. flush();
  777. }
  778. }, 200, [
  779. 'Content-Type' => 'text/event-stream',
  780. 'Cache-Control' => 'no-cache',
  781. 'Connection' => 'keep-alive',
  782. 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
  783. 'Access-Control-Allow-Origin' => '*',
  784. 'Access-Control-Allow-Credentials' => 'true'
  785. ]);
  786. }
  787. // 调整大纲
  788. public function reGenerateAnimeForAce(Request $request) {
  789. // 忽略所有超时限制
  790. set_time_limit(0);
  791. ini_set('max_execution_time', '0');
  792. $data = $request->all();
  793. return response()->stream(function () use ($data) {
  794. // 禁用所有输出缓冲
  795. if (ob_get_level()) {
  796. ob_end_clean();
  797. }
  798. // 设置输出缓冲为关闭
  799. ini_set('output_buffering', 'off');
  800. ini_set('zlib.output_compression', 'off');
  801. // 立即刷新
  802. if (function_exists('apache_setenv')) {
  803. apache_setenv('no-gzip', '1');
  804. }
  805. try {
  806. $generator = $this->deepseekService->reGenerateAnimeForAce($data);
  807. foreach ($generator as $chunk) {
  808. // 发送 SSE 格式的数据
  809. $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
  810. echo "data: {$jsonData}\n\n";
  811. // 强制刷新输出缓冲区
  812. if (ob_get_level() > 0) {
  813. ob_flush();
  814. }
  815. flush();
  816. // 检查客户端是否断开连接
  817. if (connection_aborted()) {
  818. break;
  819. }
  820. }
  821. // 发送结束标记
  822. echo "data: [DONE]\n\n";
  823. if (ob_get_level() > 0) {
  824. ob_flush();
  825. }
  826. flush();
  827. } catch (\Exception $e) {
  828. // 发送错误信息
  829. $error = [
  830. 'type' => 'error',
  831. 'msg' => $e->getMessage(),
  832. 'code' => $e->getCode()
  833. ];
  834. $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
  835. echo "data: {$jsonError}\n\n";
  836. if (ob_get_level() > 0) {
  837. ob_flush();
  838. }
  839. flush();
  840. }
  841. }, 200, [
  842. 'Content-Type' => 'text/event-stream',
  843. 'Cache-Control' => 'no-cache',
  844. 'Connection' => 'keep-alive',
  845. 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
  846. 'Access-Control-Allow-Origin' => '*',
  847. 'Access-Control-Allow-Credentials' => 'true'
  848. ]);
  849. }
  850. public function chatForAce(Request $request) {
  851. // 忽略所有超时限制
  852. set_time_limit(0);
  853. ini_set('max_execution_time', '0');
  854. $data = $request->all();
  855. return response()->stream(function () use ($data) {
  856. // 禁用所有输出缓冲
  857. if (ob_get_level()) {
  858. ob_end_clean();
  859. }
  860. // 设置输出缓冲为关闭
  861. ini_set('output_buffering', 'off');
  862. ini_set('zlib.output_compression', 'off');
  863. // 立即刷新
  864. if (function_exists('apache_setenv')) {
  865. apache_setenv('no-gzip', '1');
  866. }
  867. try {
  868. $generator = $this->deepseekService->chatForAce($data);
  869. foreach ($generator as $chunk) {
  870. // 发送 SSE 格式的数据
  871. $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
  872. echo "data: {$jsonData}\n\n";
  873. // 强制刷新输出缓冲区
  874. if (ob_get_level() > 0) {
  875. ob_flush();
  876. }
  877. flush();
  878. // 检查客户端是否断开连接
  879. if (connection_aborted()) {
  880. break;
  881. }
  882. }
  883. // 发送结束标记
  884. echo "data: [DONE]\n\n";
  885. if (ob_get_level() > 0) {
  886. ob_flush();
  887. }
  888. flush();
  889. } catch (\Exception $e) {
  890. // 发送错误信息
  891. $error = [
  892. 'type' => 'error',
  893. 'msg' => $e->getMessage(),
  894. 'code' => $e->getCode()
  895. ];
  896. $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
  897. echo "data: {$jsonError}\n\n";
  898. if (ob_get_level() > 0) {
  899. ob_flush();
  900. }
  901. flush();
  902. }
  903. }, 200, [
  904. 'Content-Type' => 'text/event-stream',
  905. 'Cache-Control' => 'no-cache',
  906. 'Connection' => 'keep-alive',
  907. 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
  908. 'Access-Control-Allow-Origin' => '*',
  909. 'Access-Control-Allow-Credentials' => 'true'
  910. ]);
  911. }
  912. // 对话改图
  913. public function chatChangeImg(Request $request) {
  914. $data = $request->all();
  915. $result = $this->deepseekService->chatChangeImg($data);
  916. return $this->success($result);
  917. }
  918. /**
  919. * 通用文生文接口 - 流式输出
  920. * @param Request $request
  921. * @return \Symfony\Component\HttpFoundation\StreamedResponse
  922. */
  923. public function generateText(Request $request) {
  924. // 忽略所有超时限制
  925. set_time_limit(0);
  926. ini_set('max_execution_time', '0');
  927. $data = $request->all();
  928. // 处理上传的图片文件
  929. if ($request->hasFile('images')) {
  930. $data['images'] = $request->file('images');
  931. } else if ($request->hasFile('image')) {
  932. // 支持单张图片
  933. $data['images'] = [$request->file('image')];
  934. }
  935. // 处理上传的视频文件(仅 Gemini 模型支持)
  936. if ($request->hasFile('videos')) {
  937. $data['videos'] = $request->file('videos');
  938. } else if ($request->hasFile('video')) {
  939. $data['videos'] = [$request->file('video')];
  940. }
  941. return response()->stream(function () use ($data) {
  942. // 禁用所有输出缓冲
  943. if (ob_get_level()) {
  944. ob_end_clean();
  945. }
  946. // 设置输出缓冲为关闭
  947. ini_set('output_buffering', 'off');
  948. ini_set('zlib.output_compression', 'off');
  949. // 立即刷新
  950. if (function_exists('apache_setenv')) {
  951. apache_setenv('no-gzip', '1');
  952. }
  953. try {
  954. // 构建可序列化的参数快照(剔除上传文件对象)
  955. $paramsSnapshot = $data;
  956. unset($paramsSnapshot['images'], $paramsSnapshot['videos']);
  957. // 创建任务中心记录(文字任务直接更新任务结果)
  958. $taskCenter = $this->taskCenterService->createTask('text', [
  959. 'title' => '文生文',
  960. 'prompt' => getProp($data, 'prompt', ''),
  961. 'params' => json_encode($paramsSnapshot, JSON_UNESCAPED_UNICODE),
  962. ]);
  963. $taskCenterId = $taskCenter->id;
  964. $generator = $this->deepseekService->generateText($data);
  965. $fullContent = '';
  966. $firstChunk = true;
  967. foreach ($generator as $chunk) {
  968. // 第一条消息附加任务中心ID(不改变原消息结构和输出顺序)
  969. if ($firstChunk) {
  970. $chunk['task_center_id'] = $taskCenterId;
  971. $firstChunk = false;
  972. }
  973. // 累积完整文本内容
  974. if (isset($chunk['type']) && $chunk['type'] === 'content') {
  975. $fullContent .= (string)getProp($chunk, 'content', '');
  976. }
  977. if (isset($chunk['type']) && $chunk['type'] === 'done') {
  978. $fullContent = (string)getProp($chunk, 'full_content', $fullContent);
  979. }
  980. // 发送 SSE 格式的数据
  981. $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
  982. echo "data: {$jsonData}\n\n";
  983. // 强制刷新输出缓冲区
  984. if (ob_get_level() > 0) {
  985. ob_flush();
  986. }
  987. flush();
  988. // 检查客户端是否断开连接
  989. if (connection_aborted()) {
  990. break;
  991. }
  992. }
  993. // 发送结束标记
  994. echo "data: [DONE]\n\n";
  995. if (ob_get_level() > 0) {
  996. ob_flush();
  997. }
  998. flush();
  999. // 文字任务直接更新任务中心结果
  1000. $this->taskCenterService->updateTask($taskCenterId, [
  1001. 'status' => 'success',
  1002. 'result' => $fullContent,
  1003. 'error_message' => null,
  1004. ]);
  1005. } catch (\Exception $e) {
  1006. // 任务失败,更新任务中心状态(任务中心记录可能未创建成功)
  1007. if (isset($taskCenterId)) {
  1008. $this->taskCenterService->updateTask($taskCenterId, [
  1009. 'status' => 'failed',
  1010. 'error_message' => $e->getMessage(),
  1011. ]);
  1012. }
  1013. // 发送错误信息
  1014. $error = [
  1015. 'type' => 'error',
  1016. 'msg' => $e->getMessage(),
  1017. 'code' => $e->getCode()
  1018. ];
  1019. $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
  1020. echo "data: {$jsonError}\n\n";
  1021. if (ob_get_level() > 0) {
  1022. ob_flush();
  1023. }
  1024. flush();
  1025. }
  1026. }, 200, [
  1027. 'Content-Type' => 'text/event-stream',
  1028. 'Cache-Control' => 'no-cache',
  1029. 'Connection' => 'keep-alive',
  1030. 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
  1031. 'Access-Control-Allow-Origin' => '*',
  1032. 'Access-Control-Allow-Credentials' => 'true'
  1033. ]);
  1034. }
  1035. /**
  1036. * 重新生成分段剧本
  1037. * @param Request $request
  1038. * @return mixed
  1039. */
  1040. public function regenerateSegmentScript(Request $request) {
  1041. // 忽略所有超时限制
  1042. set_time_limit(0);
  1043. ini_set('max_execution_time', '0');
  1044. $data = $request->all();
  1045. $result = $this->deepseekService->regenerateSegmentScript($data);
  1046. return $this->success($result);
  1047. }
  1048. /**
  1049. * 保存剧集片段(预览确认模式使用):删除旧数据并保存新数据
  1050. * @param Request $request
  1051. * @return mixed
  1052. */
  1053. public function saveEpisodeActs(Request $request) {
  1054. // 忽略所有超时限制
  1055. set_time_limit(0);
  1056. ini_set('max_execution_time', '0');
  1057. $data = $request->all();
  1058. $result = $this->deepseekService->saveEpisodeActs($data);
  1059. return $this->success($result);
  1060. }
  1061. /**
  1062. * 批量生成多个分集
  1063. * @param Request $request
  1064. * @return mixed
  1065. */
  1066. public function batchGenerateEpisodes(Request $request) {
  1067. $data = $request->all();
  1068. $result = $this->deepseekService->batchGenerateEpisodes($data);
  1069. return $this->success($result);
  1070. }
  1071. /**
  1072. * 获取批量生成任务状态
  1073. * @param Request $request
  1074. * @return mixed
  1075. */
  1076. public function getBatchGenerationTaskStatus(Request $request) {
  1077. $data = $request->all();
  1078. $result = $this->deepseekService->getBatchGenerationTaskStatus($data);
  1079. return $this->success($result);
  1080. }
  1081. }