chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
<?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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user