提交代码

This commit is contained in:
焦钰锟 2025-02-28 19:06:45 +08:00
parent abdbe0ff2f
commit 14d114771b
22 changed files with 1546 additions and 6 deletions

View File

@ -0,0 +1,71 @@
<?php
namespace app\admin\controller\deepseek;
use app\common\controller\Backend;
/**
* deepseek提问
*
* @icon fa fa-circle-o
*/
class Question extends Backend
{
/**
* Question模型对象
* @var \app\admin\model\deepseek\Question
*/
protected $model = null;
public function _initialize()
{
parent::_initialize();
$this->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();
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace app\admin\controller\deepseek;
use app\common\controller\Backend;
/**
* deepseek流式应答采集数据
*
* @icon fa fa-circle-o
*/
class Stream extends Backend
{
/**
* Stream模型对象
* @var \app\admin\model\deepseek\Stream
*/
protected $model = null;
public function _initialize()
{
parent::_initialize();
$this->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();
}
}

View File

@ -0,0 +1,12 @@
<?php
return [
'Id' => '提问id',
'User_id' => '提问用户',
'Content' => '提问的问题',
'Createtime' => '创建时间',
'Endtime' => '答完时间',
'User.nickname' => '昵称',
'User.mobile' => '手机号',
'User.avatar' => '头像'
];

View File

@ -0,0 +1,15 @@
<?php
return [
'Id' => 'id',
'Question_id' => '提问id',
'Assistant_id' => '应答id',
'Object' => '应答对象',
'Created_time' => '应答时间',
'Model' => '应答模型',
'Usage_json' => 'usagejson',
'Choices_json' => '应答实体json',
'Createtime' => '创建时间',
'Question.user_id' => '提问用户',
'Question.content' => '提问的问题'
];

View File

@ -0,0 +1,52 @@
<?php
namespace app\admin\model\deepseek;
use think\Model;
class Question extends Model
{
// 表名
protected $name = 'deepseek_question';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'integer';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = false;
protected $deleteTime = false;
// 追加属性
protected $append = [
'endtime_text'
];
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\admin\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace app\admin\model\deepseek;
use think\Model;
class Stream extends Model
{
// 表名
protected $name = 'deepseek_stream';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'integer';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = false;
protected $deleteTime = false;
// 追加属性
protected $append = [
'created_time_text'
];
public function getCreatedTimeTextAttr($value, $data)
{
$value = $value ? $value : (isset($data['created_time']) ? $data['created_time'] : '');
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
}
protected function setCreatedTimeAttr($value)
{
return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
}
public function question()
{
return $this->belongsTo('Question', 'question_id', 'id', [], 'LEFT')->setEagerlyType(0);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace app\admin\validate\deepseek;
use think\Validate;
class Question extends Validate
{
/**
* 验证规则
*/
protected $rule = [
];
/**
* 提示消息
*/
protected $message = [
];
/**
* 验证场景
*/
protected $scene = [
'add' => [],
'edit' => [],
];
}

View File

@ -0,0 +1,27 @@
<?php
namespace app\admin\validate\deepseek;
use think\Validate;
class Stream extends Validate
{
/**
* 验证规则
*/
protected $rule = [
];
/**
* 提示消息
*/
protected $message = [
];
/**
* 验证场景
*/
protected $scene = [
'add' => [],
'edit' => [],
];
}

View File

@ -0,0 +1,27 @@
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('User_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-user_id" data-rule="required" data-source="user/user/index" data-field="nickname" class="form-control selectpage" name="row[user_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-content" class="form-control" name="row[content]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Endtime')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-endtime" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[endtime]" type="text" value="{:date('Y-m-d H:i:s')}">
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,27 @@
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('User_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-user_id" data-rule="required" data-source="user/user/index" data-field="nickname" class="form-control selectpage" name="row[user_id]" type="text" value="{$row.user_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Content')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-content" class="form-control" name="row[content]" type="text" value="{$row.content|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Endtime')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-endtime" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[endtime]" type="text" value="{:$row.endtime?datetime($row.endtime):''}">
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,29 @@
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
<a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('deepseek/question/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('deepseek/question/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('deepseek/question/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
</div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('deepseek/question/edit')}"
data-operate-del="{:$auth->check('deepseek/question/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,71 @@
<form id="add-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Question_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-question_id" data-rule="required" data-source="deepseek/question/index" class="form-control selectpage" name="row[question_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Assistant_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-assistant_id" data-rule="required" data-source="assistant/index" class="form-control selectpage" name="row[assistant_id]" type="text" value="">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Object')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-object" class="form-control" name="row[object]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Created_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-created_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[created_time]" type="text" value="{:date('Y-m-d H:i:s')}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Model')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-model" class="form-control" name="row[model]" type="text">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Usage_json')}:</label>
<div class="col-xs-12 col-sm-8">
<dl class="fieldlist" data-name="row[usage_json]">
<dd>
<ins>{:__('Key')}</ins>
<ins>{:__('Value')}</ins>
</dd>
<dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
<textarea name="row[usage_json]" class="form-control hide" cols="30" rows="5"></textarea>
</dl>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Choices_json')}:</label>
<div class="col-xs-12 col-sm-8">
<dl class="fieldlist" data-name="row[choices_json]">
<dd>
<ins>{:__('Key')}</ins>
<ins>{:__('Value')}</ins>
</dd>
<dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
<textarea name="row[choices_json]" class="form-control hide" cols="30" rows="5"></textarea>
</dl>
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,71 @@
<form id="edit-form" class="form-horizontal" role="form" data-toggle="validator" method="POST" action="">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Question_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-question_id" data-rule="required" data-source="deepseek/question/index" class="form-control selectpage" name="row[question_id]" type="text" value="{$row.question_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Assistant_id')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-assistant_id" data-rule="required" data-source="assistant/index" class="form-control selectpage" name="row[assistant_id]" type="text" value="{$row.assistant_id|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Object')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-object" class="form-control" name="row[object]" type="text" value="{$row.object|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Created_time')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-created_time" class="form-control datetimepicker" data-date-format="YYYY-MM-DD HH:mm:ss" data-use-current="true" name="row[created_time]" type="text" value="{:$row.created_time?datetime($row.created_time):''}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Model')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-model" class="form-control" name="row[model]" type="text" value="{$row.model|htmlentities}">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Usage_json')}:</label>
<div class="col-xs-12 col-sm-8">
<dl class="fieldlist" data-name="row[usage_json]">
<dd>
<ins>{:__('Key')}</ins>
<ins>{:__('Value')}</ins>
</dd>
<dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
<textarea name="row[usage_json]" class="form-control hide" cols="30" rows="5">{$row.usage_json|htmlentities}</textarea>
</dl>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Choices_json')}:</label>
<div class="col-xs-12 col-sm-8">
<dl class="fieldlist" data-name="row[choices_json]">
<dd>
<ins>{:__('Key')}</ins>
<ins>{:__('Value')}</ins>
</dd>
<dd><a href="javascript:;" class="btn btn-sm btn-success btn-append"><i class="fa fa-plus"></i> {:__('Append')}</a></dd>
<textarea name="row[choices_json]" class="form-control hide" cols="30" rows="5">{$row.choices_json|htmlentities}</textarea>
</dl>
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled">{:__('OK')}</button>
</div>
</div>
</form>

View File

@ -0,0 +1,29 @@
<div class="panel panel-default panel-intro">
{:build_heading()}
<div class="panel-body">
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade active in" id="one">
<div class="widget-body no-padding">
<div id="toolbar" class="toolbar">
<a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<a href="javascript:;" class="btn btn-success btn-add {:$auth->check('deepseek/stream/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
<a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('deepseek/stream/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
<a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('deepseek/stream/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
</div>
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
data-operate-edit="{:$auth->check('deepseek/stream/edit')}"
data-operate-del="{:$auth->check('deepseek/stream/del')}"
width="100%">
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,239 @@
<?php
namespace app\api\controller\deepseek;
use app\common\model\deepseek\Question;
use app\common\model\deepseek\Stream;
use app\common\controller\Api;
class Deepseek extends Api{
protected $noNeedLogin = ['*'];
protected $noNeedRight = ['*'];
protected $model = null;
protected $listmodel = null;
/**
* 初始化操作
* @access protected
*/
protected function _initialize()
{
$this->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': '</think>'} ")
* @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': '</think>'}]";
// 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);
}
}

View File

@ -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 * @access protected

View File

@ -3,6 +3,7 @@
namespace app\common\controller; namespace app\common\controller;
use app\admin\library\Auth; use app\admin\library\Auth;
use app\admin\model\Admin;
use think\Config; use think\Config;
use think\Controller; use think\Controller;
use think\Hook; use think\Hook;
@ -114,6 +115,75 @@ class Backend extends Controller
*/ */
use \app\admin\library\traits\Backend; 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() public function _initialize()
{ {
$modulename = $this->request->module(); $modulename = $this->request->module();
@ -228,6 +298,8 @@ class Backend extends Controller
$this->assign('auth', $this->auth); $this->assign('auth', $this->auth);
//渲染管理员对象 //渲染管理员对象
$this->assign('admin', Session::get('admin')); $this->assign('admin', Session::get('admin'));
$this->checkAssemblyParameters();
} }
/** /**
@ -259,7 +331,7 @@ class Backend extends Controller
* @param boolean $relationSearch 是否关联查询 * @param boolean $relationSearch 是否关联查询
* @return array * @return array
*/ */
protected function buildparams($searchfields = null, $relationSearch = null) protected function buildparams($searchfields = null, $relationSearch = null,$excludefields = [])
{ {
$searchfields = is_null($searchfields) ? $this->searchFields : $searchfields; $searchfields = is_null($searchfields) ? $this->searchFields : $searchfields;
$relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch; $relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch;
@ -281,6 +353,7 @@ class Backend extends Controller
$op = (array)json_decode($op, true); $op = (array)json_decode($op, true);
$filter = $filter ? $filter : []; $filter = $filter ? $filter : [];
$where = []; $where = [];
$excludearray = [];
$alias = []; $alias = [];
$bind = []; $bind = [];
$name = ''; $name = '';
@ -314,6 +387,19 @@ class Backend extends Controller
continue; continue;
} }
$sym = $op[$k] ?? '='; $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) { if (stripos($k, ".") === false) {
$k = $aliasName . $k; $k = $aliasName . $k;
} }
@ -368,8 +454,8 @@ class Backend extends Controller
case 'NOT BETWEEN': case 'NOT BETWEEN':
$arr = array_slice(explode(',', $v), 0, 2); $arr = array_slice(explode(',', $v), 0, 2);
if (stripos($v, ',') === false || !array_filter($arr, function ($v) { if (stripos($v, ',') === false || !array_filter($arr, function ($v) {
return $v != '' && $v !== false && $v !== null; return $v != '' && $v !== false && $v !== null;
})) { })) {
continue 2; 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];
} }
/** /**

View File

@ -0,0 +1,350 @@
<?php
namespace app\common\model\deepseek;
use app\common\model\BaseModel;
use think\Model;
class Question extends BaseModel
{
// 表名
protected $name = 'deepseek_question';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'integer';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = false;
protected $deleteTime = false;
// 追加属性
protected $append = [
'endtime_text',
'reasoning_text',
'answer_text'
];
public function getReasoningTextAttr($value, $data)
{
$value = $value ? $value : (isset($data['id']) ? $data['id'] : '');
//Stream model拼接所有question_id=$value =list中的所有content字段成完整字符串文本
$contents = Stream::where('question_id', $value)->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": "</think>"}
//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": "</think>"}
//],
//"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){
//本地部署</think>为结尾
if(strpos($chunk['choices'][0]['delta']['content'],'</think>')!==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;
}
}

View File

@ -0,0 +1,133 @@
<?php
namespace app\common\model\deepseek;
use app\common\model\BaseModel;
use think\Model;
class Stream extends BaseModel
{
// 表名
protected $name = 'deepseek_stream';
// 自动写入时间戳字段
protected $autoWriteTimestamp = 'integer';
// 定义时间戳字段名
protected $createTime = 'createtime';
protected $updateTime = false;
protected $deleteTime = false;
// 追加属性
protected $append = [
'created_time_text'
];
public function getCreatedTimeTextAttr($value, $data)
{
$value = $value ? $value : (isset($data['created_time']) ? $data['created_time'] : '');
return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
}
protected function setCreatedTimeAttr($value)
{
return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value);
}
public function question()
{
return $this->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);
}
}

View File

@ -18,7 +18,7 @@ return [
// 应用命名空间 // 应用命名空间
'app_namespace' => 'app', 'app_namespace' => 'app',
// 应用调试模式 // 应用调试模式
'app_debug' => Env::get('app.debug', false), 'app_debug' => Env::get('app.debug', true),
// 应用Trace // 应用Trace
'app_trace' => Env::get('app.trace', false), 'app_trace' => Env::get('app.trace', false),
// 应用模式状态 // 应用模式状态
@ -159,7 +159,7 @@ return [
// 错误显示信息,非调试模式有效 // 错误显示信息,非调试模式有效
'error_message' => '你所浏览的页面暂时无法访问', 'error_message' => '你所浏览的页面暂时无法访问',
// 显示错误信息 // 显示错误信息
'show_error_msg' => false, 'show_error_msg' => true,
// 异常处理handle类 留空使用 \think\exception\Handle // 异常处理handle类 留空使用 \think\exception\Handle
'exception_handle' => '', 'exception_handle' => '',
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
@ -306,4 +306,13 @@ return [
//API接口地址 //API接口地址
'api_url' => 'https://api.fastadmin.net', '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 持续性,连接复用
]
]; ];

View File

@ -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;
});

View File

@ -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;
});