Files
YwxAppThink/addon/wxchat/controller/api/Message.php
T

214 lines
7.0 KiB
PHP

<?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\controller\api;
use addon\wxchat\model\WxchatMessage;
use addon\wxchat\model\WxchatUnreadCount;
use think\facade\Db;
use think\facade\Request;
use ywxapp\controller\FrontendBase;
use ywxapp\model\MemberUser as UserModel;
use ywxapp\model\MemberProfile as ProfileModel;
/**
* Message 类
*
* @author ywxapp <admin@ywxapp.cn>
*/
class Message extends FrontendBase
{
protected $noNeedLogin = [];
protected $noNeedVerify = ['*'];
protected function initialize()
{}
/**
* 发送消息(HTTP 落库,WebSocket 实时推送由 worker 处理)
*/
public function send()
{
$uid = $this->auth->model->uid;
$receiverId = (int) Request::post('receiver_id', 0);
$receiverType = (int) Request::post('receiver_type', 0); // 0-单聊 1-群聊
$content = trim((string) Request::post('content', ''));
$contentType = (int) Request::post('content_type', 0);
if (! $receiverId || $receiverId == $uid) {
$this->error('接收者无效');
}
if ($content === '' && $contentType == 0) {
$this->error('消息内容不能为空');
}
$msg = new WxchatMessage();
$msg->sender_id = $uid;
$msg->receiver_type = $receiverType;
$msg->receiver_id = $receiverId;
$msg->content = $content;
$msg->content_type = $contentType;
$msg->save();
// 更新对方未读计数(单聊)
if ($receiverType == 0) {
WxchatUnreadCount::increment($receiverId, 0, $uid);
}
$this->success($msg->toArray(), '发送成功');
}
/**
* 会话历史(单聊 / 群聊)
*/
public function history()
{
$uid = $this->auth->uid;
$contactId = (int) Request::get('contact_id', 0);
$contactType = (int) Request::get('contact_type', 0);
$limit = (int) Request::get('limit', 20);
$page = (int) Request::get('page', 1);
if (! $contactId) {
$this->error('会话对象无效');
}
$query = WxchatMessage::where('is_recalled', 0)
->where('receiver_type', $contactType);
if ($contactType == 0) {
// 单聊:取双方互发消息(修复原逻辑因外层 receiver_id 过滤而漏掉对方发来的消息)
$query->where(function ($q) use ($uid, $contactId) {
$q->where(function ($q) use ($uid, $contactId) {
$q->where('sender_id', $uid)->where('receiver_id', $contactId);
})->whereOr(function ($q) use ($uid, $contactId) {
$q->where('sender_id', $contactId)->where('receiver_id', $uid);
});
});
} else {
$query->where('receiver_id', $contactId);
}
$list = $query->order('create_at', 'desc')
->paginate($limit, false, ['page' => $page]);
// 单聊:读取后清零未读
if ($contactType == 0) {
WxchatUnreadCount::reset($uid, 0, $contactId);
}
$this->success([
'list' => $list->items(),
'total' => $list->total(),
'pages' => $list->lastPage(),
]);
}
/**
* 未读消息列表
*/
public function unread()
{
$uid = $this->auth->model->uid;
$list = WxchatUnreadCount::where('user_id', $uid)
->where('unread_count', '>', 0)
->order('update_at', 'desc')
->select();
$this->success($list);
}
/**
* 会话列表(聚合单聊最新一条消息 + 未读 + 对方资料/在线状态)
*/
public function conversations()
{
$uid = $this->auth->model->uid;
$msgs = WxchatMessage::where('receiver_type', 0)
->where('is_recalled', 0)
->where(function ($q) use ($uid) {
$q->where('sender_id', $uid)->whereOr('receiver_id', $uid);
})
->order('id', 'desc')
->limit(1000)
->select();
$convMap = [];
$peerIds = [];
foreach ($msgs as $m) {
$sid = (int) $m->sender_id;
$rid = (int) $m->receiver_id;
$peer = ($sid == $uid) ? $rid : $sid;
if (! isset($convMap[$peer])) {
$convMap[$peer] = [
'peer_id' => $peer,
'last_content' => $m->content,
'last_content_type' => (int) $m->content_type,
'last_time' => (int) $m->create_at,
'last_sender_id' => $sid,
];
$peerIds[] = $peer;
}
}
if (empty($peerIds)) {
$this->success(['list' => [], 'total' => 0]);
return;
}
$users = UserModel::where('uid', 'in', $peerIds)->column('*', 'uid');
$unread = WxchatUnreadCount::where('user_id', $uid)
->where('contact_type', 0)
->where('contact_id', 'in', $peerIds)
->column('unread_count', 'contact_id');
$onlineMap = MemberProfile::where('uid', 'in', $peerIds)->column('online_status', 'uid');
$list = [];
foreach ($convMap as $peer => $c) {
$u = $users[$peer] ?? [];
$list[] = [
'peer_id' => $peer,
'nickname' => $u['nickname'] ?? '',
'avatar' => $u['avatar'] ?? '',
'online' => ((int) ($onlineMap[$peer] ?? 0)) == 1,
'last_content' => $c['last_content'],
'last_content_type' => $c['last_content_type'],
'last_time' => $c['last_time'],
'unread_count' => (int) ($unread[$peer] ?? 0),
'is_self' => $c['last_sender_id'] == $uid,
];
}
usort($list, function ($a, $b) {
return $b['last_time'] - $a['last_time'];
});
$this->success(['list' => $list, 'total' => count($list)]);
}
/**
* 撤回消息
*/
public function recall()
{
$userId = $this->auth->model->uid;
$messageId = (int) Request::post('message_id', 0);
$message = WxchatMessage::find($messageId);
if (! $message || $message->sender_id != $userId) {
$this->error('无权撤回', 403);
}
$message->is_recalled = 1;
$message->recalled_at = date('Y-m-d H:i:s');
$message->save();
$this->success([], '撤回成功');
}
}