chore(addon/aikefu): 更新文档说明及功能
This commit is contained in:
545
docs/api_kefu.md
545
docs/api_kefu.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 一、接口说明
|
## 一、接口说明
|
||||||
|
|
||||||
本接口用于连接微信小程序与Dify聊天机器人,实现智能客服功能。
|
本接口用于连接微信小程序与Dify聊天机器人,实现智能客服功能。所有接口都采用事件驱动架构,支持高并发和模块化扩展。
|
||||||
|
|
||||||
## 二、配置说明
|
## 二、配置说明
|
||||||
|
|
||||||
@@ -28,9 +28,160 @@
|
|||||||
|
|
||||||
## 三、接口列表
|
## 三、接口列表
|
||||||
|
|
||||||
### 1. 智能客服聊天接口
|
### 1. 系统健康检查
|
||||||
|
|
||||||
**接口地址**:`/api/kefu/chat`
|
**接口地址**:`/api/health`
|
||||||
|
|
||||||
|
**请求方式**:GET
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| uniacid | int | 是 | 站点ID |
|
||||||
|
| check_type | string | 否 | 检查类型:full(完整)、basic(基础)、ai_service(AI服务),默认full |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "healthy",
|
||||||
|
"data": {
|
||||||
|
"status": "healthy",
|
||||||
|
"check_id": "health_63a8f9c1234abcd",
|
||||||
|
"timestamp": "2023-12-25 10:30:45",
|
||||||
|
"total_checks": 4,
|
||||||
|
"passed_checks": 4,
|
||||||
|
"failed_checks": 0,
|
||||||
|
"response_time_ms": 156.78,
|
||||||
|
"components": {
|
||||||
|
"database": {
|
||||||
|
"status": "healthy",
|
||||||
|
"message": "数据库连接正常",
|
||||||
|
"response_time_ms": 12.34,
|
||||||
|
"details": {
|
||||||
|
"connection": "success",
|
||||||
|
"query_test": "passed"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ai_service_config": {
|
||||||
|
"status": "healthy",
|
||||||
|
"message": "AI服务配置正常",
|
||||||
|
"response_time_ms": 8.56,
|
||||||
|
"details": {
|
||||||
|
"configured": true,
|
||||||
|
"complete": true,
|
||||||
|
"enabled": true,
|
||||||
|
"base_url": "https://api.example.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ai_service_connection": {
|
||||||
|
"status": "healthy",
|
||||||
|
"message": "AI服务连接正常",
|
||||||
|
"response_time_ms": 45.67,
|
||||||
|
"details": {
|
||||||
|
"http_status": 200,
|
||||||
|
"url": "https://api.example.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"system_resources": {
|
||||||
|
"status": "healthy",
|
||||||
|
"message": "系统资源正常",
|
||||||
|
"response_time_ms": 2.34,
|
||||||
|
"details": {
|
||||||
|
"php_version": "8.1.0",
|
||||||
|
"memory_usage": "45.67 MB",
|
||||||
|
"memory_limit": "512.00 MB",
|
||||||
|
"memory_usage_percent": "8.92%",
|
||||||
|
"max_execution_time": "30s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warnings": [],
|
||||||
|
"errors": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 获取服务配置信息
|
||||||
|
|
||||||
|
**接口地址**:`/api/info`
|
||||||
|
|
||||||
|
**请求方式**:GET
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| uniacid | int | 否 | 站点ID |
|
||||||
|
| member_id | int | 否 | 会员ID |
|
||||||
|
| token | string | 否 | 访问令牌 |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "success",
|
||||||
|
"data": {
|
||||||
|
"service_info": {
|
||||||
|
"name": "智能客服",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"enabled": true,
|
||||||
|
"status": "enabled"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"chat": true,
|
||||||
|
"chat_stream": true,
|
||||||
|
"conversation_management": true,
|
||||||
|
"history_management": true
|
||||||
|
},
|
||||||
|
"limits": {
|
||||||
|
"max_message_length": 4000,
|
||||||
|
"max_conversation_history": 100,
|
||||||
|
"rate_limit": {
|
||||||
|
"requests_per_minute": 60,
|
||||||
|
"requests_per_hour": 1000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"endpoints": {
|
||||||
|
"chat": "/api/chat",
|
||||||
|
"chat_stream": "/api/chatStream",
|
||||||
|
"create_conversation": "/api/createConversation",
|
||||||
|
"get_history": "/api/getHistory",
|
||||||
|
"clear_conversation": "/api/clearConversation",
|
||||||
|
"health": "/api/health",
|
||||||
|
"info": "/api/info"
|
||||||
|
},
|
||||||
|
"api_config": {
|
||||||
|
"base_url": "https://api.dify.ai/v1",
|
||||||
|
"chat_endpoint": "/chat-messages",
|
||||||
|
"supports_streaming": true,
|
||||||
|
"authentication": "bearer_token"
|
||||||
|
},
|
||||||
|
"client_info": {
|
||||||
|
"user_agent": "Mozilla/5.0...",
|
||||||
|
"ip": "192.168.1.100",
|
||||||
|
"timestamp": 1703505845
|
||||||
|
},
|
||||||
|
"server_info": {
|
||||||
|
"php_version": "8.1.0",
|
||||||
|
"server_time": "2023-12-25 10:30:45",
|
||||||
|
"timezone": "Asia/Shanghai"
|
||||||
|
},
|
||||||
|
"user_stats": {
|
||||||
|
"can_use_service": true,
|
||||||
|
"member_id": 123,
|
||||||
|
"site_id": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 智能客服聊天接口
|
||||||
|
|
||||||
|
**接口地址**:`/api/chat`
|
||||||
|
|
||||||
**请求方式**:POST
|
**请求方式**:POST
|
||||||
|
|
||||||
@@ -38,10 +189,13 @@
|
|||||||
|
|
||||||
| 参数名 | 类型 | 必填 | 说明 |
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
| uniacid | int | 是 | 站点ID |
|
||||||
| message | string | 是 | 用户输入的消息内容 |
|
| message | string | 是 | 用户输入的消息内容 |
|
||||||
| user_id | string | 否 | 用户ID,默认使用当前登录会员ID |
|
| user_id | string | 否 | 用户ID,默认使用当前登录会员ID |
|
||||||
| conversation_id | string | 否 | 会话ID,第一次聊天可不传,系统会自动创建 |
|
| conversation_id | string | 否 | 会话ID,第一次聊天可不传,系统会自动创建 |
|
||||||
| stream | bool | 否 | 是否使用流式响应,默认false |
|
| stream | bool | 否 | 是否使用流式响应,默认false |
|
||||||
|
| member_id | int | 否 | 会员ID |
|
||||||
|
| token | string | 否 | 访问令牌 |
|
||||||
|
|
||||||
**响应示例**:
|
**响应示例**:
|
||||||
|
|
||||||
@@ -59,58 +213,78 @@
|
|||||||
"completion_tokens": 20,
|
"completion_tokens": 20,
|
||||||
"total_tokens": 30
|
"total_tokens": 30
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"timestamp": 1640995200
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. 获取会话历史
|
### 4. 智能客服流式聊天接口
|
||||||
|
|
||||||
**接口地址**:`/api/kefu/getHistory`
|
**接口地址**:`/api/chatStream`
|
||||||
|
|
||||||
**请求方式**:POST
|
**请求方式**:POST
|
||||||
|
|
||||||
**请求参数**:
|
**请求参数**:同 `/api/chat` 接口
|
||||||
|
|
||||||
| 参数名 | 类型 | 必填 | 说明 |
|
**响应格式**:Server-Sent Events (SSE)
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| conversation_id | string | 是 | 会话ID |
|
|
||||||
| user_id | string | 否 | 用户ID,默认使用当前登录会员ID |
|
|
||||||
| limit | int | 否 | 每页条数,默认20 |
|
|
||||||
| offset | int | 否 | 偏移量,默认0 |
|
|
||||||
|
|
||||||
**响应示例**:
|
**响应示例**:
|
||||||
|
|
||||||
```json
|
```javascript
|
||||||
{
|
// 开始事件
|
||||||
"code": 0,
|
event: start
|
||||||
"message": "success",
|
data: {
|
||||||
|
"id": "unique_id",
|
||||||
|
"event": "start",
|
||||||
|
"timestamp": 1703505845,
|
||||||
"data": {
|
"data": {
|
||||||
"messages": [
|
"request_id": "stream_123",
|
||||||
{
|
"message": "开始处理请求"
|
||||||
"id": "msg_123456789",
|
}
|
||||||
"role": "user",
|
}
|
||||||
"content": "你好",
|
|
||||||
"created_at": "2023-01-01T00:00:00Z"
|
// 内容块事件
|
||||||
},
|
event: message
|
||||||
{
|
data: {
|
||||||
"id": "msg_987654321",
|
"id": "unique_id",
|
||||||
"role": "assistant",
|
"event": "message",
|
||||||
"content": "您好,我是智能客服,有什么可以帮助您的?",
|
"timestamp": 1703505845,
|
||||||
"created_at": "2023-01-01T00:00:01Z"
|
"data": {
|
||||||
}
|
"content": "您",
|
||||||
],
|
"conversation_id": "conv_123",
|
||||||
"total": 2,
|
"finished": false
|
||||||
"limit": 20,
|
}
|
||||||
"offset": 0
|
}
|
||||||
},
|
|
||||||
"timestamp": 1640995200
|
// 完成事件
|
||||||
|
event: complete
|
||||||
|
data: {
|
||||||
|
"id": "unique_id",
|
||||||
|
"event": "complete",
|
||||||
|
"timestamp": 1703505845,
|
||||||
|
"data": {
|
||||||
|
"conversation_id": "conv_123",
|
||||||
|
"message_id": "msg_456",
|
||||||
|
"usage": {},
|
||||||
|
"finish_reason": "stop"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结束事件
|
||||||
|
event: end
|
||||||
|
data: {
|
||||||
|
"id": "unique_id",
|
||||||
|
"event": "end",
|
||||||
|
"timestamp": 1703505845,
|
||||||
|
"data": {
|
||||||
|
"request_id": "stream_123",
|
||||||
|
"status": "completed"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 创建新会话
|
### 5. 创建新会话
|
||||||
|
|
||||||
**接口地址**:`/api/kefu/createConversation`
|
**接口地址**:`/api/createConversation`
|
||||||
|
|
||||||
**请求方式**:POST
|
**请求方式**:POST
|
||||||
|
|
||||||
@@ -118,7 +292,10 @@
|
|||||||
|
|
||||||
| 参数名 | 类型 | 必填 | 说明 |
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
| uniacid | int | 是 | 站点ID |
|
||||||
| user_id | string | 否 | 用户ID,默认使用当前登录会员ID |
|
| user_id | string | 否 | 用户ID,默认使用当前登录会员ID |
|
||||||
|
| member_id | int | 否 | 会员ID |
|
||||||
|
| token | string | 否 | 访问令牌 |
|
||||||
|
|
||||||
**响应示例**:
|
**响应示例**:
|
||||||
|
|
||||||
@@ -129,9 +306,83 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"conversation_id": "conv_123456789",
|
"conversation_id": "conv_123456789",
|
||||||
"name": "智能客服会话",
|
"name": "智能客服会话",
|
||||||
"created_at": "2023-01-01T00:00:00Z"
|
"created_at": "2023-12-25 10:30:45"
|
||||||
},
|
}
|
||||||
"timestamp": 1640995200
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 获取会话历史
|
||||||
|
|
||||||
|
**接口地址**:`/api/getHistory`
|
||||||
|
|
||||||
|
**请求方式**:POST
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| uniacid | int | 是 | 站点ID |
|
||||||
|
| conversation_id | string | 是 | 会话ID |
|
||||||
|
| user_id | string | 否 | 用户ID,默认使用当前登录会员ID |
|
||||||
|
| limit | int | 否 | 每页条数,默认20 |
|
||||||
|
| offset | int | 否 | 偏移量,默认0 |
|
||||||
|
| member_id | int | 否 | 会员ID |
|
||||||
|
| token | string | 否 | 访问令牌 |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "success",
|
||||||
|
"data": {
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"role": "user",
|
||||||
|
"content": "你好",
|
||||||
|
"create_time": "2023-12-25 10:30:45"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "您好,我是智能客服,有什么可以帮助您的?",
|
||||||
|
"create_time": "2023-12-25 10:30:46"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 2,
|
||||||
|
"limit": 20,
|
||||||
|
"offset": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. 清除会话历史
|
||||||
|
|
||||||
|
**接口地址**:`/api/clearConversation`
|
||||||
|
|
||||||
|
**请求方式**:POST
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| uniacid | int | 是 | 站点ID |
|
||||||
|
| conversation_id | string | 否 | 会话ID(与user_id二选一) |
|
||||||
|
| user_id | string | 否 | 用户ID,用于清除该用户所有会话(与conversation_id二选一) |
|
||||||
|
| member_id | int | 否 | 会员ID |
|
||||||
|
| token | string | 否 | 访问令牌 |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 0,
|
||||||
|
"message": "success",
|
||||||
|
"data": {
|
||||||
|
"deleted_messages": 15,
|
||||||
|
"deleted_conversations": 3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -143,15 +394,41 @@
|
|||||||
// 引入请求封装(根据项目实际情况调整)
|
// 引入请求封装(根据项目实际情况调整)
|
||||||
import { request } from '@/utils/request';
|
import { request } from '@/utils/request';
|
||||||
|
|
||||||
// 智能客服聊天
|
// 1. 获取服务配置信息
|
||||||
|
async function getAIInfo() {
|
||||||
|
try {
|
||||||
|
const res = await request({
|
||||||
|
url: '/api/info',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
uniacid: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
console.log('AI服务状态:', res.data.service_info);
|
||||||
|
console.log('可用功能:', res.data.features);
|
||||||
|
return res.data;
|
||||||
|
} else {
|
||||||
|
console.error('获取配置失败:', res.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取配置请求失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 智能客服聊天(普通模式)
|
||||||
async function chatWithAI(message, conversationId = '') {
|
async function chatWithAI(message, conversationId = '') {
|
||||||
try {
|
try {
|
||||||
const res = await request({
|
const res = await request({
|
||||||
url: '/api/kefu/chat',
|
url: '/api/chat',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
|
uniacid: 1,
|
||||||
message: message,
|
message: message,
|
||||||
conversation_id: conversationId,
|
conversation_id: conversationId
|
||||||
// user_id: 'your-user-id', // 可选
|
// user_id: 'your-user-id', // 可选
|
||||||
// stream: false // 可选
|
// stream: false // 可选
|
||||||
}
|
}
|
||||||
@@ -169,13 +446,78 @@ async function chatWithAI(message, conversationId = '') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取会话历史
|
// 3. 智能客服聊天(流式模式)
|
||||||
|
async function chatWithAIStream(message, conversationId = '', onMessage, onComplete, onError) {
|
||||||
|
try {
|
||||||
|
const response = await uni.request({
|
||||||
|
url: '/api/chatStream',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
uniacid: 1,
|
||||||
|
message: message,
|
||||||
|
conversation_id: conversationId
|
||||||
|
},
|
||||||
|
responseType: 'text'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理流式响应
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
const lines = response.data.split('\n');
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('data: ')) {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(line.substring(6));
|
||||||
|
if (data.event === 'message' && onMessage) {
|
||||||
|
onMessage(data.data);
|
||||||
|
} else if (data.event === 'complete' && onComplete) {
|
||||||
|
onComplete(data.data);
|
||||||
|
} else if (data.event === 'error' && onError) {
|
||||||
|
onError(data.data);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('解析流式数据失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('流式聊天请求失败:', error);
|
||||||
|
if (onError) onError({ error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 创建新会话
|
||||||
|
async function createNewConversation() {
|
||||||
|
try {
|
||||||
|
const res = await request({
|
||||||
|
url: '/api/createConversation',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
uniacid: 1
|
||||||
|
// user_id: 'your-user-id', // 可选
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data.conversation_id;
|
||||||
|
} else {
|
||||||
|
console.error('创建会话失败:', res.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建会话请求失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 获取会话历史
|
||||||
async function getChatHistory(conversationId, limit = 20, offset = 0) {
|
async function getChatHistory(conversationId, limit = 20, offset = 0) {
|
||||||
try {
|
try {
|
||||||
const res = await request({
|
const res = await request({
|
||||||
url: '/api/kefu/getHistory',
|
url: '/api/getHistory',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
|
uniacid: 1,
|
||||||
conversation_id: conversationId,
|
conversation_id: conversationId,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
offset: offset
|
offset: offset
|
||||||
@@ -195,25 +537,51 @@ async function getChatHistory(conversationId, limit = 20, offset = 0) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建新会话
|
// 6. 清除会话历史
|
||||||
async function createNewConversation() {
|
async function clearConversation(conversationId = '', userId = '') {
|
||||||
try {
|
try {
|
||||||
const res = await request({
|
const res = await request({
|
||||||
url: '/api/kefu/createConversation',
|
url: '/api/clearConversation',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
// user_id: 'your-user-id', // 可选
|
uniacid: 1,
|
||||||
|
conversation_id: conversationId,
|
||||||
|
user_id: userId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
return res.data.conversation_id;
|
return res.data;
|
||||||
} else {
|
} else {
|
||||||
console.error('创建会话失败:', res.message);
|
console.error('清除会话失败:', res.message);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('创建会话请求失败:', error);
|
console.error('清除会话请求失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 健康检查
|
||||||
|
async function checkHealth(checkType = 'full') {
|
||||||
|
try {
|
||||||
|
const res = await request({
|
||||||
|
url: '/api/health',
|
||||||
|
method: 'GET',
|
||||||
|
data: {
|
||||||
|
uniacid: 1,
|
||||||
|
check_type: checkType
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
} else {
|
||||||
|
console.error('健康检查失败:', res.message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('健康检查请求失败:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,46 +589,77 @@ async function createNewConversation() {
|
|||||||
|
|
||||||
## 五、使用流程
|
## 五、使用流程
|
||||||
|
|
||||||
1. **初始化会话**:小程序端进入客服页面时,调用`createConversation`接口创建新会话,或使用本地存储的会话ID
|
1. **初始化检查**:小程序端启动时,调用`health`和`info`接口检查服务状态
|
||||||
2. **发送消息**:用户输入消息后,调用`chat`接口发送消息,获取机器人回复
|
2. **创建会话**:进入客服页面时,调用`createConversation`接口创建新会话,或使用本地存储的会话ID
|
||||||
3. **显示消息**:将用户消息和机器人回复显示在聊天界面
|
3. **发送消息**:用户输入消息后,调用`chat`或`chatStream`接口发送消息,获取机器人回复
|
||||||
4. **加载历史记录**:需要时调用`getHistory`接口加载历史消息
|
4. **显示消息**:将用户消息和机器人回复显示在聊天界面
|
||||||
5. **维护会话**:保持会话ID,用于后续消息交流
|
5. **加载历史记录**:需要时调用`getHistory`接口加载历史消息
|
||||||
|
6. **维护会话**:保持会话ID,用于后续消息交流
|
||||||
|
7. **清理数据**:根据用户需求调用`clearConversation`接口清理历史数据
|
||||||
|
|
||||||
## 六、注意事项
|
## 六、注意事项
|
||||||
|
|
||||||
1. 请确保Dify API密钥的安全性,不要泄露给前端
|
1. **必填参数**:所有接口都需要`uniacid`(站点ID)参数
|
||||||
2. 建议对用户ID进行加密处理,避免直接使用敏感信息
|
2. **事件驱动**:后端采用事件驱动架构,所有业务逻辑通过事件处理器执行
|
||||||
3. 对于大量用户的场景,建议实现会话管理机制,定期清理过期会话
|
3. **安全性**:请确保Dify API密钥的安全性,不要泄露给前端
|
||||||
4. 建议添加请求频率限制,防止恶意请求
|
4. **用户标识**:建议对用户ID进行加密处理,避免直接使用敏感信息
|
||||||
5. 在生产环境中,建议关闭DEBUG模式
|
5. **流式支持**:推荐使用`chatStream`接口获得更好的用户体验
|
||||||
|
6. **会话管理**:建议实现会话管理机制,定期清理过期会话
|
||||||
|
7. **频率限制**:建议添加请求频率限制,防止恶意请求
|
||||||
|
8. **生产环境**:在生产环境中,建议关闭DEBUG模式
|
||||||
|
|
||||||
## 七、测试建议
|
## 七、测试建议
|
||||||
|
|
||||||
1. 首先在Dify平台测试聊天机器人功能是否正常
|
1. **基础检查**:首先调用`health`接口检查系统状态
|
||||||
2. 在后端配置正确的API密钥
|
2. **配置验证**:调用`info`接口验证配置信息
|
||||||
3. 使用Postman或类似工具测试后端API接口
|
3. **接口测试**:使用Postman或类似工具测试各个API接口
|
||||||
4. 在小程序端集成并测试完整流程
|
4. **流式测试**:测试`chatStream`接口的流式响应
|
||||||
5. 模拟不同场景下的用户输入,测试机器人回复效果
|
5. **完整流程**:在小程序端集成并测试完整流程
|
||||||
|
6. **边界测试**:模拟不同场景下的用户输入,测试机器人回复效果
|
||||||
|
7. **压力测试**:测试接口在高并发情况下的表现
|
||||||
|
|
||||||
## 八、常见问题
|
## 八、常见问题
|
||||||
|
|
||||||
### 1. 接口返回401错误
|
### 1. 接口返回400错误
|
||||||
|
|
||||||
|
**原因**:缺少必填参数`uniacid`或参数格式错误
|
||||||
|
**解决方法**:确保请求中包含有效的站点ID
|
||||||
|
|
||||||
|
### 2. 健康检查返回503错误
|
||||||
|
|
||||||
|
**原因**:AI服务配置不完整或服务异常
|
||||||
|
**解决方法**:检查插件配置和Dify API服务状态
|
||||||
|
|
||||||
|
### 3. 接口返回401错误
|
||||||
|
|
||||||
**原因**:Dify API密钥无效或过期
|
**原因**:Dify API密钥无效或过期
|
||||||
**解决方法**:重新获取有效的API密钥并更新插件配置
|
**解决方法**:重新获取有效的API密钥并更新插件配置
|
||||||
|
|
||||||
### 2. 接口返回500错误
|
### 4. 接口返回500错误
|
||||||
|
|
||||||
**原因**:后端服务器错误或Dify API服务异常
|
**原因**:后端服务器错误或Dify API服务异常
|
||||||
**解决方法**:查看服务器日志,检查Dify API服务状态
|
**解决方法**:查看服务器日志,检查Dify API服务状态
|
||||||
|
|
||||||
### 3. 机器人回复为空
|
### 5. 机器人回复为空
|
||||||
|
|
||||||
**原因**:Dify聊天机器人配置问题或请求参数错误
|
**原因**:Dify聊天机器人配置问题或请求参数错误
|
||||||
**解决方法**:检查Dify机器人配置,验证请求参数是否正确
|
**解决方法**:检查Dify机器人配置,验证请求参数是否正确
|
||||||
|
|
||||||
### 4. 会话ID无效
|
### 6. 流式响应无法解析
|
||||||
|
|
||||||
|
**原因**:客户端不支持SSE或解析方式错误
|
||||||
|
**解决方法**:使用正确的方式解析Server-Sent Events格式
|
||||||
|
|
||||||
|
### 7. 会话ID无效
|
||||||
|
|
||||||
**原因**:会话已过期或不存在
|
**原因**:会话已过期或不存在
|
||||||
**解决方法**:创建新会话,获取新的会话ID
|
**解决方法**:创建新会话,获取新的会话ID
|
||||||
|
|
||||||
|
## 九、性能优化建议
|
||||||
|
|
||||||
|
1. **缓存配置**:可对`info`接口返回的配置信息进行客户端缓存
|
||||||
|
2. **连接复用**:HTTP请求使用连接池,减少建立连接的开销
|
||||||
|
3. **压缩传输**:启用gzip压缩减少传输数据量
|
||||||
|
4. **分页加载**:历史记录使用分页加载,避免一次性加载大量数据
|
||||||
|
5. **CDN加速**:静态资源使用CDN加速访问
|
||||||
|
6. **监控告警**:建立接口性能监控和告警机制
|
||||||
@@ -26,6 +26,9 @@ return [
|
|||||||
'KefuChatStream' => [
|
'KefuChatStream' => [
|
||||||
'addon\aikefu\event\KefuChatStream'
|
'addon\aikefu\event\KefuChatStream'
|
||||||
],
|
],
|
||||||
|
'KefuGetInfo' => [
|
||||||
|
'addon\aikefu\event\KefuGetInfo'
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
'subscribe' => [
|
'subscribe' => [
|
||||||
|
|||||||
127
src/addon/aikefu/event/KefuGetInfo.php
Normal file
127
src/addon/aikefu/event/KefuGetInfo.php
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace addon\aikefu\event;
|
||||||
|
|
||||||
|
use addon\aikefu\model\Config as KefuConfigModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取智能客服配置信息
|
||||||
|
*/
|
||||||
|
class KefuGetInfo
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 处理获取配置信息事件
|
||||||
|
* @param array $data 事件数据
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function handle($data)
|
||||||
|
{
|
||||||
|
$site_id = $data['site_id'] ?? 0;
|
||||||
|
$member_id = $data['member_id'] ?? 0;
|
||||||
|
$client_info = $data['client_info'] ?? [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取智能客服配置
|
||||||
|
$kefu_config_model = new KefuConfigModel();
|
||||||
|
$config_info = $kefu_config_model->getConfig($site_id);
|
||||||
|
|
||||||
|
$response_data = [
|
||||||
|
'service_info' => [
|
||||||
|
'name' => '智能客服',
|
||||||
|
'version' => '1.0.0',
|
||||||
|
'enabled' => false,
|
||||||
|
'status' => 'disabled'
|
||||||
|
],
|
||||||
|
'features' => [],
|
||||||
|
'limits' => [
|
||||||
|
'max_message_length' => 4000,
|
||||||
|
'max_conversation_history' => 100,
|
||||||
|
'rate_limit' => [
|
||||||
|
'requests_per_minute' => 60,
|
||||||
|
'requests_per_hour' => 1000
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'endpoints' => [
|
||||||
|
'chat' => '/api/chat',
|
||||||
|
'chat_stream' => '/api/chatStream',
|
||||||
|
'create_conversation' => '/api/createConversation',
|
||||||
|
'get_history' => '/api/getHistory',
|
||||||
|
'clear_conversation' => '/api/clearConversation',
|
||||||
|
'health' => '/api/health',
|
||||||
|
'info' => '/api/info'
|
||||||
|
],
|
||||||
|
'client_info' => $client_info,
|
||||||
|
'server_info' => [
|
||||||
|
'php_version' => PHP_VERSION,
|
||||||
|
'server_time' => date('Y-m-d H:i:s'),
|
||||||
|
'timezone' => date_default_timezone_get()
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// 处理配置信息
|
||||||
|
if (!empty($config_info['data']['value'])) {
|
||||||
|
$config = $config_info['data']['value'];
|
||||||
|
|
||||||
|
// 服务状态
|
||||||
|
$response_data['service_info']['enabled'] = $config['status'] == 1;
|
||||||
|
$response_data['service_info']['status'] = $config['status'] == 1 ? 'enabled' : 'disabled';
|
||||||
|
|
||||||
|
// 可用功能
|
||||||
|
if ($config['status'] == 1) {
|
||||||
|
$response_data['features'] = [
|
||||||
|
'chat' => true,
|
||||||
|
'chat_stream' => true,
|
||||||
|
'conversation_management' => true,
|
||||||
|
'history_management' => true
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// API端点信息(仅在启用时显示详细配置)
|
||||||
|
if ($config['status'] == 1) {
|
||||||
|
$response_data['api_config'] = [
|
||||||
|
'base_url' => $config['base_url'] ?? '',
|
||||||
|
'chat_endpoint' => $config['chat_endpoint'] ?? '',
|
||||||
|
'supports_streaming' => true,
|
||||||
|
'authentication' => 'bearer_token'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制配置(如果有的话)
|
||||||
|
if (isset($config['max_message_length'])) {
|
||||||
|
$response_data['limits']['max_message_length'] = intval($config['max_message_length']);
|
||||||
|
}
|
||||||
|
if (isset($config['rate_limit_per_minute'])) {
|
||||||
|
$response_data['limits']['rate_limit']['requests_per_minute'] = intval($config['rate_limit_per_minute']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加使用统计信息(如果需要的话)
|
||||||
|
if ($member_id > 0) {
|
||||||
|
$response_data['user_stats'] = [
|
||||||
|
'can_use_service' => $response_data['service_info']['enabled'],
|
||||||
|
'member_id' => $member_id,
|
||||||
|
'site_id' => $site_id
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'code' => 0,
|
||||||
|
'message' => '获取配置信息成功',
|
||||||
|
'data' => $response_data
|
||||||
|
];
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return [
|
||||||
|
'code' => -1,
|
||||||
|
'message' => '获取配置信息失败:' . $e->getMessage(),
|
||||||
|
'data' => [
|
||||||
|
'service_info' => [
|
||||||
|
'name' => '智能客服',
|
||||||
|
'status' => 'error'
|
||||||
|
],
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -125,6 +125,82 @@ class AI extends BaseApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得AI服务的配置信息发送给客户端
|
||||||
|
* @return \think\response\Json
|
||||||
|
*/
|
||||||
|
public function info()
|
||||||
|
{
|
||||||
|
// (可选)获取站点ID和会员ID,可以通过事件数据传递
|
||||||
|
$site_id = $this->params['uniacid'] ?? $this->site_id;
|
||||||
|
$member_id = $this->params['member_id'] ?? $this->member_id;
|
||||||
|
$token = $this->params['token'] ?? $this->token;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 准备事件数据
|
||||||
|
$event_data = [
|
||||||
|
'site_id' => $site_id,
|
||||||
|
'member_id' => $member_id,
|
||||||
|
'token' => $token,
|
||||||
|
'client_info' => [
|
||||||
|
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
|
||||||
|
'ip' => $this->getClientIp(),
|
||||||
|
'timestamp' => time()
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// 触发获取配置信息事件
|
||||||
|
$result = Event::trigger('KefuGetInfo', $event_data);
|
||||||
|
|
||||||
|
// 处理事件结果
|
||||||
|
$response = [
|
||||||
|
'code' => 0,
|
||||||
|
'message' => 'success',
|
||||||
|
'data' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
if (is_array($result) && !empty($result)) {
|
||||||
|
foreach ($result as $res) {
|
||||||
|
if (isset($res['code']) && $res['code'] < 0) {
|
||||||
|
$response = $res;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (isset($res['data'])) {
|
||||||
|
$response['data'] = array_merge($response['data'], $res['data']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->response($response);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->response($this->error('请求失败:' . $e->getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取客户端IP地址
|
||||||
|
*/
|
||||||
|
private function getClientIp()
|
||||||
|
{
|
||||||
|
$ip = '';
|
||||||
|
|
||||||
|
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||||
|
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||||
|
} elseif (isset($_SERVER['HTTP_X_REAL_IP']) && !empty($_SERVER['HTTP_X_REAL_IP'])) {
|
||||||
|
$ip = $_SERVER['HTTP_X_REAL_IP'];
|
||||||
|
} elseif (isset($_SERVER['REMOTE_ADDR']) && !empty($_SERVER['REMOTE_ADDR'])) {
|
||||||
|
$ip = $_SERVER['REMOTE_ADDR'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理多个IP的情况(X-Forwarded-For可能包含多个IP)
|
||||||
|
if (strpos($ip, ',') !== false) {
|
||||||
|
$ips = explode(',', $ip);
|
||||||
|
$ip = trim($ips[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ip;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除会话历史
|
* 清除会话历史
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,187 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace app\api\controller;
|
|
||||||
|
|
||||||
use app\api\controller\BaseApi;
|
|
||||||
use think\facade\Event;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 智能客服API控制器
|
|
||||||
*/
|
|
||||||
class Kefu extends BaseApi
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* 智能客服聊天接口
|
|
||||||
* @return \think\response\Json
|
|
||||||
*/
|
|
||||||
public function chat()
|
|
||||||
{
|
|
||||||
// 获取请求参数
|
|
||||||
$message = $this->params['message'] ?? '';
|
|
||||||
$user_id = $this->params['user_id'] ?? $this->member_id;
|
|
||||||
$conversation_id = $this->params['conversation_id'] ?? '';
|
|
||||||
$stream = $this->params['stream'] ?? false;
|
|
||||||
|
|
||||||
// (可选)获取站点ID和会员ID,可以通过事件数据传递
|
|
||||||
$site_id = $this->params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了
|
|
||||||
$member_id = $this->params['member_id'] ?? $this->member_id;
|
|
||||||
$token = $this->params['token'] ?? $this->token;
|
|
||||||
|
|
||||||
// 验证参数
|
|
||||||
if (empty($message)) {
|
|
||||||
return $this->response($this->error('请输入消息内容'));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 准备事件数据
|
|
||||||
$event_data = [
|
|
||||||
'message' => $message,
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'conversation_id' => $conversation_id,
|
|
||||||
'stream' => $stream,
|
|
||||||
'site_id' =>$site_id,
|
|
||||||
'member_id' => $member_id,
|
|
||||||
'token' => $token,
|
|
||||||
];
|
|
||||||
|
|
||||||
// 触发智能客服聊天事件
|
|
||||||
$result = Event::trigger('KefuChat', $event_data);
|
|
||||||
|
|
||||||
// 处理事件结果
|
|
||||||
$response = [
|
|
||||||
'code' => 0,
|
|
||||||
'message' => 'success',
|
|
||||||
'data' => []
|
|
||||||
];
|
|
||||||
|
|
||||||
if (is_array($result) && !empty($result)) {
|
|
||||||
foreach ($result as $res) {
|
|
||||||
if (isset($res['code']) && $res['code'] < 0) {
|
|
||||||
$response = $res;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (isset($res['data'])) {
|
|
||||||
$response['data'] = array_merge($response['data'], $res['data']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->response($response);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return $this->response($this->error('请求失败:' . $e->getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建新会话
|
|
||||||
* @return \think\response\Json
|
|
||||||
*/
|
|
||||||
public function createConversation()
|
|
||||||
{
|
|
||||||
// 获取请求参数
|
|
||||||
$user_id = $this->params['user_id'] ?? $this->member_id;
|
|
||||||
|
|
||||||
// (可选)获取站点ID和会员ID,可以通过事件数据传递
|
|
||||||
$site_id = $this->params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了
|
|
||||||
$member_id = $this->params['member_id'] ?? $this->member_id;
|
|
||||||
$token = $this->params['token'] ?? $this->token;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 准备事件数据
|
|
||||||
$event_data = [
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'site_id' =>$site_id,
|
|
||||||
'member_id' => $member_id,
|
|
||||||
'token' => $token,
|
|
||||||
];
|
|
||||||
|
|
||||||
// 触发创建会话事件
|
|
||||||
$result = Event::trigger('KefuCreateConversation', $event_data);
|
|
||||||
|
|
||||||
// 处理事件结果
|
|
||||||
$response = [
|
|
||||||
'code' => 0,
|
|
||||||
'message' => 'success',
|
|
||||||
'data' => []
|
|
||||||
];
|
|
||||||
|
|
||||||
if (is_array($result) && !empty($result)) {
|
|
||||||
foreach ($result as $res) {
|
|
||||||
if (isset($res['code']) && $res['code'] < 0) {
|
|
||||||
$response = $res;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (isset($res['data'])) {
|
|
||||||
$response['data'] = array_merge($response['data'], $res['data']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->response($response);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return $this->response($this->error('请求失败:' . $e->getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取会话历史
|
|
||||||
* @return \think\response\Json
|
|
||||||
*/
|
|
||||||
public function getHistory()
|
|
||||||
{
|
|
||||||
// 获取请求参数
|
|
||||||
$conversation_id = $this->params['conversation_id'] ?? '';
|
|
||||||
$user_id = $this->params['user_id'] ?? $this->member_id;
|
|
||||||
$limit = $this->params['limit'] ?? 20;
|
|
||||||
$offset = $this->params['offset'] ?? 0;
|
|
||||||
|
|
||||||
// (可选)获取站点ID和会员ID,可以通过事件数据传递
|
|
||||||
$site_id = $this->params['uniacid'] ?? $this->site_id; // 使用 uniacid, 方便以后迁移,而且uniacid 是唯一的, site_id 不是,同时被params给过滤了
|
|
||||||
$member_id = $this->params['member_id'] ?? $this->member_id;
|
|
||||||
$token = $this->params['token'] ?? $this->token;
|
|
||||||
|
|
||||||
// 验证参数
|
|
||||||
if (empty($conversation_id)) {
|
|
||||||
return $this->response($this->error('会话ID不能为空'));
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 准备事件数据
|
|
||||||
$event_data = [
|
|
||||||
'conversation_id' => $conversation_id,
|
|
||||||
'user_id' => $user_id,
|
|
||||||
'limit' => $limit,
|
|
||||||
'offset' => $offset,
|
|
||||||
'site_id' =>$site_id,
|
|
||||||
'member_id' => $member_id,
|
|
||||||
'token' => $token,
|
|
||||||
];
|
|
||||||
|
|
||||||
// 触发获取历史消息事件
|
|
||||||
$result = Event::trigger('KefuGetHistory', $event_data);
|
|
||||||
|
|
||||||
// 处理事件结果
|
|
||||||
$response = [
|
|
||||||
'code' => 0,
|
|
||||||
'message' => 'success',
|
|
||||||
'data' => []
|
|
||||||
];
|
|
||||||
|
|
||||||
if (is_array($result) && !empty($result)) {
|
|
||||||
foreach ($result as $res) {
|
|
||||||
if (isset($res['code']) && $res['code'] < 0) {
|
|
||||||
$response = $res;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (isset($res['data'])) {
|
|
||||||
$response['data'] = array_merge($response['data'], $res['data']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->response($response);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return $this->response($this->error('请求失败:' . $e->getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user