1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-07-03 07:26:49 +08:00

设计消息盒子

This commit is contained in:
xiaochong0302 2020-06-25 19:34:14 +08:00
parent 7c2d87c411
commit eb0d013fec
12 changed files with 364 additions and 196 deletions

View File

@ -235,6 +235,30 @@ class MessengerController extends LayerController
return $this->jsonSuccess($content);
}
/**
* @Post("/friend/accept", name="web.im.accept_friend")
*/
public function acceptFriendAction()
{
$service = new MessengerService();
$service->acceptFriend();
return $this->jsonSuccess();
}
/**
* @Post("/friend/refuse", name="web.im.refuse_friend")
*/
public function refuseFriendAction()
{
$service = new MessengerService();
$service->refuseFriend();
return $this->jsonSuccess();
}
/**
* @Post("/group/apply", name="web.im.apply_group")
*/
@ -249,14 +273,6 @@ class MessengerController extends LayerController
return $this->jsonSuccess($content);
}
/**
* @Post("/friend/accept", name="web.im.accept_friend")
*/
public function acceptFriendAction()
{
}
/**
* @Post("/group/accept", name="web.web.im.accept_group")
*/

View File

@ -6,6 +6,7 @@ use App\Builders\ImMessageList as ImMessageListBuilder;
use App\Caches\ImHotGroupList as ImHotGroupListCache;
use App\Caches\ImHotUserList as ImHotUserListCache;
use App\Library\Paginator\Query as PagerQuery;
use App\Models\ImFriendGroup as ImFriendGroupModel;
use App\Models\ImFriendMessage as ImFriendMessageModel;
use App\Models\ImFriendUser as ImFriendUserModel;
use App\Models\ImGroupMessage as ImGroupMessageModel;
@ -236,13 +237,7 @@ class Messenger extends Service
}
}
/**
* @todo 发送未读消息
*/
/**
* @todo 发送盒子消息
*/
$this->pullUnreadFriendMessages($user->id);
}
public function sendMessage()
@ -375,63 +370,88 @@ class Messenger extends Service
$validator->checkIfJoined($user->id, $friend->id);
$validator->checkIfBlocked($user->id, $friend->id);
$friendUserRepo = new ImFriendUserRepo();
$friendUser = $friendUserRepo->findFriendUser($user->id, $friend->id);
if (!$friendUser) {
$model = new ImFriendUserModel();
$model->user_id = $user->id;
$model->friend_id = $friend->id;
$model->group_id = $group->id;
$model->create();
} else {
$friendUser->group_id = $group->id;
$friendUser->update();
}
$this->handleApplyFriendNotice($user, $friend, $remark);
$this->handleApplyFriendNotice($user, $friend, $group, $remark);
}
public function acceptFriend()
{
$post = $this->request->getPost();
$user = $this->getLoginUser();
$messageId = $this->request->getPost('message_id');
$groupId = $this->request->getPost('group_id');
$validator = new ImFriendUserValidator();
$friend = $validator->checkFriend($post['friend_id']);
$group = $validator->checkGroup($post['group_id']);
$validator->checkGroup($groupId);
$validator = new ImMessageValidator();
$message = $validator->checkMessage($messageId, 'system');
if ($message->item_type != ImSystemMessageModel::TYPE_FRIEND_REQUEST) {
return;
}
$userRepo = new UserRepo();
$sender = $userRepo->findById($message->sender_id);
$friendUserRepo = new ImFriendUserRepo();
$friendUser = $friendUserRepo->findFriendUser($user->id, $friend->id);
$friendUser = $friendUserRepo->findFriendUser($user->id, $sender->id);
$friendUserModel = new ImFriendUserModel();
if (!$friendUser) {
$model = new ImFriendUserModel();
$model->user_id = $user->id;
$model->friend_id = $friend->id;
$model->group_id = $group->id;
$model->create();
$friendUserModel->create([
'user_id' => $user->id,
'friend_id' => $sender->id,
'group_id' => $groupId,
]);
}
$this->handleAcceptFriendNotice();
$friendUser = $friendUserRepo->findFriendUser($sender->id, $user->id);
$groupId = $message->item_info['group']['id'] ?: 0;
if (!$friendUser) {
$friendUserModel->create([
'user_id' => $user->id,
'friend_id' => $sender->id,
'group_id' => $groupId,
]);
}
$itemInfo = $message->item_info;
$itemInfo['status'] = ImSystemMessageModel::REQUEST_ACCEPTED;
$message->update(['item_info' => $itemInfo]);
$this->handleAcceptFriendNotice($user, $sender);
}
public function refuseFriend()
{
$friendId = $this->request->getPost('friend_id');
$user = $this->getLoginUser();
$userValidator = new UserValidator();
$messageId = $this->request->getPost('message_id');
$friend = $userValidator->checkUser($friendId);
$validator = new ImMessageValidator();
/**
* @todo 向对方发拒绝添加好友的系统消息
*/
$message = $validator->checkMessage($messageId, 'system');
if ($message->item_type != ImSystemMessageModel::TYPE_FRIEND_REQUEST) {
return;
}
$itemInfo = $message->item_info;
$itemInfo['status'] = ImSystemMessageModel::REQUEST_REFUSED;
$message->update(['item_info' => $itemInfo]);
$userRepo = new UserRepo();
$sender = $userRepo->findById($message->sender_id);
$this->handleRefuseFriendNotice($user, $sender);
}
public function applyGroup()
@ -446,6 +466,44 @@ class Messenger extends Service
{
}
protected function pullUnreadFriendMessages($userId)
{
$userRepo = new UserRepo();
$messages = $userRepo->findUnreadImFriendMessages($userId);
if ($messages->count() == 0) {
return;
}
$builder = new ImMessageListBuilder();
$senders = $builder->getSenders($messages->toArray());
foreach ($messages as $message) {
$message->update(['viewed' => 1]);
$sender = $senders[$message->sender_id];
$content = kg_json_encode([
'type' => 'show_chat_msg',
'message' => [
'username' => $sender['name'],
'avatar' => $sender['avatar'],
'content' => $message->content,
'fromid' => $sender['id'],
'id' => $sender['id'],
'timestamp' => 1000 * $message->create_time,
'type' => 'friend',
'mine' => false,
],
]);
Gateway::sendToUid($userId, $content);
}
}
protected function handleFriendList($userId)
{
$userRepo = new UserRepo();
@ -621,7 +679,7 @@ class Messenger extends Service
return $pager;
}
protected function handleApplyFriendNotice(UserModel $sender, UserModel $receiver, $remark)
protected function handleApplyFriendNotice(UserModel $sender, UserModel $receiver, ImFriendGroupModel $group, $remark)
{
$userRepo = new UserRepo();
@ -630,34 +688,59 @@ class Messenger extends Service
$message = $userRepo->findImSystemMessage($receiver->id, $itemType);
if ($message) {
/**
* 请求未过期
*/
if (time() - $message->create_time < 3 * 86400) {
return;
}
if ($message->item_type['accepted'] == 1) {
$expired = time() - $message->create_time > 7 * 86400;
$pending = $message->item_type['status'] == ImSystemMessageModel::REQUEST_PENDING;
if (!$expired && $pending) {
return;
}
}
$senderInfo = [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
];
$sysMsgModel = new ImSystemMessageModel();
$sysMsgModel->sender_id = $sender->id;
$sysMsgModel->receiver_id = $receiver->id;
$sysMsgModel->item_type = ImSystemMessageModel::TYPE_FRIEND_REQUEST;
$sysMsgModel->item_info = [
'sender' => $senderInfo,
'sender' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
],
'group' => [
'id' => $group->id,
'name' => $group->name,
],
'remark' => $remark,
'accepted' => 0,
'status' => ImSystemMessageModel::REQUEST_PENDING,
];
$sysMsgModel->create();
Gateway::$registerAddress = '127.0.0.1:1238';
$online = Gateway::isUidOnline($receiver->id);
if ($online) {
$content = kg_json_encode(['type' => 'refresh_sys_msg']);
Gateway::sendToUid($receiver->id, $content);
}
}
protected function handleAcceptFriendNotice(UserModel $sender, UserModel $receiver)
{
$sysMsgModel = new ImSystemMessageModel();
$sysMsgModel->sender_id = $sender->id;
$sysMsgModel->receiver_id = $receiver->id;
$sysMsgModel->item_type = ImSystemMessageModel::TYPE_FRIEND_ACCEPTED;
$sysMsgModel->item_info = [
'sender' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
]
];
$sysMsgModel->create();
@ -674,42 +757,35 @@ class Messenger extends Service
}
}
protected function handleAcceptFriendNotice(UserModel $user, UserModel $friend)
protected function handleRefuseFriendNotice(UserModel $sender, UserModel $receiver)
{
$sysMsgModel = new ImSystemMessageModel();
$sysMsgModel->user_id = $friend->id;
$sysMsgModel->item_id = $user->id;
$sysMsgModel->item_type = ImSystemMessageModel::TYPE_FRIEND_APPROVED;
$sysMsgModel->sender_id = $sender->id;
$sysMsgModel->receiver_id = $receiver->id;
$sysMsgModel->item_type = ImSystemMessageModel::TYPE_FRIEND_REFUSED;
$sysMsgModel->item_info = [
'user' => ['id' => $user->id, 'name' => $user->name, 'avatar' => $user->avatar],
'sender' => [
'id' => $sender->id,
'name' => $sender->name,
'avatar' => $sender->avatar,
]
];
$sysMsgModel->create();
Gateway::$registerAddress = '127.0.0.1:1238';
$online = Gateway::isUidOnline($friend->id);
$online = Gateway::isUidOnline($receiver->id);
if ($online) {
$userRepo = new UserRepo();
$content = kg_json_encode(['type' => 'show_msg_box']);
$msgCount = $userRepo->countUnreadImSystemMessages($friend->id);
$message = kg_json_encode([
'type' => 'show_msg_box',
'content' => ['msg_count' => $msgCount],
]);
Gateway::sendToUid($friend->id, $message);
Gateway::sendToUid($receiver->id, $content);
}
}
protected function handleRefuseFriendNotice()
{
}
protected function getGroupName($groupId)
{
return "group_{$groupId}";

View File

@ -3,7 +3,7 @@
{% block content %}
<div class="im-search">
<form class="layui-form" method="get" action="{{ url({'for':'web.im.find'}) }}">
<form class="layui-form" method="get" action="{{ url({'for':'web.im.search'}) }}">
<input class="layui-input" type="text" name="query" placeholder="请输入关键字...">
<button class="layui-hide" type="submit" lay-submit="true" lay-filter="im_search">搜索</button>
</form>

View File

@ -9,7 +9,7 @@
</div>
<div class="name layui-elip" title="{{ item.name|e }}">{{ item.name }}</div>
<div class="action">
<a href="javascript:" class="layui-badge-rim apply-group" data-id="{{ item.id }}">加入群组</a>
<a href="javascript:" class="layui-badge-rim apply-group" data-id="{{ item.id }}" data-name="{{ item.name }}" data-avatar="{{ item.avatar }}">加入群组</a>
</div>
</div>
</div>

View File

@ -1,20 +1,73 @@
{% if pager.total_pages > 0 %}
<div class="im-user-list clearfix">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
<div class="layui-col-md2">
<div class="user-card" title="{{ item.about|e }}">
<div class="avatar">
<a href="javascript:"><img src="{{ item.avatar }}" alt="{{ item.name }}"></a>
</div>
<div class="name layui-elip" title="{{ item.name|e }}">{{ item.name }}</div>
<div class="action">
<a href="javascript:" class="layui-badge-rim apply-group" data-id="{{ item.id }}">加入群组</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{{ partial('partials/pager_ajax') }}
{%- macro message_info(item) %}
{% set item_type = item.item_type %}
{% set item_info = item.item_info %}
{% if item_type == '1' %}
{% set sender = item_info.sender %}
{% set group = item_info.group %}
{% set remark = item_info.remark ? '附言:' ~ item_info.remark : '' %}
<li data-id="{{ item.id }}">
<a href="#" target="_blank"><img src="{{ sender.avatar }}" class="layui-circle layim-msgbox-avatar"></a>
<p class="layim-msgbox-user" data-id="{{ sender.id }}" data-name="{{ sender.name }}" data-avatar="{{ sender.avatar }}" data-group="{{ group.id }}">
<a href="#" target="_blank">{{ sender.name }}</a>
<span>{{ date('Y-m-d H:i:s',item.create_time) }}</span>
</p>
<p class="layim-msgbox-content">申请添加你为好友 <span>{{ remark }}</span></p>
<p class="layim-msgbox-btn">
{% if item_info.status == 'pending' %}
<button class="layui-btn layui-btn-small" data-type="acceptFriend">接受</button>
<button class="layui-btn layui-btn-small layui-btn-primary" data-type="refuseFriend">拒绝</button>
{% elseif item_info.status == 'accepted' %}
已同意
{% elseif item_info.status == 'refused' %}
已拒绝
{% endif %}
</p>
</li>
{% elseif item_type == '2' %}
<li class="layim-msgbox-system">
<p><em>系统:</em>{{ item_info.sender.name }} 接受了你的好友申请<span>{{ date('Y-m-d H:i:s',item.create_time) }}</span></p>
</li>
{% elseif item_type == '3' %}
<li class="layim-msgbox-system">
<p><em>系统:</em>{{ item_info.sender.name }} 拒绝了你的好友申请<span>{{ date('Y-m-d H:i:s',item.create_time) }}</span></p>
</li>
{% elseif item_type == '4' %}
{% set remark = item_info.remark ? '附言:' ~ item_info.remark : '' %}
<li data-id="{{ item.id }}">
<a href="#" target="_blank"><img src="{{ item_info.sender.avatar }}" class="layui-circle layim-msgbox-avatar"></a>
<p class="layim-msgbox-user">
<a href="#" target="_blank">{{ item_info.sender.name }}</a>
<span>{{ date('Y-m-d H:i:s',item.create_time) }}</span>
</p>
<p class="layim-msgbox-content">申请加入群组 <span>{{ remark }}</span></p>
<p class="layim-msgbox-btn">
{% if item_info.status == 'pending' %}
<button class="layui-btn layui-btn-small" data-type="acceptGroup">接受</button>
<button class="layui-btn layui-btn-small layui-btn-primary" data-type="refuseGroup">拒绝</button>
{% elseif item_info.status == 'accepted' %}
已同意
{% elseif item_info.status == 'refused' %}
已拒绝
{% endif %}
</p>
</li>
{% elseif item_type == '5' %}
<li class="layim-msgbox-system">
<p><em>系统:</em>{{ item_info.sender.name }} 接受了你的入群申请<span>{{ date('Y-m-d H:i:s',item.create_time) }}</span></p>
</li>
{% elseif item_type == '6' %}
<li class="layim-msgbox-system">
<p><em>系统:</em>{{ item_info.sender.name }} 拒绝了你的入群申请<span>{{ date('Y-m-d H:i:s',item.create_time) }}</span></p>
</li>
{% endif %}
{%- endmacro %}
{% if pager.items %}
<ul class="layim-msgbox">
{% for item in pager.items %}
{{ message_info(item) }}
{% endfor %}
</ul>
{% endif %}

View File

@ -7,12 +7,22 @@ use Phalcon\Mvc\Model\Behavior\SoftDelete;
class ImSystemMessage extends Model
{
const TYPE_FRIEND_REQUEST = 1;
const TYPE_GROUP_REQUEST = 2;
const TYPE_FRIEND_ACCEPTED = 3;
const TYPE_FRIEND_REFUSED = 4;
const TYPE_GROUP_ACCEPTED = 5;
const TYPE_GROUP_REFUSED = 6;
/**
* 请求状态
*/
const REQUEST_PENDING = 'pending'; // 待处理
const REQUEST_ACCEPTED = 'accepted'; // 已接受
const REQUEST_REFUSED = 'refused'; // 已拒绝
/**
* 消息类型
*/
const TYPE_FRIEND_REQUEST = 1; // 好友请求
const TYPE_FRIEND_ACCEPTED = 2; // 好友被接受
const TYPE_FRIEND_REFUSED = 3; // 好友被拒绝
const TYPE_GROUP_REQUEST = 4; // 入群请求
const TYPE_GROUP_ACCEPTED = 5; // 入群被接受
const TYPE_GROUP_REFUSED = 6; // 入群被拒绝
/**
* 主键编号
@ -115,6 +125,10 @@ class ImSystemMessage extends Model
public function beforeUpdate()
{
$this->update_time = time();
if (is_array($this->item_info) && !empty($this->item_info)) {
$this->item_info = kg_json_encode($this->item_info);
}
}
public function afterFetch()

View File

@ -6,6 +6,7 @@ use App\Library\Paginator\Adapter\QueryBuilder as PagerQueryBuilder;
use App\Models\ImChatGroup as ImChatGroupModel;
use App\Models\ImChatGroupUser as ImChatGroupUserModel;
use App\Models\ImFriendGroup as ImFriendGroupModel;
use App\Models\ImFriendMessage as ImFriendMessageModel;
use App\Models\ImFriendUser as ImFriendUserModel;
use App\Models\ImSystemMessage as ImSystemMessageModel;
use App\Models\User as UserModel;
@ -157,6 +158,18 @@ class User extends Repository
->getQuery()->execute();
}
/**
* @param int $userId
* @return ResultsetInterface|Resultset|ImFriendMessageModel[]
*/
public function findUnreadImFriendMessages($userId)
{
return ImFriendMessageModel::find([
'conditions' => 'receiver_id = ?1 AND viewed = ?2',
'bind' => [1 => $userId, 2 => 0],
]);
}
/**
* @param int $userId
* @param int $itemType
@ -173,7 +186,7 @@ class User extends Repository
/**
* @param int $userId
* @return ResultsetInterface|Resultset|ImSystemMessageModel[]
* @return ResultsetInterface|Resultset|ImFriendMessageModel[]
*/
public function findUnreadImSystemMessages($userId)
{

View File

@ -56,6 +56,8 @@ class ImFriendUser extends Validator
if ($length > 30) {
throw new BadRequestException('im_friend_user.remark_too_long');
}
return $remark;
}
public function checkIfSelfApply($userId, $friendId)

View File

@ -7,6 +7,7 @@ use App\Caches\User as UserCache;
use App\Exceptions\BadRequest as BadRequestException;
use App\Repos\ImFriendMessage as ImFriendMessageRepo;
use App\Repos\ImGroupMessage as ImGroupMessageRepo;
use App\Repos\ImSystemMessage as ImSystemMessageRepo;
class ImMessage extends Validator
{
@ -23,6 +24,9 @@ class ImMessage extends Validator
} elseif ($type == 'group') {
$repo = new ImGroupMessageRepo();
$message = $repo->findById($id);
} elseif ($type == 'system') {
$repo = new ImSystemMessageRepo();
$message = $repo->findById($id);
}
if (!$message) {
@ -57,7 +61,7 @@ class ImMessage extends Validator
public function checkType($type)
{
if (!in_array($type, ['friend', 'group'])) {
if (!in_array($type, ['friend', 'group', 'system'])) {
throw new BadRequestException('im_message.invalid_type');
}

View File

@ -32,29 +32,42 @@ layui.use(['jquery', 'form', 'layer', 'layim'], function () {
},
success: function (res) {
layer.msg(res.msg, {icon: 1});
layer.close(index);
},
error: function (xhr) {
var res = JSON.parse(xhr.responseText);
layer.msg(res.msg, {icon: 2});
}
});
layer.close(index);
}
});
});
$('body').on('click', '.apply-group', function () {
var groupId = $(this).attr('data-id');
$.ajax({
type: 'POST',
url: '/im/group/apply',
data: {group_id: groupId},
success: function (res) {
layer.msg(res.msg, {icon: 1});
},
error: function (xhr) {
var res = JSON.parse(xhr.responseText);
layer.msg(res.msg, {icon: 2});
var groupName = $(this).attr('data-name');
var avatar = $(this).attr('data-avatar');
layim.add({
type: 'group',
groupname: groupName,
avatar: avatar,
submit: function (group, remark, index) {
$.ajax({
type: 'POST',
url: '/im/group/apply',
data: {
group_id: groupId,
remark: remark
},
success: function (res) {
layer.msg(res.msg, {icon: 1});
layer.close(index);
},
error: function (xhr) {
var res = JSON.parse(xhr.responseText);
layer.msg(res.msg, {icon: 2});
}
});
}
});
});

View File

@ -23,11 +23,11 @@ layui.use(['jquery', 'layim'], function () {
socket.send('pong...');
} else if (data.type === 'bind_user') {
bindUser(data.client_id);
refreshMessageBox();
refreshSystemMessage();
} else if (data.type === 'show_chat_msg') {
showChatMessage(data.message);
} else if (data.type === 'show_msg_box') {
refreshMessageBox();
} else if (data.type === 'refresh_sys_msg') {
refreshSystemMessage();
}
};
@ -83,7 +83,7 @@ layui.use(['jquery', 'layim'], function () {
layim.getMessage(message);
}
function refreshMessageBox() {
function refreshSystemMessage() {
$.ajax({
type: 'GET',
url: '/im/msg/unread/count',

View File

@ -47,86 +47,63 @@ layui.use(['jquery', 'layer', 'layim', 'laypage'], function () {
}
//操作
var active = {
//同意
agree: function (othis) {
var li = othis.parents('li')
, uid = li.data('uid')
, from_group = li.data('fromGroup')
, user = cache[uid];
var action = {
acceptFriend: function (othis) {
var li = othis.parents('li');
var sender = li.find('.layim-msgbox-user');
//选择分组
parent.layui.layim.setFriendGroup({
type: 'friend'
, username: user.username
, avatar: user.avatar
, group: parent.layui.layim.cache().friend //获取好友分组数据
, submit: function (group, index) {
//将好友追加到主面板
parent.layui.layim.addList({
type: 'friend'
, avatar: user.avatar //好友头像
, username: user.username //好友昵称
, groupid: group //所在的分组id
, id: uid //好友ID
, sign: user.sign //好友签名
type: 'friend',
username: sender.data('name'),
avatar: sender.data('avatar'),
group: parent.layui.layim.cache().friend,
submit: function (group, index) {
$.ajax({
type: 'POST',
url: '/im/friend/accept',
data: {
message_id: li.data('id'),
group_id: group
},
success: function () {
//将好友追加到主面板
parent.layui.layim.addList({
type: 'friend',
username: sender.data('name'),
avatar: sender.data('avatar'),
id: sender.data('id'),
groupid: group,
});
othis.parent().html('已同意');
parent.layer.close(index);
}
});
parent.layer.close(index);
othis.parent().html('已同意');
//实际部署时,请开启下述注释,并改成你的接口地址
/*
$.post('/im/agreeFriend', {
uid: uid //对方用户ID
,from_group: from_group //对方设定的好友分组
,group: group //我设定的好友分组
}, function(res){
if(res.code != 0){
return layer.msg(res.msg);
}
//将好友追加到主面板
parent.layui.layim.addList({
type: 'friend'
,avatar: user.avatar //好友头像
,username: user.username //好友昵称
,groupid: group //所在的分组id
,id: uid //好友ID
,sign: user.sign //好友签名
});
parent.layer.close(index);
othis.parent().html('已同意');
});
*/
}
});
}
//拒绝
, refuse: function (othis) {
var li = othis.parents('li')
, uid = li.data('uid');
},
refuseFriend: function (othis) {
var li = othis.parents('li');
layer.confirm('确定拒绝吗?', function (index) {
$.post('/im/refuseFriend', {
uid: uid //对方用户ID
}, function (res) {
if (res.code != 0) {
return layer.msg(res.msg);
$.ajax({
type: 'POST',
url: '/im/friend/refuse',
data: {message_id: li.data('id')},
success: function () {
layer.close(index);
othis.parent().html('<em>已拒绝</em>');
}
layer.close(index);
othis.parent().html('<em>已拒绝</em>');
});
});
},
acceptGroup: function (othis) {
},
refuseGroup: function (othis) {
}
};
$('body').on('click', '.layui-btn', function () {
var othis = $(this), type = othis.data('type');
active[type] ? active[type].call(this, othis) : '';
action[type] ? action[type].call(this, othis) : '';
});
});