开发体育赛事直播系统:实现聊天交友的私聊功能技术实现全方案解析

简介: 本文基于体育赛事直播系统,详细介绍了用户间私聊功能的完整实现方案。技术栈涵盖后端(PHP ThinkPHP)、前端(Vue.js)、移动端(Android Java、iOS OC),并结合MySQL数据库与WebSocket+Redis实现实时通信。功能包括一对一私聊、聊天记录显示、未读消息提示、消息免打扰、聊天置顶、删除/清空聊天记录等。文章提供了数据结构设计、接口代码示例及前后端关键实现细节,适合开发者学习参考。

基于“东莞梦幻网络科技”体育赛事直播系统,展示前后端技术(PHP ThinkPHP + Vue.js + Android Java + iOS OC)实现的“用户与用户之间私聊”完整方案,包括功能描述、界面效果、技术实现、数据结构、接口设计及关键代码示例。
聊一聊-私聊.png

一、私聊聊天功能模块包含:

1、用户与用户之间一对一私聊。
2、显示聊天记录、发送时间。
3、未读消息红点提示。
4、消息免打扰(可切换)。
5、聊天窗口置顶。
6、删除聊天、清空聊天记录。
7、发送消息、图片、表情等。

二、技术实现方案技术栈:

1、后端: PHP (ThinkPHP)
2、数据库: MySQL
3、实时通信: WebSocket + Redis(用于消息推送 & 未读消息计数)
4、前端(H5/PC): Vue.js
5、移动端: Java (Android)、Objective-C (iOS)

三、数据结构设计(MySQL)

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
);

四、后端接口设计(ThinkPHP)

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' => '发送成功']);
}

五、Vue 前端页面实现(关键代码)

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>

六、Android Java 实现(部分代码)

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();
    }
}

七、iOS Objective-C 实现(部分代码)

#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
相关文章
|
存储 弹性计算 开发工具
基于OSS搭建云上个人博客
阿里云OSS,可以托管静态网站,在这个实验中,我们将以Hexo静态博客为例,向您演示如何通过Hexo博客框架快速生成静态博客,以及如何使用Hexo插件将生成的静态博客自动部署至阿里云OSS。
|
数据中心 Anolis
性能优化特性之:LSE指令集编译优化
本文介绍了倚天实例上的编译优化特性:LSE,并从优化原理、使用方法进行了详细阐述。
|
数据可视化 关系型数据库 MySQL
将 PostgreSQL 迁移到 MySQL 数据库
将 PostgreSQL 迁移到 MySQL 数据库
3164 2
|
5月前
|
人工智能 Kubernetes 应用服务中间件
🚀【技术直播 · 重磅】Nginx Ingress 退役后,如何选对生产级网关?6月24日14:00-15:30,请锁定开发者社区直播间
"Nginx Ingress 不更新了,我该咋办?" 这场直播就是答案。 阿里云专家在线拆解迁移全流程,手把手教你不踩坑、不停服、不背锅。
444 2
|
12月前
|
存储 弹性计算 容灾
阿里云服务器优惠购买方法及自定义购买详细流程,新手入门必看
本文详解阿里云服务器ECS自定义购买全流程,涵盖付费模式、地域、实例规格、镜像、存储、网络、公网IP、安全组等配置选择,助新手快速入门。对比优惠活动与自定义购买优劣,提供实用选型建议,适合搭建网站及各类应用。
|
存储 监控 数据可视化
开发者如何使用日志服务 SLS
【10月更文挑战第17天】开发者如何使用日志服务 SLS
3396 4
|
机器学习/深度学习 传感器 Web App开发
《探索具身智能机器人视觉-运动映射模型的创新训练路径》
视觉-运动映射模型是实现机器人智能交互与精准行动的核心,如同机器人的“神经中枢”。传统训练方法存在局限,而新兴技术如微软的IGOR框架、UC伯克利的VideoMimic系统和OKAMI方法,通过迁移学习、视频模仿及动作捕捉等手段,让机器人能高效学习人类动作并完成复杂任务。这些创新方法利用大规模数据与先进技术,拓展了机器人在复杂环境中的能力边界,推动其从“机械执行”向“自主决策”进化,为具身智能发展注入新活力。
788 28
|
消息中间件 Linux
Linux IPC(进程间通信)管理:深入解析ipcs和ipcrm命令的用法与示例
在Linux操作系统中,进程间通信(IPC)是实现进程之间数据传递和同步的关键机制。Linux提供了多种IPC方式,如消息队列、信号量和共享内存等。为了优化系统性能与稳定性,及时管理和清理IPC资源非常重要。本文将深入介绍ipcs和ipcrm命令的使用方法,并给出丰富的示例,以帮助读者更好地理解IPC资源管理的实践技巧。
2003 1
|
机器学习/深度学习 人工智能 并行计算
《量子计算对人工智能发展的深远影响》
在科技发展的浪潮中,量子计算与人工智能的融合正引领着深刻的科技变革。量子计算利用量子比特的叠加和纠缠特性,实现并行计算,显著提升机器学习训练速度、优化问题求解、大数据分析能力及AI模型泛化能力,催生新型AI算法,并拓展新应用领域。然而,这一融合仍面临硬件稳定性和软件开发等挑战。
883 4
《量子计算对人工智能发展的深远影响》