From 14d114771bec6179b5ddac75a40ac8abf5929175 Mon Sep 17 00:00:00 2001 From: 15090180611 <215509543@qq.com> Date: Fri, 28 Feb 2025 19:06:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/controller/deepseek/Question.php | 71 ++++ .../admin/controller/deepseek/Stream.php | 71 ++++ .../admin/lang/zh-cn/deepseek/question.php | 12 + .../admin/lang/zh-cn/deepseek/stream.php | 15 + application/admin/model/deepseek/Question.php | 52 +++ application/admin/model/deepseek/Stream.php | 52 +++ .../admin/validate/deepseek/Question.php | 27 ++ .../admin/validate/deepseek/Stream.php | 27 ++ .../admin/view/deepseek/question/add.html | 27 ++ .../admin/view/deepseek/question/edit.html | 27 ++ .../admin/view/deepseek/question/index.html | 29 ++ .../admin/view/deepseek/stream/add.html | 71 ++++ .../admin/view/deepseek/stream/edit.html | 71 ++++ .../admin/view/deepseek/stream/index.html | 29 ++ .../api/controller/deepseek/Deepseek.php | 239 ++++++++++++ application/common/controller/Api.php | 27 ++ application/common/controller/Backend.php | 94 ++++- .../common/model/deepseek/Question.php | 350 ++++++++++++++++++ application/common/model/deepseek/Stream.php | 133 +++++++ application/config.php | 13 +- public/assets/js/backend/deepseek/question.js | 57 +++ public/assets/js/backend/deepseek/stream.js | 58 +++ 22 files changed, 1546 insertions(+), 6 deletions(-) create mode 100644 application/admin/controller/deepseek/Question.php create mode 100644 application/admin/controller/deepseek/Stream.php create mode 100644 application/admin/lang/zh-cn/deepseek/question.php create mode 100644 application/admin/lang/zh-cn/deepseek/stream.php create mode 100644 application/admin/model/deepseek/Question.php create mode 100644 application/admin/model/deepseek/Stream.php create mode 100644 application/admin/validate/deepseek/Question.php create mode 100644 application/admin/validate/deepseek/Stream.php create mode 100644 application/admin/view/deepseek/question/add.html create mode 100644 application/admin/view/deepseek/question/edit.html create mode 100644 application/admin/view/deepseek/question/index.html create mode 100644 application/admin/view/deepseek/stream/add.html create mode 100644 application/admin/view/deepseek/stream/edit.html create mode 100644 application/admin/view/deepseek/stream/index.html create mode 100644 application/api/controller/deepseek/Deepseek.php create mode 100644 application/common/model/deepseek/Question.php create mode 100644 application/common/model/deepseek/Stream.php create mode 100644 public/assets/js/backend/deepseek/question.js create mode 100644 public/assets/js/backend/deepseek/stream.js diff --git a/application/admin/controller/deepseek/Question.php b/application/admin/controller/deepseek/Question.php new file mode 100644 index 0000000..3cbc3c6 --- /dev/null +++ b/application/admin/controller/deepseek/Question.php @@ -0,0 +1,71 @@ +model = new \app\admin\model\deepseek\Question; + + } + + + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + + /** + * 查看 + */ + public function index() + { + //当前是否为关联查询 + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->with(['user']) + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + foreach ($list as $row) { + + $row->getRelation('user')->visible(['nickname','mobile','avatar']); + } + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + +} diff --git a/application/admin/controller/deepseek/Stream.php b/application/admin/controller/deepseek/Stream.php new file mode 100644 index 0000000..cfcdda8 --- /dev/null +++ b/application/admin/controller/deepseek/Stream.php @@ -0,0 +1,71 @@ +model = new \app\admin\model\deepseek\Stream; + + } + + + + /** + * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 + * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 + * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 + */ + + + /** + * 查看 + */ + public function index() + { + //当前是否为关联查询 + $this->relationSearch = true; + //设置过滤方法 + $this->request->filter(['strip_tags', 'trim']); + if ($this->request->isAjax()) { + //如果发送的来源是Selectpage,则转发到Selectpage + if ($this->request->request('keyField')) { + return $this->selectpage(); + } + list($where, $sort, $order, $offset, $limit) = $this->buildparams(); + + $list = $this->model + ->with(['question']) + ->where($where) + ->order($sort, $order) + ->paginate($limit); + + foreach ($list as $row) { + + $row->getRelation('question')->visible(['user_id','content']); + } + + $result = array("total" => $list->total(), "rows" => $list->items()); + + return json($result); + } + return $this->view->fetch(); + } + +} diff --git a/application/admin/lang/zh-cn/deepseek/question.php b/application/admin/lang/zh-cn/deepseek/question.php new file mode 100644 index 0000000..35fda66 --- /dev/null +++ b/application/admin/lang/zh-cn/deepseek/question.php @@ -0,0 +1,12 @@ + '提问id', + 'User_id' => '提问用户', + 'Content' => '提问的问题', + 'Createtime' => '创建时间', + 'Endtime' => '答完时间', + 'User.nickname' => '昵称', + 'User.mobile' => '手机号', + 'User.avatar' => '头像' +]; diff --git a/application/admin/lang/zh-cn/deepseek/stream.php b/application/admin/lang/zh-cn/deepseek/stream.php new file mode 100644 index 0000000..3d8c595 --- /dev/null +++ b/application/admin/lang/zh-cn/deepseek/stream.php @@ -0,0 +1,15 @@ + 'id', + 'Question_id' => '提问id', + 'Assistant_id' => '应答id', + 'Object' => '应答对象', + 'Created_time' => '应答时间', + 'Model' => '应答模型', + 'Usage_json' => 'usagejson', + 'Choices_json' => '应答实体json', + 'Createtime' => '创建时间', + 'Question.user_id' => '提问用户', + 'Question.content' => '提问的问题' +]; diff --git a/application/admin/model/deepseek/Question.php b/application/admin/model/deepseek/Question.php new file mode 100644 index 0000000..326182e --- /dev/null +++ b/application/admin/model/deepseek/Question.php @@ -0,0 +1,52 @@ +belongsTo('app\admin\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0); + } +} diff --git a/application/admin/model/deepseek/Stream.php b/application/admin/model/deepseek/Stream.php new file mode 100644 index 0000000..657a5fc --- /dev/null +++ b/application/admin/model/deepseek/Stream.php @@ -0,0 +1,52 @@ +belongsTo('Question', 'question_id', 'id', [], 'LEFT')->setEagerlyType(0); + } +} diff --git a/application/admin/validate/deepseek/Question.php b/application/admin/validate/deepseek/Question.php new file mode 100644 index 0000000..b5bfd6d --- /dev/null +++ b/application/admin/validate/deepseek/Question.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/validate/deepseek/Stream.php b/application/admin/validate/deepseek/Stream.php new file mode 100644 index 0000000..2f0801b --- /dev/null +++ b/application/admin/validate/deepseek/Stream.php @@ -0,0 +1,27 @@ + [], + 'edit' => [], + ]; + +} diff --git a/application/admin/view/deepseek/question/add.html b/application/admin/view/deepseek/question/add.html new file mode 100644 index 0000000..c10bedd --- /dev/null +++ b/application/admin/view/deepseek/question/add.html @@ -0,0 +1,27 @@ +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
diff --git a/application/admin/view/deepseek/question/edit.html b/application/admin/view/deepseek/question/edit.html new file mode 100644 index 0000000..4d8b3b9 --- /dev/null +++ b/application/admin/view/deepseek/question/edit.html @@ -0,0 +1,27 @@ +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
diff --git a/application/admin/view/deepseek/question/index.html b/application/admin/view/deepseek/question/index.html new file mode 100644 index 0000000..e19d226 --- /dev/null +++ b/application/admin/view/deepseek/question/index.html @@ -0,0 +1,29 @@ +
+ {:build_heading()} + +
+
+
+ +
+ +
+
+
diff --git a/application/admin/view/deepseek/stream/add.html b/application/admin/view/deepseek/stream/add.html new file mode 100644 index 0000000..53ca01a --- /dev/null +++ b/application/admin/view/deepseek/stream/add.html @@ -0,0 +1,71 @@ +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ {:__('Key')} + {:__('Value')} +
+
{:__('Append')}
+ +
+ + +
+
+
+ +
+ +
+
+ {:__('Key')} + {:__('Value')} +
+
{:__('Append')}
+ +
+ + +
+
+ +
diff --git a/application/admin/view/deepseek/stream/edit.html b/application/admin/view/deepseek/stream/edit.html new file mode 100644 index 0000000..cfb3385 --- /dev/null +++ b/application/admin/view/deepseek/stream/edit.html @@ -0,0 +1,71 @@ +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ {:__('Key')} + {:__('Value')} +
+
{:__('Append')}
+ +
+ + +
+
+
+ +
+ +
+
+ {:__('Key')} + {:__('Value')} +
+
{:__('Append')}
+ +
+ + +
+
+ +
diff --git a/application/admin/view/deepseek/stream/index.html b/application/admin/view/deepseek/stream/index.html new file mode 100644 index 0000000..606e5ab --- /dev/null +++ b/application/admin/view/deepseek/stream/index.html @@ -0,0 +1,29 @@ +
+ {:build_heading()} + +
+
+
+ +
+ +
+
+
diff --git a/application/api/controller/deepseek/Deepseek.php b/application/api/controller/deepseek/Deepseek.php new file mode 100644 index 0000000..031dc7c --- /dev/null +++ b/application/api/controller/deepseek/Deepseek.php @@ -0,0 +1,239 @@ +model = new Question(); + $this->listmodel = new Stream(); + parent::_initialize(); + + //判断登录用户是否是员工 + + } + + + /** + * @ApiTitle(deepseek提问) + * @ApiSummary(deepseek提问) + * @ApiMethod(POST) + * @ApiParams(name = "messages", type = "string",required=true,description = "提问数组 {'role': 'user', 'content': '中原算力是一个什么样的公司'},{'role': 'assistant', 'content': ''} ") + * @ApiParams(name = "key", type = "string",required=true,description = "单次提问唯一key") + * @ApiParams(name = "session_key", type = "string",required=false,description = "会话唯一key:不传默认取当前日期") + * @ApiReturn({ + + * + *}) + */ + public function question(){ + + $this->setUrlLock("all","deepseek-question"); + + $messages = $this->request->post('messages/s',""); + $messages = htmlspecialchars_decode($messages); + $key = $this->request->post('key/s',""); + $session_key = $this->request->post('session_key/s',""); + //不传默认取当前日期 + if(!$session_key) $session_key = date("Ymd"); +// $messages = "[{'role': 'user', 'content': '中原算力是一个什么样的公司'},{'role': 'assistant', 'content': ''}]"; +// var_dump($messages); + $user_id = 0; + $user = $this->auth->getUser();//登录用户 + if($user)$user_id = $user['id']; +// if(empty($id)){ +// $this->error(__('缺少必要参数')); +// } + + try { + $res = $this->model->question($messages,$user_id,$key,$session_key,false); + } catch (\Exception $e){ +// Log::log($e->getMessage()); + $this->error($e->getMessage(),['errcode'=>$e->getCode()]); + } + $this->success('请求成功,请求结果请轮询查询结果接口', ['detail' => $res]); + } + + + + + /** + * @ApiTitle( 输出结果查询接口-list为空数组为在思考中) + * @ApiSummary(输出结果查询接口) + * @ApiMethod(GET) + * @ApiParams(name = "key", type = "string",required=false,description = "提问时自定义的key或提问id") + * @ApiParams(name = "page", type = "string",required=true,description = "页数") + * @ApiParams(name = "limit", type = "string",required=true,description = "条数") + * @ApiParams(name = "session_key", type = "string",required=false,description = "会话id") + * @ApiReturn({ + * + *}) + */ + public function answer_list() + { + $user_id = 0; + $user = $this->auth->getUser();//登录用户 + if($user)$user_id = $user['id']; + + $params = []; + $my = $this->request->get('my/d', 0); //我的评价 + if($my && $user_id){ + $params['user_id'] = $user_id; + } + $page = $this->request->get('page/d', 1); //页数 + $limit = $this->request->get('limit/d', 10); //条数 + $params["key"] = $this->request->get('key/s', ''); //搜索关键字 + $params["session_key"] = $this->request->get('session_key/s', ''); //搜索关键字 + + + try{ + //当前申请状态 + $res = $this->listmodel::answerList($page, $limit,$params); +// if($user_id =='670153'){ +// file_put_contents("ceshi66.txt",(new AppointmentOrder())->getLastSql()); +// } + }catch (\Exception $e){ + + $this->error($e->getMessage()); + } + $this->success('查询成功', $res); + } + + + + /** + * @ApiTitle( 查询我的所有会话) + * @ApiSummary(查询我的所有会话) + * @ApiMethod(GET) + * @ApiReturn({ + * + *}) + */ + public function seesion_list() + { + $user_id = 0; + $user = $this->auth->getUser();//登录用户 + if($user)$user_id = $user['id']; + +// $params = []; +// $my = $this->request->get('my/d', 0); //我的评价 +// if($my && $user_id){ +// $params['user_id'] = $user_id; +// } +// $page = $this->request->get('page/d', 1); //页数 +// $limit = $this->request->get('limit/d', 1000); //条数 +// $params["key"] = $this->request->get('key/s', ''); //搜索关键字 +// $params["session_key"] = $this->request->get('session_key/s', ''); //搜索关键字 + + + try{ + //当前申请状态 + $res = $this->model->getSeesionlist($user_id); +// if($user_id =='670153'){ +// file_put_contents("ceshi66.txt",(new AppointmentOrder())->getLastSql()); +// } + }catch (\Exception $e){ + + $this->error($e->getMessage()); + } + $this->success('查询成功', $res); + } + + + + + + /** + * @ApiTitle( 提问记录记录) + * @ApiSummary(提问记录) + * @ApiMethod(GET) + * @ApiParams(name = "page", type = "string",required=true,description = "页数") + * @ApiParams(name = "limit", type = "string",required=true,description = "条数") + * @ApiParams(name = "session_key", type = "string",required=false,description = "会话id") + * @ApiReturn({ + * + *}) + */ + public function question_list() + { + $user_id = 0; + $user = $this->auth->getUser();//登录用户 + if($user)$user_id = $user['id']; + + $params = []; +// $my = $this->request->get('my/d', 0); //我的评价 + if($user_id){ + $params['user_id'] = $user_id; + } + $page = $this->request->get('page/d', 1); //页数 + $limit = $this->request->get('limit/d', 10); //条数 + $params["session_key"] = $this->request->get('session_key/s', ''); //搜索关键字 + + + try{ + //当前申请状态 + $res = $this->model::questionList($page, $limit,$params); +// if($user_id =='670153'){ +// file_put_contents("ceshi66.txt",(new AppointmentOrder())->getLastSql()); +// } + }catch (\Exception $e){ + + $this->error($e->getMessage()); + } + $this->success('查询成功', $res); + } + + + + + + /** + * @ApiTitle( 删除会话) + * @ApiSummary(删除会话) + * @ApiMethod(GET) + * @ApiParams(name = "session_key", type = "string",required=false,description = "会话id") + * @ApiReturn({ + * + *}) + */ + public function delete_session() + { $user_id = 0; + $user = $this->auth->getUser();//登录用户 + if($user)$user_id = $user['id']; + + $session_key = $this->request->get('session_key/s', ''); //搜索关键字 + + + try{ + //当前申请状态 + $res = $this->model->session_del($session_key,true); +// if($user_id =='670153'){ +// file_put_contents("ceshi66.txt",(new AppointmentOrder())->getLastSql()); +// } + }catch (\Exception $e){ + + $this->error($e->getMessage()); + } + $this->success('查询成功', $res); + + } + + + +} \ No newline at end of file diff --git a/application/common/controller/Api.php b/application/common/controller/Api.php index f685ca2..dbaa983 100644 --- a/application/common/controller/Api.php +++ b/application/common/controller/Api.php @@ -86,6 +86,33 @@ class Api } } + + protected $needUrlLock = []; + + protected function setUrlLock($url_key="",$url_suffix="",$model=null){ + if(($this->request->isPost() || (!empty($this->needUrlLock) && in_array($this->request->action(),$this->needUrlLock))) && (!empty($this->model) || $model)){ + $user_id = 0; + $user = $this->auth->getUser();//登录用户 + if($user)$user_id = $user['id']; + + $modulename = $this->request->module(); + $controllername = Loader::parseName($this->request->controller()); + $actionname = strtolower($this->request->action()); + $path = $modulename . '/' . str_replace('.', '/', $controllername) . '/' . $actionname; + if(!$model){ + $this->model::$url_lock_key = $url_key ?: $user_id; + $this->model::$url_lock_suffix = $url_suffix ?: $path."lock-suffix"; + $this->model::$url_lock = true; + }else{ + $model::$url_lock_key = $url_key ?: $user_id; + $model::$url_lock_suffix = $url_suffix ?: $path."lock-suffix"; + $model::$url_lock = true; + } + + } + } + + /** * 初始化操作 * @access protected diff --git a/application/common/controller/Backend.php b/application/common/controller/Backend.php index 3963be4..5355aa4 100644 --- a/application/common/controller/Backend.php +++ b/application/common/controller/Backend.php @@ -3,6 +3,7 @@ namespace app\common\controller; use app\admin\library\Auth; +use app\admin\model\Admin; use think\Config; use think\Controller; use think\Hook; @@ -114,6 +115,75 @@ class Backend extends Controller */ use \app\admin\library\traits\Backend; + protected $qSwitch = false; + protected $qFields = []; + + protected function checkAssemblyParameters(){ + if(!$this->qSwitch)return false; + //得到所有get参数 + $get = $this->request->get(); + //得到当前model所有字段 + + + $fields = $this->model->getTableFields(); + +// $commonFields = (new Field())->getCommonFields(); +// var_dump($commonFields); + $fieldLists = $fields; +// foreach ($commonFields as $commonField) { +// if (!in_array($commonField['column_name'], $fields)) { +// $fieldLists[] = $commonField; +// } +// } + $q_fields = []; + + foreach ($get as $kay=>$getField) { + if (in_array($kay, $fieldLists)) { + $q_fields[$kay] = $getField; + } + } + + + //将q_fields塞入模板中 + foreach ($q_fields as $k=>$v) { + //渲染站点配置 + $this->assign('q_'.$k, $v); + } + + foreach ($this->qFields as $k) { + //渲染站点配置 + if(!isset($q_fields[$k]))$this->assign('q_'.$k, ""); + } + + + } + + protected $needUrlLock = []; + + public function setUrlLock($url_key="",$url_suffix="",$model=null){ + if(($this->request->isPost() || (!empty($this->needUrlLock) && in_array($this->request->action(),$this->needUrlLock))) && (!empty($this->model) || $model)){ + $user_id = $this->auth->id ?? 0; +// $user = $this->auth->getUser();//登录用户 +// if($user)$user_id = $user['id']; + + $modulename = $this->request->module(); + $controllername = Loader::parseName($this->request->controller()); + $actionname = strtolower($this->request->action()); + $path = $modulename . '/' . str_replace('.', '/', $controllername) . '/' . $actionname; + if(!$model){ + $this->model::$url_lock_key = $url_key ?: $user_id; + $this->model::$url_lock_suffix = $url_suffix ?: $path."lock-suffix"; + $this->model::$url_lock = true; + }else{ + $model::$url_lock_key = $url_key ?: $user_id; + $model::$url_lock_suffix = $url_suffix ?: $path."lock-suffix"; + $model::$url_lock = true; + } + + } + } + + public function _initialize() { $modulename = $this->request->module(); @@ -228,6 +298,8 @@ class Backend extends Controller $this->assign('auth', $this->auth); //渲染管理员对象 $this->assign('admin', Session::get('admin')); + + $this->checkAssemblyParameters(); } /** @@ -259,7 +331,7 @@ class Backend extends Controller * @param boolean $relationSearch 是否关联查询 * @return array */ - protected function buildparams($searchfields = null, $relationSearch = null) + protected function buildparams($searchfields = null, $relationSearch = null,$excludefields = []) { $searchfields = is_null($searchfields) ? $this->searchFields : $searchfields; $relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch; @@ -281,6 +353,7 @@ class Backend extends Controller $op = (array)json_decode($op, true); $filter = $filter ? $filter : []; $where = []; + $excludearray = []; $alias = []; $bind = []; $name = ''; @@ -314,6 +387,19 @@ class Backend extends Controller continue; } $sym = $op[$k] ?? '='; + //忽略的查询条件出现在忽略数组中 2022年9月6日18:55:17 + if(in_array($k, $excludefields)){ + $excludearray[$k]['value'] = $v; + $excludearray[$k]['op'] = $sym; + + if (stripos($k, ".") === false) { + $excludearray[$k]['alias'] = $aliasName; + } + unset($filter[$k]); + unset($op[$k]); + continue; + } + if (stripos($k, ".") === false) { $k = $aliasName . $k; } @@ -368,8 +454,8 @@ class Backend extends Controller case 'NOT BETWEEN': $arr = array_slice(explode(',', $v), 0, 2); if (stripos($v, ',') === false || !array_filter($arr, function ($v) { - return $v != '' && $v !== false && $v !== null; - })) { + return $v != '' && $v !== false && $v !== null; + })) { continue 2; } //当出现一边为空时改变操作符 @@ -434,7 +520,7 @@ class Backend extends Controller } } }; - return [$where, $sort, $order, $offset, $limit, $page, $alias, $bind]; + return [$where, $sort, $order, $offset, $limit, $page, $alias, $bind,$excludearray]; } /** diff --git a/application/common/model/deepseek/Question.php b/application/common/model/deepseek/Question.php new file mode 100644 index 0000000..1218e06 --- /dev/null +++ b/application/common/model/deepseek/Question.php @@ -0,0 +1,350 @@ +where('reasoning', 1)->order('created_time asc,reasoning desc,id asc')->column("content"); + return implode("", $contents); + } + + public function getAnswerTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['id']) ? $data['id'] : ''); + $contents = Stream::where('question_id', $value)->where('reasoning', 0)->order('created_time asc,reasoning desc,id asc')->column("content"); + return implode("", $contents); + } + + + + + public function getEndtimeTextAttr($value, $data) + { + $value = $value ? $value : (isset($data['endtime']) ? $data['endtime'] : ''); + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; + } + + protected function setEndtimeAttr($value) + { + return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); + } + + + public function user() + { + return $this->belongsTo('app\common\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + + public function question($messages, $user_id, $key,$session_key,$trans = false) + { + if (!$messages) throw new \Exception('缺少必要参数1'); + if (!$key) throw new \Exception('缺少必要参数2'); + + $self = self::where('key', $key)->find(); + if($self) throw new \Exception('key已被消耗'); + //解析json成属数组 + $messages = json_decode($messages, true); + if (!$messages) throw new \Exception('json解析失败'); + //messages的json格式为{"role": "user", "content": "中原算力是一个什么样的公司"},{"role": "assistant", "content": ""} + //messages得到最后一个role名为assistant的对象并向上一个role为user的对象 + + //得到官网请求地址 + $deepseek_web_url = config("site.deepseek_web_url"); + //得到官网请求密钥 + $deepseek_key = "Bearer " . config("site.deepseek_key"); + //得到本地部署的请求地址 + $deepseek_local_url = config("site.deepseek_local_url"); + //得到切换开关 + $deepseek_switch = config("site.deepseek_switch"); + $reasoning =true; + $last = end($messages); + if ($last['role'] == 'assistant') { + $last = prev($messages); + if ($last['role'] != 'user') { + throw new \Exception('messages参数错误!'); + } + } + + //访问deepseek脚本存在超时可能会超时,所以需要设置超时时间为10分钟 + set_time_limit(6000); + + //组转json请求参数准备请求deepseekapi :{ + //"model": "qwen", + //"messages": [ + //{"role": "user", "content": "中原算力是一个什么样的公司"},{"role": "assistant", "content": ""} + //], + //"stream": true, + //"temperature":0.6 + //} + $data = [ + 'model' => $deepseek_switch ? 'qwen':'deepseek-reasoner', + 'messages' => $messages, + 'stream' => true, + 'temperature' => 0.6, + ]; + +// var_dump($data);die; + + + + //判断逻辑 + if ($trans) { + self::beginTrans(); + } + $res = true; + try { + + //保存本次请求model + $datas = [ + 'user_id' => $user_id, + 'content' => $last["content"], + "key"=>$key, + "session_key" => $session_key, + ]; + //保存数据库并得自增id + $self = self::create($datas); +// var_dump($self);die; + $question_id = $self->id; + + + // 判断逻辑 + $url = $deepseek_switch ? $deepseek_local_url : $deepseek_web_url; + $headers = $deepseek_switch ? [] : ['Authorization:' . $deepseek_key]; + + + // 发送curl post json格式的 stream式请求 + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); + curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(['Content-Type: application/json'], $headers)); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, false); + curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $data) use (&$response,$question_id,$deepseek_switch,&$reasoning,$session_key) { +// var_dump($data); + + if (!$data) { + return 0; // 结束流式请求 + } + +// var_dump($data); + $data = htmlspecialchars_decode($data); + // 处理每一部分数据 +// $lines = explode("\n", trim($data)); + $lines = explode("\n",$data); + foreach ($lines as $line) { + if (strpos($line, "data: ") === 0) { + $json = substr($line, 6); + $chunk = json_decode($json, true); + + + if (isset($chunk['choices'][0]['delta']['reasoning_content'])) { + $chunk['choices'][0]['delta']['reasoning_content'] = htmlspecialchars_decode($chunk['choices'][0]['delta']['reasoning_content']); + + // 将数据写入数据库 + // 假设有一个方法 saveStreamData 用于保存数据 + $this->saveStreamData($chunk['choices'][0]['delta']['reasoning_content'],$chunk,$question_id,true,$session_key); + } + + if (isset($chunk['choices'][0]['delta']['content'])) { + + $chunk['choices'][0]['delta']['content'] = htmlspecialchars_decode($chunk['choices'][0]['delta']['content']); + + if($deepseek_switch){ + //本地部署为结尾 + if(strpos($chunk['choices'][0]['delta']['content'],'')!==false){ + $reasoning = false; + }else{ + $this->saveStreamData($chunk['choices'][0]['delta']['content'],$chunk,$question_id,$reasoning,$session_key); + } + + }else{ + //官方调用 + + + // 将数据写入数据库 + // 假设有一个方法 saveStreamData 用于保存数据 + $this->saveStreamData($chunk['choices'][0]['delta']['content'],$chunk,$question_id,false,$session_key); + + + } + + } + } + } + + // 处理每一部分数据 +// $json = $data; +// $chunk = json_decode($json, true); +// if (isset($chunk['choices'][0]['delta']['content'])) { +// // 将数据写入数据库 +// // 假设有一个方法 saveStreamData 用于保存数据 +// $this->saveStreamData($chunk['choices'][0]['delta']['content'],$chunk,$question_id); +// } + + return strlen($data); + }); + + curl_exec($ch); + $curl_error = curl_error($ch); + curl_close($ch); + + if ($curl_error) { + throw new \Exception('Curl error: ' . $curl_error); + } + + + + if ($trans) { + self::commitTrans(); + } + } catch (\Exception $e) { + if ($trans) { + self::rollbackTrans(); + } + throw new \Exception($e->getMessage() . $e->getFile() . $e->getLine()); + } + return $res; + } + + // 假设有一个方法 saveStreamData 用于保存数据 + protected function saveStreamData($content,$chunk,$question_id,$reasoning = false,$session_key) + { + // 实现保存数据到数据库的逻辑 + $data = [ + 'question_id' => $question_id, + 'assistant_id' => $chunk["id"], + 'object' => $chunk["object"], + "created_time" => $chunk["created"], + "model" => $chunk["model"], + "usage_json" => isset($chunk["usage"])? htmlspecialchars_decode(json_encode( $chunk["usage"])) : '', + "choices_json" => isset($chunk["choices"])? htmlspecialchars_decode(json_encode($chunk["choices"])): '', + "content" => $content, + "reasoning" => $reasoning, + "session_key" =>$session_key + ]; + Stream::create($data); + + } + + + public function getSeesionlist($user_id=0){ + //用当前模型查询问题qusetion列表,返回每个会话session_key下的最后一个问题,组成列表返回 + $list = self::where("user_id",$user_id)->group("session_key")->order("createtime desc")->select(); + return $list; + } + + + + /**得到基础条件 + * @param $status + * @param null $model + * @param string $alisa + */ + public static function getBaseWhere($whereData = [], $model = null, $alisa = '',$with = false) + { + + if (!$model) { + $model = new static; + if ($alisa&&!$with) $model = $model->alias($alisa); + } + if ($alisa) $alisa = $alisa . '.'; + $tableFields = (new static)->getTableFields(); + foreach ($tableFields as $fields) + { +// if(in_array($fields, ['key']))continue; +// if (isset($whereData[$fields]) && $whereData[$fields]) $model = $model->where("{$alisa}{$fields}", '=', $whereData[$fields]); + + if (isset($whereData[$fields]) && $whereData[$fields]){ + if(is_array($whereData[$fields])){ + $model = $model->where("{$alisa}{$fields}", $whereData[$fields][0], $whereData[$fields][1]); + }else{ + $model = $model->where("{$alisa}{$fields}", '=', $whereData[$fields]); + } + + } + + + } + + if (isset($whereData['time'])&&$whereData['time']){ + $model = $model->time(["{$alisa}createtime",$whereData['time']]); + } + + + + + return $model; + } + + + public static function questionList($page, $limit,$params=[]){ + $with_field = [ + 'base'=>['*'], + ]; +// $alisa = (new self)->getWithAlisaName(); + $sort = "id asc"; + $serch_where = []; + $serch_where = array_merge($serch_where,$params); + return (new self)->getBaseList($serch_where, $page, $limit,$sort,$with_field); + } + + + + public function session_del($session_key,$trans=false){ +//判断逻辑 + if($trans){ + self::beginTrans(); + } + $res = true; + try{ + //根据session_key删除问题 + $question_list = self::where("session_key",$session_key)->delete(); + //根据session_key删除stream + $stream_list = Stream::where("session_key",$session_key)->delete(); + + if($trans){ + self::commitTrans(); + } + }catch (\Exception $e){ + if($trans){ + self::rollbackTrans(); + } + throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); + } + return $res; + } + +} diff --git a/application/common/model/deepseek/Stream.php b/application/common/model/deepseek/Stream.php new file mode 100644 index 0000000..db46894 --- /dev/null +++ b/application/common/model/deepseek/Stream.php @@ -0,0 +1,133 @@ +belongsTo('Question', 'question_id', 'id', [], 'LEFT')->setEagerlyType(0); + } + + + public function getUsageJsonAttr($value, $data) + { + return $value === '' ? [] : json_decode($value, true); + } + + + public function getChoicesJsonAttr($value, $data) + { + return $value === '' ? [] : json_decode($value, true); + } + + + + + + /**得到基础条件 + * @param $status + * @param null $model + * @param string $alisa + */ + public static function getBaseWhere($whereData = [], $model = null, $alisa = '',$with = false) + { + + if (!$model) { + $model = new static; + if ($alisa&&!$with) $model = $model->alias($alisa); + } + if ($alisa) $alisa = $alisa . '.'; + $tableFields = (new static)->getTableFields(); + foreach ($tableFields as $fields) + { + if(in_array($fields, ['key']))continue; +// if (isset($whereData[$fields]) && $whereData[$fields]) $model = $model->where("{$alisa}{$fields}", '=', $whereData[$fields]); + + if (isset($whereData[$fields]) && $whereData[$fields]){ + if(is_array($whereData[$fields])){ + $model = $model->where("{$alisa}{$fields}", $whereData[$fields][0], $whereData[$fields][1]); + }else{ + $model = $model->where("{$alisa}{$fields}", '=', $whereData[$fields]); + } + + } + + + } + + + if (isset($whereData['key'])&&$whereData['key']){ + $question = Question::where("id|key", '=', $whereData['key'])->find(); + if(!$question){ + $model = $model->where("{$alisa}question_id", '=', -1); + }else{ + $model = $model->where("{$alisa}question_id", '=', $question["id"]); + } + + } + if (isset($whereData['time'])&&$whereData['time']){ + $model = $model->time(["{$alisa}created_time",$whereData['time']]); + } + + + + + return $model; + } + + + public static function answerList($page, $limit,$params=[]){ + $with_field = [ + 'base'=>['*'], + ]; +// $alisa = (new self)->getWithAlisaName(); + $sort = "created_time asc,reasoning desc,id asc"; + $serch_where = []; + $serch_where = array_merge($serch_where,$params); + return (new self)->getBaseList($serch_where, $page, $limit,$sort,$with_field); + } + +} diff --git a/application/config.php b/application/config.php index 86da54d..4ac7cd6 100644 --- a/application/config.php +++ b/application/config.php @@ -18,7 +18,7 @@ return [ // 应用命名空间 'app_namespace' => 'app', // 应用调试模式 - 'app_debug' => Env::get('app.debug', false), + 'app_debug' => Env::get('app.debug', true), // 应用Trace 'app_trace' => Env::get('app.trace', false), // 应用模式状态 @@ -159,7 +159,7 @@ return [ // 错误显示信息,非调试模式有效 'error_message' => '你所浏览的页面暂时无法访问', // 显示错误信息 - 'show_error_msg' => false, + 'show_error_msg' => true, // 异常处理handle类 留空使用 \think\exception\Handle 'exception_handle' => '', // +---------------------------------------------------------------------- @@ -306,4 +306,13 @@ return [ //API接口地址 'api_url' => 'https://api.fastadmin.net', ], + //增加 redis 配置 + 'redis' => [ + 'host' => '127.0.0.1', // redis 主机地址 + 'password' => '', // redis 密码 + 'port' => 6379, // redis 端口 + 'select' => 3, // redis 数据库 + 'timeout' => 0, // redis 超时时间 + 'persistent' => false, // redis 持续性,连接复用 + ] ]; diff --git a/public/assets/js/backend/deepseek/question.js b/public/assets/js/backend/deepseek/question.js new file mode 100644 index 0000000..d0612ee --- /dev/null +++ b/public/assets/js/backend/deepseek/question.js @@ -0,0 +1,57 @@ +define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { + + var Controller = { + index: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + index_url: 'deepseek/question/index' + location.search, + add_url: 'deepseek/question/add', + edit_url: 'deepseek/question/edit', + del_url: 'deepseek/question/del', + multi_url: 'deepseek/question/multi', + import_url: 'deepseek/question/import', + table: 'deepseek_question', + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: $.fn.bootstrapTable.defaults.extend.index_url, + pk: 'id', + sortName: 'id', + columns: [ + [ + {checkbox: true}, + {field: 'id', title: __('Id')}, + {field: 'user_id', title: __('User_id')}, + {field: 'content', title: __('Content'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content}, + {field: 'createtime', title: __('Createtime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime}, + {field: 'endtime', title: __('Endtime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime}, + {field: 'user.nickname', title: __('User.nickname'), operate: 'LIKE'}, + {field: 'user.mobile', title: __('User.mobile'), operate: 'LIKE'}, + {field: 'user.avatar', title: __('User.avatar'), operate: 'LIKE', events: Table.api.events.image, formatter: Table.api.formatter.image}, + {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate} + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, + add: function () { + Controller.api.bindevent(); + }, + edit: function () { + Controller.api.bindevent(); + }, + api: { + bindevent: function () { + Form.api.bindevent($("form[role=form]")); + } + } + }; + return Controller; +}); diff --git a/public/assets/js/backend/deepseek/stream.js b/public/assets/js/backend/deepseek/stream.js new file mode 100644 index 0000000..ccafd70 --- /dev/null +++ b/public/assets/js/backend/deepseek/stream.js @@ -0,0 +1,58 @@ +define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) { + + var Controller = { + index: function () { + // 初始化表格参数配置 + Table.api.init({ + extend: { + index_url: 'deepseek/stream/index' + location.search, + add_url: 'deepseek/stream/add', + edit_url: 'deepseek/stream/edit', + del_url: 'deepseek/stream/del', + multi_url: 'deepseek/stream/multi', + import_url: 'deepseek/stream/import', + table: 'deepseek_stream', + } + }); + + var table = $("#table"); + + // 初始化表格 + table.bootstrapTable({ + url: $.fn.bootstrapTable.defaults.extend.index_url, + pk: 'id', + sortName: 'id', + columns: [ + [ + {checkbox: true}, + {field: 'id', title: __('Id')}, + {field: 'question_id', title: __('Question_id')}, + {field: 'assistant_id', title: __('Assistant_id'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content}, + {field: 'object', title: __('Object'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content}, + {field: 'created_time', title: __('Created_time'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime}, + {field: 'model', title: __('Model'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content}, + {field: 'createtime', title: __('Createtime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime}, + {field: 'question.user_id', title: __('Question.user_id')}, + {field: 'question.content', title: __('Question.content'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content}, + {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate} + ] + ] + }); + + // 为表格绑定事件 + Table.api.bindevent(table); + }, + add: function () { + Controller.api.bindevent(); + }, + edit: function () { + Controller.api.bindevent(); + }, + api: { + bindevent: function () { + Form.api.bindevent($("form[role=form]")); + } + } + }; + return Controller; +});