| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- <?php
- namespace App\Http\Controllers\DeepSeek;
- use App\Transformer\DeepSeek\DeepSeekTransformer;
- use App\Facade\Site;
- use App\Consts\ErrorConst;
- use App\Exceptions\ApiException;
- use App\Libs\ApiResponse;
- use App\Libs\Utils;
- use App\Services\DeepSeek\DeepSeekService;
- use Illuminate\Http\Request;
- use Illuminate\Routing\Controller as BaseController;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Redis;
- use Illuminate\Support\Facades\Validator;
- class DeepSeekController extends BaseController
- {
- use ApiResponse;
- protected $deepseekService;
- public function __construct(
- DeepSeekService $deepseekService
- ) {
- $this->deepseekService = $deepseekService;
- }
- /**
- * 可选供应商
- *
- * @param Request $request
- * @return mixed
- */
- public function chatWithReasoner(Request $request) {
- // 忽略所有超时限制
- set_time_limit(0);
- ini_set('max_execution_time', '0');
- $data = $request->all();
- $result = $this->deepseekService->chatWithReasoner($data);
- return $this->success($result);
- }
- /**
- * 音色列表
- *
- * @param Request $request
- * @return mixed
- */
- public function timbreList(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->timbreList($data);
- return $this->success($result);
- }
- // 重置段落音频状态
- public function resetParagraphAudio(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->resetParagraphAudio($data);
- return $this->success(['success'=>$result ? 1 : 0]);
- }
- // 保存段落音频
- public function saveParagraphAudio(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->saveParagraphAudio($data);
- return $this->success(['success'=>$result ? 1 : 0]);
- }
- // 保存段落音频
- public function insertAudioEffect(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->insertAudioEffect($data);
- return $this->success(['success'=>$result ? 1 : 0]);
- }
- // 保存段落音频
- public function insertBgm(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->insertBgm($data);
- return $this->success(['success'=>$result ? 1 : 0]);
- }
- /**
- * 获取段落视频
- */
- public function paragraphAudios(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->getParagraphAudios($data);
- return $this->success($result);
- }
- /**
- * 新增合成任务
- *
- * @param Request $request
- * @return mixed
- */
- public function addGenerateTask(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->addGenerateTask($data);
- return $this->success(['success'=>$result ? 1 : 0]);
- }
- // 获取火山临时token
- public function setStsToken(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->setStsToken($data);
- return $this->success($result);
- }
- public function sseLink(Request $request) {
- $data = $request->all();
- $bid = getProp($data, 'bid');
- $version_id = getProp($data, 'version_id');
- $cid = getProp($data, 'cid');
- // 禁用输出缓冲
- while (ob_get_level()) ob_end_clean();
- // 设置SSE所需的HTTP头
- header('Content-Type: text/event-stream');
- header('Cache-Control: no-cache');
- header('Connection: keep-alive');
- header('X-Accel-Buffering: no'); // 防止Nginx缓冲
- // 允许跨域请求(根据需求设置)
- header('Access-Control-Allow-Origin: *');
- header('Access-Control-Allow-Credentials: true');
- // 设置脚本执行时间无限
- set_time_limit(0);
- // 手动刷新输出缓冲区
- ob_implicit_flush(true);
- if(ob_get_level()>0) ob_flush();
- flush();
- // 客户端ID(用于多用户区分)
- $clientId = $_SERVER['REMOTE_ADDR'] . ':' . $_SERVER['REMOTE_PORT'];
- $lastEventId = isset($_SERVER['HTTP_LAST_EVENT_ID']) ? intval($_SERVER['HTTP_LAST_EVENT_ID']) : 0;
- // dd($clientId, $lastEventId);
- // 发送初始欢迎消息
- $this->sendEvent([
- // 'message' => "欢迎使用SSE服务! 你的客户端ID: {$clientId}",
- // 'type' => 'info',
- // 'timestamp' => date('H:i:s')
- 'code' => 0,
- 'msg' => "已连接! 你的客户端ID: {$clientId}",
- 'data' => [],
- ]);
- if (!$bid && !$version_id && !$cid) {
- $this->sendEvent([
- // 'message' => '参数异常',
- // 'type' => 'error',
- // 'timestamp' => date('H:i:s')
- 'code' => -1,
- 'msg' => "参数异常",
- 'data' => [],
- ]);
- }
- $redis_key = "select-{$bid}-{$version_id}-{$cid}";
- // 主循环 - 定期发送数据
- while (true) {
- // 检查客户端是否断开连接
- if (connection_aborted()) {
- exit();
- }
- // 判断是否有待更新数据,有则查询
- $ids = Redis::smembers($redis_key);
- if (count($ids) > 0) {
- $count = DB::table('mp_chapter_paragraph_audios')->whereIn('id', $ids)->where(function($query) {
- return $query->where('generate_status', '!=', '制作中')->orWhere('error_msg', '!=', '');
- })->count('id');
- // 如果有更新数据,则发送消息
- if ($count > 0) {
- // 查询更新后的信息
- $paragraph_urls = DB::table('mp_chapter_paragraph_audios')->where('bid', $bid)->where('version_id', $version_id)
- ->where('cid', $cid)->where(function($query) {
- return $query->where('generate_status', '!=', '制作中')->orWhere('error_msg', '!=', '');
- })->select('id', 'sequence', 'generate_status', 'paragraph_audio_url', 'error_msg')
- ->get()->map(function ($value) {
- return (array)$value;
- })->toArray();
- $rem_ids = [];
- foreach ($paragraph_urls as $item) {
- $rem_ids[] = getProp($item, 'id');
- }
-
- // 发送消息
- $this->sendEvent([
- 'code' => 0,
- 'msg' => "",
- 'data' => $paragraph_urls,
- // 'message' => '数据更新',
- // 'type' => 'update',
- // 'timestamp' => date('H:i:s')
- ]);
- // 删除有数据的id
- Redis::srem($redis_key, $rem_ids);
- }
- }
-
- // 等待1秒
- sleep(1);
- }
- }
- private function sendEvent($data, $event = null, $id = null) {
- // // 事件ID
- // if ($id !== null) {
- // echo "id: {$id}\n";
- // }
-
- // 事件类型
- if ($event !== null) {
- echo "event: {$event}\n";
- }
-
- // 数据部分(JSON格式)
- echo "data: " . json_encode($data) . "\n\n";
-
- // 立即发送数据
- if(ob_get_level()>0) ob_flush();
- flush();
- }
- public function emotionGroups(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->emotionGroups($data);
- return $this->success($result);
- }
- /**
- * 剧本列表
- *
- * @param Request $request
- * @return mixed
- */
- public function scriptList(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->scriptList($data);
- return $this->success($result, [new DeepSeekTransformer(), 'newBuildScriptList']);
- }
- // 剧本详情
- public function scriptInfo(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->scriptInfo($data);
- return $this->success($result);
- }
- /**
- * 创建剧本
- *
- * @param Request $request
- * @return mixed
- */
- public function createScript(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->createScript($data);
- return $this->success(['script_id' => $result]);
- }
- /**
- * 与DeepSeek对话(带文件上传)
- * @param Request $request
- * @return mixed
- * @throws ApiException
- */
- public function chatWithFile(Request $request) {
- // 忽略所有超时限制
- set_time_limit(0);
- ini_set('max_execution_time', '0');
- $data = $request->all();
- $result = $this->deepseekService->chatWithFile($data);
- return $this->success($result);
- }
- /**
- * 与DeepSeek对话(带文件上传 - 流式输出版本)
- * @param Request $request
- * @return \Symfony\Component\HttpFoundation\StreamedResponse
- * @throws ApiException
- */
- public function chatWithFileStream(Request $request) {
- // 忽略所有超时限制
- set_time_limit(0);
- ini_set('max_execution_time', '0');
- $data = $request->all();
- return response()->stream(function () use ($data) {
- // 禁用所有输出缓冲
- if (ob_get_level()) {
- ob_end_clean();
- }
-
- // 设置输出缓冲为关闭
- ini_set('output_buffering', 'off');
- ini_set('zlib.output_compression', 'off');
-
- // 立即刷新
- if (function_exists('apache_setenv')) {
- apache_setenv('no-gzip', '1');
- }
- try {
- $generator = $this->deepseekService->chatWithFileStream($data);
-
- foreach ($generator as $chunk) {
- // 发送 SSE 格式的数据
- $jsonData = json_encode($chunk, JSON_UNESCAPED_UNICODE);
- echo "data: {$jsonData}\n\n";
-
- // 强制刷新输出缓冲区
- if (ob_get_level() > 0) {
- ob_flush();
- }
- flush();
-
- // 检查客户端是否断开连接
- if (connection_aborted()) {
- break;
- }
- }
-
- // 发送结束标记
- echo "data: [DONE]\n\n";
- if (ob_get_level() > 0) {
- ob_flush();
- }
- flush();
-
- } catch (\Exception $e) {
- // 发送错误信息
- $error = [
- 'type' => 'error',
- 'message' => $e->getMessage(),
- 'code' => $e->getCode()
- ];
- $jsonError = json_encode($error, JSON_UNESCAPED_UNICODE);
- echo "data: {$jsonError}\n\n";
- if (ob_get_level() > 0) {
- ob_flush();
- }
- flush();
- }
- }, 200, [
- 'Content-Type' => 'text/event-stream',
- 'Cache-Control' => 'no-cache',
- 'Connection' => 'keep-alive',
- 'X-Accel-Buffering' => 'no', // 防止 Nginx 缓冲
- 'Access-Control-Allow-Origin' => '*',
- 'Access-Control-Allow-Credentials' => 'true'
- ]);
- }
- // 保存剧本
- public function saveScript(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->saveScript($data);
- return $this->success(['success'=>$result ? 1 : 0]);
- }
- // 导出剧本
- public function exportScript(Request $request) {
- $data = $request->all();
-
- // 验证必要参数
- if (empty($data['script_id'])) {
- return $this->error('20003:缺少剧本ID参数');
- }
-
- // 直接调用导出服务,该方法会直接输出文件并退出
- $this->deepseekService->exportScript($data);
- }
-
- // 确认动漫剧本大纲
- public function confirmOutline(Request $request) {
- $data = $request->all();
- $result = $this->deepseekService->confirmOutline($data);
- return $this->success(['success'=>$result ? 1 : 0]);
- }
- }
|