handle(true, fn (array $p) => Logic::prepare($p, $this->adminId)); } public function complete() { return $this->handle(true, fn (array $p) => Logic::complete($p, $this->adminId)); } public function status() { return $this->handle(false, fn (array $p) => Logic::status($p, $this->adminId)); } public function transcript() { return $this->handle(false, fn (array $p) => Logic::transcript($p, $this->adminId)); } public function audio() { return $this->handle(false, fn (array $p) => Logic::audio($p, $this->adminId, (string) $this->request->header('range', ''))); } public function chunk() { return $this->handle(true, function (array $p) { $type = strtolower(trim(explode(';', (string) $this->request->header('content-type', ''))[0])); if ($type !== 'application/octet-stream') { throw new DomainException('分片必须使用 application/octet-stream'); } return Logic::chunk($p, $this->request->getInput(), $this->adminId); }, true); } private function handle(bool $post, callable $operation, bool $binary = false) { $headers = ['Cache-Control' => 'private, no-store, max-age=0', 'X-Content-Type-Options' => 'nosniff']; $rejected = ['error_code' => 'request_rejected', 'retryable' => false]; if ($post ? !$this->request->isPost() : !$this->request->isGet()) { return $this->fail('请求方式错误', $rejected)->header($headers); } try { MobileRecordingLogic::actor($this->adminId); if ($this->request->file()) { throw new DomainException('请使用分片接口上传,不接受文件名或表单附件'); } $params = $binary || !$post ? $this->request->get('', null, null) : $this->request->post('', null, null); $result = $operation($params); return ($result instanceof Response ? $result : $this->data($result))->header($headers); } catch (MobileRecordingResyncException $e) { return $this->fail($e->getMessage(), ['error_code' => MobileRecordingResyncException::ERROR_CODE, 'retryable' => true])->header($headers); } catch (DomainException $e) { return $this->fail($e->getMessage(), $rejected)->header($headers); } catch (\Throwable $e) { return $this->fail('手机录音上传服务暂不可用,请稍后重试或联系管理员', ['error_code' => 'temporarily_unavailable', 'retryable' => true])->header($headers); } } }