基于“东莞梦幻网络科技”体育赛事直播系统,展示前后端技术(PHP ThinkPHP + Vue.js + Android Java + iOS OC)实现的“用户与用户之间私聊”完整方案,包括功能描述、界面效果、技术实现、数据结构、接口设计及关键代码示例。
1、用户与用户之间一对一私聊。
2、显示聊天记录、发送时间。
3、未读消息红点提示。
4、消息免打扰(可切换)。
5、聊天窗口置顶。
6、删除聊天、清空聊天记录。
7、发送消息、图片、表情等。
1、后端: PHP (ThinkPHP)
3、实时通信: WebSocket + Redis(用于消息推送 & 未读消息计数)
4、前端(H5/PC): Vue.js
5、移动端: Java (Android)、Objective-C (iOS)
1、用户表 users
CREATE TABLE `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(50),
`avatar` VARCHAR(255),
`status` TINYINT DEFAULT 1
);
2、聊天消息表 chat_messages
CREATE TABLE `chat_messages` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`sender_id` INT,
`receiver_id` INT,
`content` TEXT,
`send_time` DATETIME,
`is_read` TINYINT DEFAULT 0,
`is_deleted` TINYINT DEFAULT 0
);
3、聊天设置表 chat_settings
CREATE TABLE `chat_settings` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`user_id` INT,
`chat_user_id` INT,
`is_top` TINYINT DEFAULT 0,
`no_disturb` TINYINT DEFAULT 0
);
1、获取聊天列表
public function getChatList($userId)
{
$chats = ChatModel::getChatListByUser($userId); // 关联读取chat_settings、last_message
return json(['status' => 1, 'data' => $chats]);
}
2、获取聊天记录
public function getMessages(Request $request)
{
$userId = $request->param('user_id');
$peerId = $request->param('peer_id');
$messages = ChatMessageModel::getMessages($userId, $peerId);
return json(['status' => 1, 'data' => $messages]);
}
3、发送消息
public function sendMessage(Request $request)
{
$data = $request->post();
ChatMessageModel::send($data['sender_id'], $data['receiver_id'], $data['content']);
// WebSocket推送
WebSocketService::pushToUser($data['receiver_id'], $data);
return json(['status' => 1, 'msg' => '发送成功']);
}
1、聊天列表组件
<template>
<div class="chat-list">
<div v-for="chat in chatList" :key="chat.user_id" class="chat-item">
<div class="avatar"><img :src="chat.avatar"></div>
<div class="info">
<div class="name">{{ chat.username }}</div>
<div class="last-msg">{{ chat.last_message }}</div>
<div class="time">{{ chat.last_time }}</div>
<div v-if="chat.unread_count > 0" class="red-dot">{{ chat.unread_count }}</div>
</div>
<el-dropdown>
<span class="el-dropdown-link">···</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="setTop(chat)">置顶聊天</el-dropdown-item>
<el-dropdown-item @click.native="deleteChat(chat)">删除聊天</el-dropdown-item>
<el-dropdown-item @click.native="clearMessages(chat)">清空消息</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
2、聊天窗口
<template>
<div class="chat-window">
<div class="header">
{{ targetUser.username }}
<el-switch v-model="noDisturb" @change="toggleNoDisturb">消息免打扰</el-switch>
</div>
<div class="chat-content">
<div v-for="msg in messages" :class="{'mine': msg.sender_id === userId}">
<div class="message">{{ msg.content }}</div>
<div class="time">{{ msg.send_time }}</div>
</div>
</div>
<div class="input-area">
<el-input v-model="input" @keyup.enter="sendMessage" placeholder="请输入消息..." />
</div>
</div>
</template>
public class ChatListActivity extends AppCompatActivity {
private RecyclerView chatListView;
private ChatListAdapter adapter;
private List<ChatItem> chatItems = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat_list);
chatListView = findViewById(R.id.chat_list_view);
chatListView.setLayoutManager(new LinearLayoutManager(this));
adapter = new ChatListAdapter(chatItems, new ChatListAdapter.OnItemClickListener() {
@Override
public void onItemClick(ChatItem item) {
openChatWindow(item);
}
@Override
public void onMoreClick(ChatItem item, View view) {
showChatMenu(item, view);
}
});
chatListView.setAdapter(adapter);
loadChatList();
}
private void loadChatList() {
// 调用API获取聊天列表
ApiService.getInstance().getChatList(new ApiCallback<List<ChatItem>>() {
@Override
public void onSuccess(List<ChatItem> result) {
chatItems.clear();
chatItems.addAll(result);
adapter.notifyDataSetChanged();
}
@Override
public void onFailure(String error) {
Toast.makeText(ChatListActivity.this, error, Toast.LENGTH_SHORT).show();
}
});
}
private void openChatWindow(ChatItem item) {
Intent intent = new Intent(this, ChatActivity.class);
intent.putExtra("chat_id", item.getId());
intent.putExtra("user_name", item.getUsername());
startActivity(intent);
}
private void showChatMenu(ChatItem item, View anchorView) {
PopupMenu popupMenu = new PopupMenu(this, anchorView);
popupMenu.getMenuInflater().inflate(R.menu.chat_item_menu, popupMenu.getMenu());
// 设置菜单项状态
popupMenu.getMenu().findItem(R.id.menu_top).setTitle(item.isTop() ? "取消置顶" : "置顶");
popupMenu.getMenu().findItem(R.id.menu_mute).setTitle(item.isMuted() ? "关闭免打扰" : "消息免打扰");
popupMenu.setOnMenuItemClickListener(menuItem -> {
switch (menuItem.getItemId()) {
case R.id.menu_top:
toggleTopChat(item, !item.isTop());
return true;
case R.id.menu_mute:
toggleMuteChat(item, !item.isMuted());
return true;
case R.id.menu_delete:
deleteChat(item);
return true;
case R.id.menu_clear:
clearMessages(item);
return true;
default:
return false;
}
});
popupMenu.show();
}
private void toggleTopChat(ChatItem item, boolean isTop) {
ApiService.getInstance().topChat(item.getId(), isTop, new ApiCallback<Void>() {
@Override
public void onSuccess(Void result) {
loadChatList();
}
@Override
public void onFailure(String error) {
Toast.makeText(ChatListActivity.this, error, Toast.LENGTH_SHORT).show();
}
});
}
private void toggleMuteChat(ChatItem item, boolean isMuted) {
ApiService.getInstance().muteChat(item.getId(), isMuted, new ApiCallback<Void>() {
@Override
public void onSuccess(Void result) {
loadChatList();
}
@Override
public void onFailure(String error) {
Toast.makeText(ChatListActivity.this, error, Toast.LENGTH_SHORT).show();
}
});
}
private void deleteChat(ChatItem item) {
new AlertDialog.Builder(this)
.setTitle("删除聊天")
.setMessage("确定要删除此聊天记录吗?")
.setPositiveButton("确定", (dialog, which) -> {
ApiService.getInstance().deleteChat(item.getId(), new ApiCallback<Void>() {
@Override
public void onSuccess(Void result) {
loadChatList();
}
@Override
public void onFailure(String error) {
Toast.makeText(ChatListActivity.this, error, Toast.LENGTH_SHORT).show();
}
});
})
.setNegativeButton("取消", null)
.show();
}
private void clearMessages(ChatItem item) {
new AlertDialog.Builder(this)
.setTitle("清空消息")
.setMessage("确定要清空此聊天中的所有消息吗?")
.setPositiveButton("确定", (dialog, which) -> {
ApiService.getInstance().clearMessages(item.getId(), new ApiCallback<Void>() {
@Override
public void onSuccess(Void result) {
loadChatList();
}
@Override
public void onFailure(String error) {
Toast.makeText(ChatListActivity.this, error, Toast.LENGTH_SHORT).show();
}
});
})
.setNegativeButton("取消", null)
.show();
}
}
#import "ChatListViewController.h"
#import "ChatItem.h"
#import "ChatViewController.h"
#import "ApiService.h"
@interface ChatListViewController ()
@end
@implementation ChatListViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"私聊";
self.chatItems = [NSMutableArray array];
// 设置表格视图
self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"ChatCell"];
[self.view addSubview:self.tableView];
// 加载数据
[self loadChatList];
}
- (void)loadChatList {
[[ApiService sharedInstance] getChatListWithCompletion:^(NSArray *chatItems, NSError *error) {
if (error) {
NSLog(@"Error loading chat list: %@", error.localizedDescription);
return;
}
[self.chatItems removeAllObjects];
[self.chatItems addObjectsFromArray:chatItems];
[self.tableView reloadData];
}];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.chatItems.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ChatCell" forIndexPath:indexPath];
ChatItem *chatItem = self.chatItems[indexPath.row];
// 配置单元格
cell.textLabel.text = chatItem.username;
cell.detailTextLabel.text = chatItem.lastMessage;
cell.imageView.image = [UIImage imageNamed:chatItem.avatar ?: @"default_avatar"];
// 显示未读消息数
if (chatItem.unreadCount > 0) {
UILabel *badge = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
badge.text = [NSString stringWithFormat:@"%ld", (long)chatItem.unreadCount];
badge.textColor = [UIColor whiteColor];
badge.backgroundColor = [UIColor redColor];
badge.textAlignment = NSTextAlignmentCenter;
badge.layer.cornerRadius = 10;
badge.layer.masksToBounds = YES;
cell.accessoryView = badge;
} else {
cell.accessoryView = nil;
}
// 置顶聊天背景色
if (chatItem.isTop) {
cell.backgroundColor = [UIColor colorWithRed:0.9 green:0.95 blue:1.0 alpha:1.0];
} else {
cell.backgroundColor = [UIColor whiteColor];
}
return cell;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
ChatItem *chatItem = self.chatItems[indexPath.row];
[self openChatWithChatItem:chatItem];
}
- (NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
ChatItem *chatItem = self.chatItems[indexPath.row];
// 置顶/取消置顶
UITableViewRowAction *topAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal
title:chatItem.isTop ? @"取消置顶" : @"置顶"
handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
[self toggleTopChat:chatItem];
}];
topAction.backgroundColor = [UIColor blueColor];
// 删除
UITableViewRowAction *deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive
title:@"删除"
handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
[self deleteChat:chatItem];
}];
return @[deleteAction, topAction];
}
- (void)openChatWithChatItem:(ChatItem *)chatItem {
ChatViewController *chatVC = [[ChatViewController alloc] init];
chatVC.chatId = chatItem.chatId;
chatVC.chatTitle = chatItem.username;
[self.navigationController pushViewController:chatVC animated:YES];
}
- (void)toggleTopChat:(ChatItem *)chatItem {
[[ApiService sharedInstance] topChat:chatItem.chatId isTop:!chatItem.isTop completion:^(BOOL success, NSError *error) {
if (success) {
[self loadChatList];
} else {
NSLog(@"Error toggling top chat: %@", error.localizedDescription);
}
}];
}
- (void)deleteChat:(ChatItem *)chatItem {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"删除聊天"
message:@"确定要删除此聊天记录吗?"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[[ApiService sharedInstance] deleteChat:chatItem.chatId completion:^(BOOL success, NSError *error) {
if (success) {
[self loadChatList];
} else {
NSLog(@"Error deleting chat: %@", error.localizedDescription);
}
}];
}]];
[self presentViewController:alert animated:YES completion:nil];
}
@end
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有