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

简介: 本文基于体育赛事直播系统,详细介绍了用户间私聊功能的完整实现方案。技术栈涵盖后端(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
相关文章
|
3月前
|
存储 消息中间件 NoSQL
体育赛事直播系统中基于 WebSocket 实现的聊天室与弹幕模块设计与实践
本文详解东莞梦幻网络科技体育直播系统中「聊天室+弹幕」模块的实时通信技术实现,涵盖 WebSocket 架构、连接池管理、多房间逻辑、消息转发与并发控制等核心要点,助力构建高并发、低延迟的直播互动体验。
|
安全 前端开发 Java
Springboot中如何优雅的进行字段以及业务校验
Springboot中如何优雅的进行字段以及业务校验
307 0
|
Web App开发 应用服务中间件 Go
尝鲜:如何搭建一个简单的webrtc服务器
前几天我一朋友问我有关webrtc的事,简单了解了下相关知识,搭建了一个webrtc的服务,以及经历的各种踩坑事件,感觉踩坑主要是Python、Node、OpenSSL等版本问题和证书问题导致。本来以为很简单的搭建,但在搭建的过程中遇到各种阻碍,写一篇文章梳理一下。
12515 0
|
7月前
|
前端开发 JavaScript API
体育赛事即时比分 分析页面的开发技术架构与实现细节
本文基于“体育即时比分系统”开发经验总结,分享技术实现细节。系统通过后端(ThinkPHP)、前端(Vue.js)、移动端(Android/iOS)协同工作,解决实时比分更新、赔率同步及赛事分析展示等问题。前端采用 Vue.js 结合 WebSocket 实现数据推送,提升用户体验;后端提供 API 支持比赛数据调用;移动端分别使用 Java 和 Objective-C 实现跨平台功能。代码示例涵盖比赛分析页面、API 接口及移动端数据加载逻辑,为同类项目开发提供参考。
|
5月前
|
机器学习/深度学习 人工智能 安全
AI的万亿商机:红杉资本眼中的人工智能新时代
AI不仅仅是不可避免的趋势,而是已经到来的现实,其市场规模将远超过去的任何一次技术变革。这不是一场可以观望的比赛,而是一场必须全力以赴参与的革命。
250 22
|
6月前
|
存储 缓存 关系型数据库
开发体育直播系统后台权限设计实践分享|ThinkPHP 技术栈落地案例
本文分享了一套体育直播源码,基于 **ThinkPHP + MySQL** 技术栈构建。系统采用清晰的分层架构,涵盖用户端、前端接口、后端服务、权限控制及数据层。通过增强的 RBAC 权限模型,实现了从菜单配置到动态角色绑定的自动化权限管理,并结合 Redis 缓存优化性能。同时,部署架构支持高可用性,安全设计遵循最小化原则,确保平台稳定运行与数据安全。
|
11月前
|
机器学习/深度学习 数据采集 人工智能
深入理解SVM中的核函数及其应用
深入理解SVM中的核函数及其应用
511 83
|
JavaScript 前端开发 API
python对接API二次开发高级实战案例解析:百度地图Web服务API封装函数(行政区划区域检索、地理编码、国内天气查询、IP定位、坐标转换)
python对接API二次开发高级实战案例解析:百度地图Web服务API封装函数(行政区划区域检索、地理编码、国内天气查询、IP定位、坐标转换)
812 0
|
12月前
|
物联网 5G API
怎么在三大运营商中选择物联网卡
在选择物联网卡(IoT卡)的运营商时,您需要考虑几个关键因素,包括网络覆盖、服务质量、价格、功能需求以及后续的技术支持等。中国的三大主要运营商是中国移动、中国联通和中国电信,它们各自在物联网领域有不同的优势和特点。以下是一些建议,帮助您在这三者之间做出选择: