Files
YwxAppThink/addon/wxchat/worker/handle/ChatHandle.php
T

169 lines
5.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace addon\wxchat\worker\handle;
use addon\wxchat\model\WxchatMessage;
use addon\wxchat\model\WxchatUnreadCount;
use addon\wxchat\service\SafetyService;
use addon\wxchat\worker\enum\MsgFrame;
use addon\wxchat\worker\enum\MsgType;
use addon\wxchat\worker\Message;
use addon\wxchat\worker\MsgReply;
use GatewayWorker\Lib\Gateway;
/**
* 聊天消息处理(实时落库 + 转发 + ACK)
*
* 协议约定(客户端上行):
* {
* "header": { "type":"chat", "target":"single"|"group", "timestamp":... },
* "payload": { "uid":123, "receiver":456, "session_id":789, "type":"text"|"image"|"voice"|"video"|"file"|"gift", "content":"...", "ext":{} }
* }
*/
class ChatHandle
{
// 内容类型字符串 -> 数据库 content_type(int)
private const CONTENT_TYPE_MAP = [
'text' => 0,
'image' => 1,
'voice' => 2,
'video' => 3,
'file' => 4,
'gift' => 5,
];
public static function handle($client_id, $header, $payload)
{
$uid = Gateway::getUidByClientId($client_id);
if (empty($uid)) {
MsgReply::sendError($client_id, '未认证,请先登录');
return;
}
$payloadUid = (int)($payload->uid ?? 0);
if ($payloadUid && $payloadUid != $uid) {
MsgReply::sendError($client_id, '身份不一致');
return;
}
$contentType = (string)($payload->type ?? 'text');
if (! isset(self::CONTENT_TYPE_MAP[$contentType])) {
MsgReply::sendError($client_id, '不支持的消息类型: ' . $contentType);
return;
}
// 礼物消息:仅做实时通知,落库由 HTTP Gift::send 负责
if ($contentType === 'gift') {
self::relayGift($client_id, $uid, $payload);
return;
}
$target = (string)($header->target ?? 'single');
$receiverType = ($target === 'group') ? 1 : 0;
$receiverId = (int)($target === 'group'
? ($payload->session_id ?? 0)
: ($payload->receiver ?? 0));
if (! $receiverId) {
MsgReply::sendError($client_id, '接收者无效');
return;
}
$content = $payload->content ?? '';
// 文本类消息做敏感词过滤
if ($contentType === 'text') {
$content = SafetyService::instance()->filter($content);
}
$msgId = self::saveMessage($uid, $receiverType, $receiverId, self::CONTENT_TYPE_MAP[$contentType], $content);
// 单聊:对方未读 +1
if ($receiverType == 0) {
WxchatUnreadCount::increment($receiverId, 0, $uid);
}
// 构造下发消息并转发
$out = self::buildOutbound($msgId, $uid, $contentType, $content, $payload);
if ($receiverType == 0) {
if (Gateway::isUidOnline($receiverId)) {
Gateway::sendToUid($receiverId, json_encode($out));
}
// 离线:消息已落库,接收方下次拉取 history 获取
} else {
Gateway::sendToGroup((string)$receiverId, json_encode($out));
}
// 发送方回执
MsgReply::send($client_id, MsgFrame::STATUS->value, MsgType::DELIVERED->value, 'delivered', [
'msg_id' => $msgId,
'receiver' => $receiverId,
'session_id' => $receiverId,
]);
}
/**
* 消息落库(可被 HTTP / WebSocket 共用,返回消息ID
*/
public static function saveMessage($senderId, $receiverType, $receiverId, $contentType, $content)
{
$msg = new WxchatMessage();
$msg->sender_id = $senderId;
$msg->receiver_type = $receiverType;
$msg->receiver_id = $receiverId;
$msg->content = $content;
$msg->content_type = $contentType;
$msg->save();
return $msg->id;
}
private static function buildOutbound($msgId, $senderId, $contentType, $content, $payload)
{
$out = new Message();
$out->header->frame = MsgFrame::CHAT->value;
$out->header->type = MsgFrame::CHAT->value;
$out->payload->msg_id = $msgId;
$out->payload->uid = $senderId;
$out->payload->type = $contentType; // 文本类为字符串,媒体类为URL
$out->payload->content = $content;
$out->payload->receiver = $payload->receiver ?? 0;
$out->payload->session_id = $payload->session_id ?? 0;
$out->payload->status = 'delivered';
if (! empty($payload->ext)) {
$out->payload->ext = $payload->ext;
}
return $out;
}
private static function relayGift($client_id, $uid, $payload)
{
$receiver = (int)($payload->receiver ?? 0);
if (! $receiver) {
MsgReply::sendError($client_id, '礼物接收者无效');
return;
}
$out = new Message();
$out->header->frame = MsgFrame::CHAT->value;
$out->header->type = 'gift';
$out->payload->uid = $uid;
$out->payload->receiver = $receiver;
$out->payload->content = $payload->content ?? '';
$out->payload->ext = $payload->ext ?? null;
if (Gateway::isUidOnline($receiver)) {
Gateway::sendToUid($receiver, json_encode($out));
}
MsgReply::send($client_id, MsgFrame::STATUS->value, MsgType::DELIVERED->value, 'delivered', [
'receiver' => $receiver,
]);
}
}