Merge branch 'develop' of https://gitee.com/Tyssen/yi-shi into develop

This commit is contained in:
柳清爽
2025-04-03 17:52:46 +08:00
5 changed files with 475 additions and 62 deletions

View File

@@ -11,6 +11,14 @@ Route::group('v1/cozeai', function () {
// 会话管理
Route::group('conversation', function () {
Route::get('list', 'cozeai/ConversationController/list');
Route::post('create', 'cozeai/ConversationController/create');
Route::get('create', 'cozeai/ConversationController/create');
Route::post('createChat', 'cozeai/ConversationController/createChat');
Route::get('chatRetrieve', 'cozeai/ConversationController/chatRetrieve');
Route::get('chatMessage','cozeai/ConversationController/chatMessage');
});
});
// 消息管理
Route::group('message', function () {
Route::get('list', 'cozeai/MessageController/getMessages');
});
})->middleware(['jwt']);

View File

@@ -25,50 +25,86 @@ class BaseController extends Controller
// 设置请求头
$this->headers = [
'Authorization:Bearer '. $this->accessToken,
'Content-Type:application/json'
'Authorization: Bearer ' . $this->accessToken,
'Content-Type: application/json'
];
}
/**
* 发送GET请求
* @param string $url 请求地址
* @param array $params 请求参数
* @return array
/**
* CURL请求
*
* @param $url 请求url地址
* @param $method 请求方法 get post
* @param null $postfields post数据数组
* @param array $headers 请求header信息
* @param bool|false $debug 调试开启 默认false
* @return mixed
*/
protected function get($url, $params = [])
protected function httpRequest($url, $method = "GET", $postfields = null, $headers = array(), $timeout = 30, $debug = false)
{
try {
$client = new \GuzzleHttp\Client();
$response = $client->get($this->apiUrl . $url, [
'headers' => $this->headers,
'query' => $params
]);
return json_decode($response->getBody()->getContents(), true);
} catch (\Exception $e) {
return ['error' => $e->getMessage()];
$method = strtoupper($method);
$ci = curl_init();
/* Curl settings */
// curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); // 使用哪个版本
curl_setopt($ci, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 60); /* 在发起连接前等待的时间如果设置为0则无限等待 */
// curl_setopt($ci, CURLOPT_TIMEOUT, 7); /* 设置cURL允许执行的最长秒数 */
curl_setopt($ci, CURLOPT_TIMEOUT, $timeout); /* 设置cURL允许执行的最长秒数 */
curl_setopt($ci, CURLOPT_RETURNTRANSFER, true);
switch ($method) {
case "POST":
curl_setopt($ci, CURLOPT_POST, true);
if (!empty($postfields)) {
if (is_string($postfields) && preg_match('/^([\w\-]+=[\w\-]+(&[\w\-]+=[\w\-]+)*)$/', $postfields)) {
parse_str($postfields, $output);
$postfields = $output;
}
if (is_array($postfields)) {
$tmpdatastr = http_build_query($postfields);
} else {
$tmpdatastr = $postfields;
}
curl_setopt($ci, CURLOPT_POSTFIELDS, $tmpdatastr);
}
break;
default:
curl_setopt($ci, CURLOPT_CUSTOMREQUEST, $method); /* //设置请求方式 */
break;
}
$ssl = preg_match('/^https:\/\//i', $url) ? TRUE : FALSE;
curl_setopt($ci, CURLOPT_URL, $url);
if ($ssl) {
curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在
// curl_setopt($ci, CURLOPT_SSLVERSION, 4); //因为之前的POODLE 病毒爆发许多网站禁用了sslv3nginx默认是禁用的ssl_protocols 默认值为TLSv1 TLSv1.1 TLSv1.2;最新使用sslv4
}
//curl_setopt($ci, CURLOPT_HEADER, true); /*启用时会将头文件的信息作为数据流输出*/
if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
curl_setopt($ci, CURLOPT_FOLLOWLOCATION, 1);
}
curl_setopt($ci, CURLOPT_MAXREDIRS, 2);/*指定最多的HTTP重定向的数量这个选项是和CURLOPT_FOLLOWLOCATION一起使用的*/
curl_setopt($ci, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ci, CURLINFO_HEADER_OUT, true);
/*curl_setopt($ci, CURLOPT_COOKIE, $Cookiestr); * *COOKIE带过去** */
$response = curl_exec($ci);
$requestinfo = curl_getinfo($ci);
$http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
if ($debug) {
echo "=====post data======\r\n";
var_dump($postfields);
echo "=====info===== \r\n";
print_r($requestinfo);
echo "=====response=====\r\n";
print_r($response);
}
curl_close($ci);
return $response;
//return array($http_code, $response,$requestinfo);
}
/**
* 发送POST请求
* @param string $url 请求地址
* @param array $data 请求数据
* @return array
*/
protected function post($url, $data = [])
{
try {
$client = new \GuzzleHttp\Client();
$response = $client->post($this->apiUrl . $url, [
'headers' => $this->headers,
'json' => $data
]);
return json_decode($response->getBody()->getContents(), true);
} catch (\Exception $e) {
return ['error' => $e->getMessage()];
}
}
}

View File

@@ -3,12 +3,15 @@
namespace app\cozeai\controller;
use app\cozeai\model\Conversation as ConversationModel;
use app\cozeai\model\Message as MessageModel;
use think\facade\Env;
/**
* Coze AI 对话控制器
*/
class ConversationController extends BaseController
{
/**
* 保存对话数据到数据库
* @param array $conversation 对话数据
@@ -22,20 +25,26 @@ class ConversationController extends BaseController
// 检查是否已存在
$exists = ConversationModel::where('conversation_id', $conversation['id'])->find();
$meta_data = $conversation['meta_data'] ?? [];
if (!$exists) {
// 不存在则插入
return ConversationModel::create([
$data = [
'conversation_id' => $conversation['id'],
'bot_id' => $bot_id,
'created_at' => $conversation['created_at'],
'meta_data' => json_encode($meta_data),
'meta_data' => $meta_data,
'create_time' => time(),
'update_time' => time()
]);
];
if(isset($meta_data['uid']) && !empty($meta_data['uid'])){
$data['userId'] = $meta_data['uid'];
}
if(isset($meta_data['companyId']) && !empty($meta_data['companyId'])){
$data['companyId'] = $meta_data['companyId'];
}
return ConversationModel::create($data);
} else {
// 存在则更新
return $exists->save([
@@ -87,12 +96,12 @@ class ConversationController extends BaseController
}
/**
* 创建
* 创建
*/
public function create()
public function create($is_internal = false)
{
try {
$bot_id = input('bot_id','');
$bot_id = Env::get('ai.bot_id');
$userInfo = request()->userInfo;
$uid = $userInfo['id'];
$companyId = $userInfo['companyId'];
@@ -103,43 +112,262 @@ class ConversationController extends BaseController
// 构建元数据和消息
$meta_data = [
'uid' => '1111',
'companyId' => '2222',
'uid' => strval($uid),
'companyId' => strval($companyId),
];
$messages[] = [
'role' => 'assistant',
'content' => '欢迎使用美业AI助手我可以帮您管理客户关系、自动回复消息、创建朋友圈内容自动点赞开发客户。请问有什么可以帮你的',
'content' => Env::get('ai.content'),
'type' => 'answer',
'content_type' => 'text',
];
$params = [
'bot_id' => $bot_id,
'meta_data' => json_encode($meta_data),
'messages' => json_encode($messages),
'bot_id' => strval($bot_id),
'meta_data' => $meta_data,
'messages' => $messages,
];
$result = requestCurl($this->apiUrl . '/v1/conversation/create', $params, 'POST', $this->headers);
$url = $this->apiUrl . '/v1/conversation/create';
$result = $this->httpRequest($url, 'POST', json_encode($params,256), $this->headers);
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
}
// 获取返回的对话数据并保存
$conversation = $result['data'] ?? [];
if (!empty($conversation)) {
$this->saveConversation($conversation, $bot_id);
// 保存用户发送的消息
$userMessageData = [
'chat_id' => $conversation['id'],
'conversation_id' => $conversation['id'],
'bot_id' => $bot_id,
'content' => Env::get('ai.content'),
'content_type' => 'text',
'role' => 'assistant',
'type' => 'answer',
'created_at' => time(),
'updated_at' => time()
];
MessageModel::create($userMessageData);
}
return successJson($conversation, '创建成功');
if($is_internal){
return json_encode([
'code' => 200,
'data' => $conversation,
'msg' => '创建成功'
]);
}else{
return successJson($conversation, '创建成功');
}
} catch (\Exception $e) {
return errorJson('创建对话失败:' . $e->getMessage());
}
}
/**
* 创建对话
*/
public function createChat()
{
try {
$bot_id = Env::get('ai.bot_id');
$conversation_id = input('conversation_id','');
$question = input('question','');
if(empty($bot_id)){
return errorJson('智能体ID不能为空');
}
if(empty($conversation_id)){
return errorJson('会话ID不能为空');
}
if(empty($question)){
return errorJson('问题不能为空');
}
$userInfo = request()->userInfo;
$uid = $userInfo['id'];
$companyId = $userInfo['companyId'];
// 构建请求数据
$params = [
'bot_id' => strval($bot_id),
'user_id' => strval($uid),
'additional_messages' => [
[
'role' => 'user',
'content' => $question,
'type' => 'question',
'content_type' => 'text'
]
],
'stream' => false,
'auto_save_history' => true
];
$url = $this->apiUrl . '/v3/chat?conversation_id='.$conversation_id;
$result = $this->httpRequest($url, 'POST', json_encode($params,256), $this->headers);
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
}
// 保存用户发送的消息
$userMessageData = [
'chat_id' => $result['data']['id'],
'conversation_id' => $conversation_id,
'bot_id' => $bot_id,
'content' => $question,
'content_type' => 'text',
'role' => 'user',
'type' => 'question',
'created_at' => $result['data']['created_at'],
'updated_at' => $result['data']['created_at']
];
MessageModel::create($userMessageData);
return successJson($result['data'], '发送成功');
} catch (\Exception $e) {
return errorJson('创建对话失败:' . $e->getMessage());
}
}
/**
* 查看对话详情
*/
public function chatRetrieve()
{
$conversation_id = input('conversation_id','');
$chat_id = input('chat_id','');
if(empty($conversation_id) && empty($chat_id)){
return errorJson('参数缺失');
}
$conversation = ConversationModel::where('conversation_id', $conversation_id)->find();
if(empty($conversation)){
return errorJson('会话不存在');
}
$params = [
'conversation_id' => $conversation_id,
'chat_id' => $chat_id
];
$url = $this->apiUrl . '/v3/chat/retrieve?' . dataBuild($params);
$result = $this->httpRequest($url, 'GET', [], $this->headers);
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
}
$status = [
'created' => '对话已创建',
'in_progress' => '智能体正在处理中',
'completed' => '智能体已完成处理,本次对话结束',
'failed' => '对话失败',
'requires_action' => '对话中断,需要进一步处理',
'canceled' => '对话已取消',
];
$status_msg = $status[$result['data']['status']] ?? '未知状态';
if($result['data']['status'] == 'failed'){
$last_error = $result['data']['last_error'];
$error_msg = $status_msg . ',错误信息:' . $last_error['msg'];
return errorJson($error_msg);
}else{
return successJson($result['data'],$status_msg);
}
}
/**
* 获取对话消息详情
*/
public function chatMessage(){
$conversation_id = input('conversation_id','');
$chat_id = input('chat_id','');
if(empty($conversation_id) && empty($chat_id)){
return errorJson('参数缺失');
}
$conversation = ConversationModel::where('conversation_id', $conversation_id)->find();
if(empty($conversation)){
return errorJson('会话不存在');
}
$params = [
'conversation_id' => $conversation_id,
'chat_id' => $chat_id
];
$url = $this->apiUrl . '/v3/chat/message/list?' . dataBuild($params);
$result = $this->httpRequest($url, 'GET', [], $this->headers);
$result = json_decode($result, true);
if ($result['code'] != 0) {
return errorJson($result['msg'], $result['code']);
}
$data = $result['data'];
$list = [];
foreach($data as $item){
if($item['type'] == 'answer'){
$timestamp = $item['updated_at'];
$now = time();
$today = strtotime(date('Y-m-d'));
$yesterday = strtotime('-1 day', $today);
$thisYear = strtotime(date('Y-01-01'));
// 格式化时间
if($timestamp >= $today) {
$time = date('H:i', $timestamp);
} elseif($timestamp >= $yesterday) {
$time = '昨天 ' . date('H:i', $timestamp);
} elseif($timestamp >= $thisYear) {
$time = date('m-d H:i', $timestamp);
} else {
$time = date('Y-m-d H:i', $timestamp);
}
// 保存消息记录
$messageData = [
'chat_id' => $item['id'],
'conversation_id' => $conversation_id,
'bot_id' => $item['bot_id'],
'content' => $item['content'],
'content_type' => $item['content_type'],
'role' => $item['role'],
'type' => $item['type'],
'created_at' => $item['created_at'],
'updated_at' => $item['updated_at']
];
// 检查消息是否已存在
$exists = MessageModel::where('chat_id', $item['id'])->find();
if (!$exists) {
MessageModel::create($messageData);
}
$list = [
'id' => $item['id'],
'type' => 'assistant',
'content' => $item['content'],
'time' => $time
];
break;
}
}
return successJson($list, '获取成功');
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace app\cozeai\controller;
use app\cozeai\model\Conversation as ConversationModel;
use app\cozeai\model\Message as MessageModel;
use think\facade\Env;
/**
* Coze AI 消息控制器
*/
class MessageController extends BaseController
{
/**
* 获取用户与AI的对话记录
*/
public function getMessages()
{
try {
// 获取用户信息
$userInfo = request()->userInfo;
$uid = $userInfo['id'];
$companyId = $userInfo['companyId'];
// 获取会话ID
$conversation_id = input('conversation_id', '');
// 如果没有传入会话ID则查询用户最新的会话
if (empty($conversation_id)) {
// 查询用户是否有会话记录
$conversation = ConversationModel::where([
['userId', '=', $uid],
['companyId', '=', $companyId]
])->order('create_time', 'desc')->find();
// 如果没有会话记录,创建新会话
if (empty($conversation)) {
$conversationController = new ConversationController();
$result = $conversationController->create(true);
$result = json_decode($result, true);
if ($result['code'] != 200) {
return errorJson('创建会话失败:' . $result['msg']);
}
$conversation_id = $result['data']['id'];
} else {
$conversation_id = $conversation['conversation_id'];
}
} else {
// 验证会话是否属于当前用户
$conversation = ConversationModel::where([
['conversation_id', '=', $conversation_id],
['userId', '=', $uid],
['companyId', '=', $companyId]
])->find();
if (empty($conversation)) {
return errorJson('会话不存在或无权访问');
}
}
// 分页参数
$page = input('page', 1);
$limit = input('limit', 20);
// 查询消息记录
$messages = MessageModel::where('conversation_id', $conversation_id)
->order('id', 'DESC')
->page($page, $limit)
->select()
->each(function($item) {
// 格式化时间显示
$timestamp = $item['created_at'];
$today = strtotime(date('Y-m-d'));
$yesterday = strtotime('-1 day', $today);
$thisYear = strtotime(date('Y-01-01'));
if($timestamp >= $today) {
$item['show_time'] = date('H:i', $timestamp);
} elseif($timestamp >= $yesterday) {
$item['show_time'] = '昨天 ' . date('H:i', $timestamp);
} elseif($timestamp >= $thisYear) {
$item['show_time'] = date('m-d H:i', $timestamp);
} else {
$item['show_time'] = date('Y-m-d H:i', $timestamp);
}
// 根据role设置type
if ($item['role'] == 'assistant') {
$item['type'] = 'assistant';
} else {
$item['type'] = 'user';
}
unset($item['role']);
return $item;
});
// 对消息进行倒序处理
$messages = array_reverse($messages->toArray());
// 获取总记录数
$total = MessageModel::where('conversation_id', $conversation_id)->count();
$data = [
'list' => $messages,
'total' => $total,
'conversation_id' => $conversation_id
];
return successJson($data, '获取成功');
} catch (\Exception $e) {
return errorJson('获取对话记录失败:' . $e->getMessage());
}
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace app\cozeai\model;
use think\Model;
class Message extends Model
{
protected $name = 'coze_message';
protected $pk = 'id';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
protected $createTime = 'create_time';
protected $updateTime = 'update_time';
// 类型转换
protected $type = [
'created_at' => 'integer',
'updated_at' => 'integer',
'create_time' => 'integer',
'update_time' => 'integer'
];
}