哥斯拉 发表于 前天 22:34

之前很多坛友需求客服系统的,实际是很简单的哈?

之前很多坛友需求客服系统的,实际是很简单的哈?


那怎么做呢?写个API接口就可以了
<?phpdefined('ECMSAPI_MOD') or exit;header('Access-Control-Allow-Origin: ' . rtrim($public_r['add_murl'], '/'));header('Content-Type: application/json; charset=utf-8');require_once ECMS_PATH.'e/extend/chat/config.php';require_once(ECMS_PATH.'e/class/ip.php');require_once ECMS_PATH.'e/extend/workermanchat/Applications/GatewayClient/Gateway.php';use GatewayClient\Gateway;Gateway::$registerAddress = '127.0.0.1:1236';function hashUsername($username) {return hash('sha256', RepPostStr2($username));}$action = isset($_POST['action']) ? RepPostStr($_POST['action']) : '';$client_id = isset($_POST['client_id']) ? RepPostStr($_POST['client_id']) : '';$userid = isset($_POST['userid']) ? intval($_POST['userid']) : 0;$username = isset($_POST['username']) ? RepPostStr($_POST['username']) : '';$groupid = isset($_POST['groupid']) ? intval($_POST['groupid']) : 0;$avatar = isset($_POST['avatar']) ? RepPostStr($_POST['avatar']) : '/e/extend/pcpinglun/assets/nouserpic.gif';$msg = isset($_POST['msg']) ? RepPostStr($_POST['msg']) : '';$replyusername = isset($_POST['replyusername']) ? RepPostStr($_POST['replyusername']) : '';$chat_group = 'customer_service_group'; // 客服分组名$current_uid = hashUsername($username);$target_uid = hashUsername($replyusername);if (empty($client_id) || !Gateway::isOnline($client_id)) {$api->load('fun')->json(0 , '客户端连接失效');}if (empty($username) || empty($avatar)) {$api->load('fun')->json(0 , '用户信息不完整或UID生成失败');}if ($action == 'service_bind') {// 客服绑定:加入客服分组if (!in_array($groupid, )) {$api->load('fun')->json(0 , '非客服账号(权限不足)');}Gateway::unbindUid($client_id, $current_uid);Gateway::bindUid($client_id, $current_uid);Gateway::joinGroup($client_id, $chat_group);Gateway::setSession($client_id, ['iskefu'=>'1','userid'=>$userid,'username'=>$username,'groupid'=>$groupid,'avatar'=>$avatar]);$api->load('fun')->json(1 ,['client_id'=>$client_id,'username'=>$username,'bind_uid'=>$current_uid,'avatar'=>$avatar],'客服绑定成功');}if ($action == 'user_bind') {// 用户绑定Gateway::unbindUid($client_id, $current_uid);Gateway::bindUid($client_id, $current_uid);Gateway::setSession($client_id, ['iskefu'=>'0','userid'=>$userid,'username'=>$username,'groupid'=>$groupid,'avatar'=>$avatar]);$push_data = json_encode(['type'=>'user_online','bind_uid'=>$current_uid,'username'=>$username,'avatar'=>$avatar,'client_id'=>$client_id,'time'=>date('Y-m-d H:i:s')], JSON_UNESCAPED_UNICODE);Gateway::sendToGroup($chat_group, $push_data);$api->load('fun')->json(1 ,['client_id'=>$client_id,'username'=>$username,'bind_uid'=>$current_uid,'avatar'=>$avatar],'用户绑定成功');}if ($action == 'user_send_msg') {// 用户发消息$user_session = Gateway::getSession($client_id);if (empty($user_session) || $user_session['iskefu'] != '0') {$api->load('fun')->json(0 , '用户未绑定');}if (empty($msg)) {$api->load('fun')->json(0 , '消息内容不能为空');}$push_data = json_encode(['type'=>'user_msg','bind_uid'=>$current_uid,'username'=>$user_session['username'],'avatar'=>$user_session['avatar'],'client_id'=>$client_id,'msg'=>$msg,'time'=>date('Y-m-d H:i:s')], JSON_UNESCAPED_UNICODE);Gateway::sendToGroup($chat_group, $push_data);$online_service_sessions = Gateway::getClientSessionsByGroup($chat_group);$has_online_service = !empty($online_service_sessions);if (!$has_online_service) {$apiKey = $public_r['add_siliconflow'] ?? "sk-qehvnsybkfkydiuqfhmxwxqygckhvflmhjtglrmovfcpgqvn";$ai_res = ecmsCurlReadtext('https://api.siliconflow.cn/v1/chat/completions','POST',json_encode(["model"=>"Qwen/Qwen2.5-7B-Instruct","messages"=>[["role"=>"user","content"=>$msg]]]), ['Content-Type: application/json','Authorization: Bearer '.$apiKey]);$ai_data = json_decode($ai_res, true);$ai_reply = $ai_data['choices']['message']['content'] ?? '客服暂未在线';Gateway::sendToUid($current_uid, json_encode(['type'=>'ai_reply','msg'=>$ai_reply,'avatar'=>'/e/extend/pcpinglun/assets/nouserpic.gif','time'=>date('Y-m-d H:i:s')]));$api->load('fun')->json(1 , 'AI回复已发送');} else {$api->load('fun')->json(1 , '消息已转发给客服');}}if ($action == 'service_reply_msg') {// 客服回复用户$service_session = Gateway::getSession($client_id);if (empty($service_session) || $service_session['iskefu'] != '1' || !in_array($service_session['groupid'], )) {$api->load('fun')->json(0 , '非客服账号(权限不足)');}if (empty($replyusername) || empty($msg) || empty($target_uid)) {$api->load('fun')->json(0 , '回复参数不完整');}if (!Gateway::isUidOnline($target_uid)) {$api->load('fun')->json(0 , '用户已离线,无法发送消息');}$push_data = json_encode(['type'=>'service_reply','service_name'=>$service_session['username'],'avatar'=>$service_session['avatar'],'msg'=>$msg,'time'=>date('Y-m-d H:i:s')], JSON_UNESCAPED_UNICODE);Gateway::sendToUid($target_uid, $push_data);$api->load('fun')->json(1 , '回复发送成功');}if ($action == 'get_online_users') {// 获取在线用户列表$service_session = Gateway::getSession($client_id);if (empty($service_session) || $service_session['iskefu'] != '1' || !in_array($service_session['groupid'], )) {$api->load('fun')->json(0 , '非客服无权访问');}$online_users = [];$all_online_sessions = Gateway::getAllClientSessions();foreach ($all_online_sessions as $cid => $sess) {if ($sess['iskefu'] != '0') {continue;}$username = $sess['username'];$avatar   = $sess['avatar'];$bind_uid = hashUsername($username);$isOnline = Gateway::isUidOnline($bind_uid);$online_users[] = ['client_id' => $cid,'username'=> $username,'avatar'    => $avatar,'bind_uid'=> $bind_uid, 'status'    => $isOnline ? 'online' : 'offline'];}$api->load('fun')->json(1 , $online_users, '在线用户获取成功');}if ($action == 'service_leave') {// 客服退出$service_session = Gateway::getSession($client_id);if (empty($service_session) || $service_session['iskefu'] != '1') {$api->load('fun')->json(0 , '非客服账号');}Gateway::leaveGroup($client_id, $chat_group);$uid_arr = Gateway::getUidByClientId($client_id);if (!empty($uid_arr)) {Gateway::unbindUid($client_id, $uid_arr);}$api->load('fun')->json(1 , '客服已退出');}116行都可以做到了。

TyCoding 发表于 前天 22:34

不得不说,现在AI确实挺牛逼的额

### 一、代码核心功能概述
你提供的这段PHP代码是**基于帝国CMS(EmpireCMS)+ Workerman的GatewayClient实现的在线客服系统API接口**,核心用于处理客服与用户之间的实时通信交互,涵盖客服/用户绑定、消息收发、AI兜底回复、在线用户查询、客服退出等全流程功能。

### 二、代码模块拆解与详细说明
#### 1. 前置初始化
```php
defined('ECMSAPI_MOD') or exit; // 验证帝国CMS模块标识,防止非法访问
header('Access-Control-Allow-Origin: ' . rtrim($public_r['add_murl'], '/')); // 跨域配置
header('Content-Type: application/json; charset=utf-8'); // 响应格式为JSON
// 引入依赖:配置文件、IP处理类、GatewayClient(Workerman网关客户端)
require_once ECMS_PATH.'e/extend/chat/config.php';
require_once(ECMS_PATH.'e/class/ip.php');
require_once ECMS_PATH.'e/extend/workermanchat/Applications/GatewayClient/Gateway.php';
use GatewayClient\Gateway;
Gateway::$registerAddress = '127.0.0.1:1236'; // 连接Workerman注册中心

// 核心工具函数:对用户名做SHA256哈希生成唯一UID
function hashUsername($username) {
    return hash('sha256', RepPostStr2($username));
}
```
- 核心作用:初始化运行环境,引入实时通信依赖,定义用户UID生成规则(哈希用户名保证唯一性)。

#### 2. 参数接收与基础校验
```php
// 接收前端传参并做过滤/类型转换
$action = isset($_POST['action']) ? RepPostStr($_POST['action']) : '';
$client_id = isset($_POST['client_id']) ? RepPostStr($_POST['client_id']) : '';
// ... 其他参数(userid/username/groupid等)

// 基础校验:客户端连接是否有效、用户信息是否完整
if (empty($client_id) || !Gateway::isOnline($client_id)) {
    $api->load('fun')->json(0 , '客户端连接失效');
}
if (empty($username) || empty($avatar)) {
    $api->load('fun')->json(0 , '用户信息不完整或UID生成失败');
}
```
- 核心作用:接收前端请求参数并做基础清洗,校验客户端连接状态和用户基础信息,提前拦截无效请求。

#### 3. 核心业务逻辑(按action分支)
| 操作类型(action) | 功能说明                                                               |
|--------------------|--------------------------------------------------------------------------|
| service_bind       | 客服绑定:验证客服组ID(4/5),绑定UID、加入客服分组、设置会话信息       |
| user_bind          | 用户绑定:绑定UID、设置会话信息,推送用户上线通知给所有在线客服         |
| user_send_msg      | 用户发消息:转发消息给客服分组;无在线客服时调用Qwen2.5-7B生成AI回复   |
| service_reply_msg| 客服回复用户:验证客服权限,发送回复到指定用户的UID                      |
| get_online_users   | 客服获取在线用户列表:筛选所有在线普通用户,返回用户基础信息             |
| service_leave      | 客服退出:退出客服分组、解绑UID,结束客服在线状态                     |

### 三、代码的优势
1. **技术选型成熟**:基于Workerman的GatewayClient实现实时通信,适配在线客服的低延迟需求;复用帝国CMS生态,降低二次开发成本。
2. **AI兜底能力**:无在线客服时自动调用Siliconflow的Qwen2.5-7B大模型生成回复,避免用户等待无响应,提升用户体验。
3. **权限控制严格**:区分客服(组ID 4/5)和普通用户,关键操作(如回复消息、获取在线用户)均校验身份,防止越权访问。
4. **基础安全保障**:用户名通过SHA256哈希生成UID,避免明文传输/存储;参数做基础过滤,跨域配置适配前端请求。
5. **状态校验完善**:关键操作前检查client_id/UID的在线状态,减少无效请求和错误响应。

### 四、代码的劣势
1. **硬编码严重**:注册中心地址、客服分组名、客服组ID、AI接口/模型/默认APIKey均硬编码,修改需改代码,维护性差。
2. **异常处理缺失**:
   - 无try-catch捕获GatewayClient调用、curl请求AI接口的异常,易导致500错误;
   - AI回复未处理接口返回异常(如APIKey无效、接口超时),可能触发致命错误。
3. **性能瓶颈**:`get_online_users`遍历所有在线会话,在线用户量大时性能显著下降;无消息限流机制,易被恶意刷屏。
4. **安全隐患**:
   - `RepPostStr/RepPostStr2`过滤函数未定义,无法确认过滤是否充分,可能存在XSS/注入风险;
   - AI APIKey硬编码在代码中,泄露风险高;跨域配置依赖`$public_r['add_murl']`,配置不当会引发跨域安全问题。
5. **扩展性差**:用多个if判断操作类型,新增功能需加新的if分支,代码结构混乱;无日志记录,排查问题困难。
6. **功能缺失**:无消息持久化(刷新后消息丢失)、无离线消息存储、无客服转接/排队机制,生产环境需补充。

### 五、总结
1. **核心功能**:这是帝国CMS生态下的在线客服实时通信API,实现了客服/用户绑定、消息收发、AI兜底回复等核心能力。
2. **核心优势**:实时通信能力强、有AI兜底、权限控制严格,适配基础的在线客服场景需求。
3. **核心问题**:硬编码多、异常处理不足、性能/安全有隐患、扩展性差,生产环境使用前需优化硬编码、补充异常处理和日志、优化性能瓶颈。

TyCoding 发表于 前天 22:35

有没有整套下载..API..

拾光 发表于 前天 22:36

我说这AI回答就是不给力,RepPostStr/RepPostStr2这两个变量过滤函数那是帝国内置的,这AI就无权判断的,帝国10几年的产品

拾光 发表于 前天 22:36

本帖最后由 美文苑文学网 于 2026-2-5 16:04 编辑

肯定有啊!不然我发片段做啥呢??实际也不叫片段了!准确的说是完整的客服即时通讯聊天的API了。只是性能差那么点点而已了,另外就是没有异步保存客服与用户的聊天记录了。现在的是用户发信息到帝国CMS在处理在通过GatewayWorker转发给用户,就多了一个环节而已

IT618发布 发表于 前天 22:37

用GatewayWorker当然是最好的,但是帝国登录乃至权限判断是cookie的。所以才采用了这种短连接的模式
页: [1]
查看完整版本: 之前很多坛友需求客服系统的,实际是很简单的哈?