04环信聊天界面 - 显示历史会话记录

Foomchat:开源AI聊天界面定制框架部署与实战指南 AI聊天界面是现代人机交互的重要载体,其核心原理是通过前端技术栈与后端AI服务的API集成,实现自然语言对话的可视化呈现。在技术价值层面,定制化界面能够显著提升用户体验,满足不同场景的特定交互需求。应用场景广泛覆盖企业客服系统、产品集成开发、研究实验平台等领域。Foomchat作为开源框架,通过支持Docker容器化部署和REST API调用,为开发者提供了灵活的本地部署方案和界面定制能力。该项目特别强调界面自定义和API服务集成,让用户能够自由设计聊天交互逻辑,轻松适配各种AI模型后端。 阅读详情
1.在消息控制器获取历史回话记录
/**
 *  获取历史会话对象记录
 */
- (void)loadConversations
{
    // 当前登录用户回话对象列表
   NSArray *conversations = [[EaseMob sharedInstance].chatManager conversations];
    if (conversations.count == 0) {
        // 从数据库conversation表获取
        conversations = [[EaseMob sharedInstance].chatManager loadAllConversationsFromDatabaseWithAppend2Chat:YES];
    }
    
    self.conversations = conversations;
    
    // 显示总的未读数
    [self showTabBarBadge];
}

#pragma mark - EMChatManagerChatDelegate
/**
 *  历史会话列表更新了会调用
 */
- (void)didUpdateConversationList:(NSArray *)conversationList
{
    // 给数据源重新赋值
    self.conversations = conversationList;
    
    // 刷新表格
    [self.tableView reloadData];
    
    // 显示总的未读数
    [self showTabBarBadge];
}

/**
 *  未读消息数改变了会调用
 */
- (void)didUnreadMessagesCountChanged
{
    [self.tableView reloadData];
    
    // 显示总的未读数
    [self showTabBarBadge];
}

/**
 *  将总的未读消息数显示到tabBar上
 */
- (void)showTabBarBadge
{
    NSInteger totalUnreadCount = 0;
    for (EMConversation *conversation in self.conversations) {
        totalUnreadCount += [conversation unreadMessagesCount];
    }
    
    self.navigationController.tabBarItem.badgeValue = [NSString stringWithFormat:@"%ld",totalUnreadCount];
}
2.消息控制器完整代码
//
//  MessageViewController.m


#import "MessageViewController.h"
#import "EaseMob.h"
#import "ChatViewController.h"

@interface MessageViewController ()<EMChatManagerDelegate>

/**
 *  历史会话对象列表
 */
@property(nonatomic,strong)NSArray *conversations;
@end

@implementation MessageViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 设置标题
    self.title = @"消息";
    
    // 添加聊天管理器的代理
    [[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:nil];
    
    // 获取历史回话记录
    [self loadConversations];
}

- (void)dealloc
{
    // 移除聊天管理器的代理
    [[EaseMob sharedInstance].chatManager removeDelegate:self];
}

/**
 *  获取历史会话对象记录
 */
- (void)loadConversations
{
    // 当前登录用户回话对象列表
   NSArray *conversations = [[EaseMob sharedInstance].chatManager conversations];
    if (conversations.count == 0) {
        // 从数据库conversation表获取
        conversations = [[EaseMob sharedInstance].chatManager loadAllConversationsFromDatabaseWithAppend2Chat:YES];
    }
    
    self.conversations = conversations;
    
    // 显示总的未读数
    [self showTabBarBadge];
}

#pragma mark - EMChatManagerChatDelegate
/**
 *  历史会话列表更新了会调用
 */
- (void)didUpdateConversationList:(NSArray *)conversationList
{
    // 给数据源重新赋值
    self.conversations = conversationList;
    
    // 刷新表格
    [self.tableView reloadData];
    
    // 显示总的未读数
    [self showTabBarBadge];
}

/**
 *  未读消息数改变了会调用
 */
- (void)didUnreadMessagesCountChanged
{
    [self.tableView reloadData];
    
    // 显示总的未读数
    [self showTabBarBadge];
}

/**
 *  将总的未读消息数显示到tabBar上
 */
- (void)showTabBarBadge
{
    NSInteger totalUnreadCount = 0;
    for (EMConversation *conversation in self.conversations) {
        totalUnreadCount += [conversation unreadMessagesCount];
    }
    
    self.navigationController.tabBarItem.badgeValue = [NSString stringWithFormat:@"%ld",totalUnreadCount];
}

/*!
 @method
 @brief 将要发起自动重连操作时发送该回调
 @discussion
 @result
 */
- (void)willAutoReconnect
{
    self.title = @"网络连接中...";
}

/*!
 @method
 @brief 自动重连操作完成后的回调(成功的话,error为nil,失败的话,查看error的错误信息)
 @discussion
 @result
 */
- (void)didAutoReconnectFinishedWithError:(NSError *)error
{
    if (!error) {
        self.title = @"消息";
    }
}

/**
 *  监听网络状态
 */
- (void)didConnectionStateChanged:(EMConnectionState)connectionState{
    //    eEMConnectionConnected,   //连接成功
    //    eEMConnectionDisconnected,//未连接
    if (connectionState == eEMConnectionDisconnected) {
//        NSLog(@"网络断开,未连接...");
        self.title = @"未连接.";
    }else{
//        NSLog(@"网络通了...");
    }
    
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.conversations.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 创建cell
    static NSString *ID = @"messageCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    
    // 设置cell上显示的数据
    EMConversation *conversation = self.conversations[indexPath.row];
    cell.textLabel.text = [NSString stringWithFormat:@"%@ 未读:%ld",conversation.chatter,conversation.unreadMessagesCount];
    
    id body = conversation.latestMessage.messageBodies[0];
    if ([body isKindOfClass:[EMTextMessageBody class]]) {
        EMTextMessageBody *textBody = body;
        cell.detailTextLabel.text = textBody.text;
    }else if ([body isKindOfClass:[EMVideoMessageBody class]]){
        EMVideoMessageBody *voiceBody = body;
        cell.detailTextLabel.text = [voiceBody displayName];
    }else if ([body isKindOfClass:[EMImageMessageBody class]]){
        EMImageMessageBody *imgBody = body;
        cell.detailTextLabel.text = imgBody.displayName;
    }else{
        cell.detailTextLabel.text = @"未知消息类型";
    }
    
    return cell;
}

#pragma mark - UITabelViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 从storyboard加载聊天控制器
    ChatViewController *chatVc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"chatPage"];
    
    // 设置好友属性
    EMConversation *conversation = self.conversations[indexPath.row];
    EMBuddy *buddy = [EMBuddy buddyWithUsername:conversation.chatter];
    chatVc.buddy = buddy;
    
    // 跳转到聊天控制器
    [self.navigationController pushViewController:chatVc animated:YES];
}



@end

3.从消息控制器跳转到聊天控制器


消息控制器中实现

#pragma mark - UITabelViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 从storyboard加载聊天控制器
    ChatViewController *chatVc = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"chatPage"];
    
    // 设置好友属性
    EMConversation *conversation = self.conversations[indexPath.row];
    EMBuddy *buddy = [EMBuddy buddyWithUsername:conversation.chatter];
    chatVc.buddy = buddy;
    
    // 跳转到聊天控制器
    [self.navigationController pushViewController:chatVc animated:YES];
}
4.聊天控制器里需要标识消息为已读,聊天控制器完整代码
//
//  ChatViewController.m
//  环信项目
//
//  Created by yongkaidong on 16/2/13.
//  Copyright © 2016年 com.yongkaidong. All rights reserved.
//

#import "ChatViewController.h"
#import "ChatCell.h"
#import "EaseMob.h"
#import "EMCDDeviceManager.h"
#import "AudioPlayTool.h"
#import "TimeCell.h"
#import "TimeTool.h"

@interface ChatViewController ()<UITableViewDataSource,UITableViewDelegate,UITextViewDelegate,EMChatManagerDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate>
/**
 *  inputToolbar(输入工具栏)底部的约束
 */
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *inputToolbarBottomConstraint;
/**
 *  inputToolbar高度的约束
 */
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *inputToolbarHeightConstraint;
@property (weak, nonatomic) IBOutlet UIButton *recordBtn;
@property (weak, nonatomic) IBOutlet UIButton *voiceBtn;
@property (weak, nonatomic) IBOutlet UITextView *textView;

/**
 *  里边存的都是消息模型
 */
@property(nonatomic,strong)NSMutableArray *dataSources;
@property (weak, nonatomic) IBOutlet UITableView *tableView;

/**
 *  专门用来计算高度的cell工具对象
 */
@property(nonatomic,strong) ChatCell *chatCellTool;

/**
 *  当前添加的时间字符串
 */
@property(nonatomic,copy)NSString *currentTimeStr;

/**
 *  当前会话对象
 */
@property(nonatomic,strong)EMConversation *conversation;
@end

@implementation ChatViewController

- (NSMutableArray *)dataSources
{
    if (!_dataSources) {
        _dataSources = [NSMutableArray array];
    }
    return _dataSources;
}

/**
 *  懒加载创建chatCellTool
 */
- (ChatCell *)chatCellTool
{
    if (!_chatCellTool) {
        //Identifier 用recivierCell或senderCell都可以,因为2个cell其实内部
        //Identifier 声明在cell内部的
        _chatCellTool = [self.tableView dequeueReusableCellWithIdentifier:senderCell];
    }
    return _chatCellTool;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 0.设置标题
    self.title = self.buddy.username;
   
    // 1.监听键盘的弹出
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    // 2.监听键盘的退出
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];

    // 3.加载本地数据库的聊天记录
    [self loadLocalChatRecords];
    
    
    // 4.设置聊天管理器的代理
    [[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:nil];
    
    // 5.tableView滚动到最底部
    [self scrollToBottom];
}

/**
 *  加载本地的聊天记录
 */
- (void)loadLocalChatRecords
{
    // 获取本地聊天记录 使用回话对象
   EMConversation *conversation = [[EaseMob sharedInstance].chatManager conversationForChatter:self.buddy.username conversationType:eConversationTypeChat];
    self.conversation = conversation;
    
    // 加载与当前聊天用户的所有聊记录
    NSArray *messages = [conversation loadAllMessages];
    
    // 添加到数据源
    for (EMMessage *msg in messages) {
        [self addDataSourcesWithMessage:msg];
    }
}

/**
 *  键盘退出会调用
 */
- (void)keyboardWillHide:(NSNotification *)notice
{
    // inputToolbar回到原位
    self.inputToolbarBottomConstraint.constant = 0;
    [UIView animateWithDuration:0.25 animations:^{
        [self.view layoutIfNeeded];
    }];
}

/**
 *  键盘弹出会调用
 */
- (void)keyboardWillShow:(NSNotification *)notice
{
    // 1.获取键盘高度
    CGFloat heigth = [notice.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
    
    // 2.更改inputToolbar底部约束
    self.inputToolbarBottomConstraint.constant = heigth;
    [UIView animateWithDuration:0.25 animations:^{
        [self.view layoutIfNeeded]; 
    }];
    
    // 3.滚动tableView
    [self scrollToBottom];
}

/**
 *  添加通知要移除,我们可以在这里方法里移除
 */
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

#pragma mark -UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.dataSources.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //判断数据源类型
    if ([self.dataSources[indexPath.row] isKindOfClass:[NSString class]]) {
        TimeCell *cell = [tableView dequeueReusableCellWithIdentifier:@"timeCell"];
        cell.timeLabel.text = self.dataSources[indexPath.row];
        return cell;
    }
    
    // 1.获取消息模型
    EMMessage *message = self.dataSources[indexPath.row];

    // 2.创建cell
    ChatCell *cell = nil;
    if ([message.from isEqualToString:self.buddy.username]) { //好友
        cell = [tableView dequeueReusableCellWithIdentifier:recivierCell];
    }else{ //自己
        cell = [tableView dequeueReusableCellWithIdentifier:senderCell];
    }
    
    // 3.给cell传递数据模型
    cell.message = message;
    
    // 4.返回
    return cell;
}

#pragma mark - UITableViewDelegate
/**
 *  返回cell的高度
 */
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 时间cell的高度是固定的
    if ([self.dataSources[indexPath.row] isKindOfClass:[NSString class]]) {
        return 20;
    }
    
    // 1.获取消息模型传递给cell,让cell有了数据然后调整布局
    EMMessage *msg = self.dataSources[indexPath.row];
    self.chatCellTool.message = msg;
    
    // 2.必须上面cell有了数据调整完布局,才能得到高度
    return [self.chatCellTool cellHeight];
}

#pragma mark - UITableViewDelegate
/**
 *  开始滑动scrollView会调用
 */
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
    // 停止语音播放
    [AudioPlayTool stop];
}

#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView
{
    // 1.计算textView的高度,调整整个inputToolbar的高度
    CGFloat textViewH = 0;
    CGFloat minH = 33; //textView最小高度
    CGFloat maxH = 68; //textView最大高度
    CGFloat contentH = textView.contentSize.height;
    if (contentH < minH) {
        textViewH = minH;
    }else if (contentH > maxH){
        textViewH = maxH;
    }else{
        textViewH = contentH;
    }
    
    
    // 2.监听用户是否点击了键盘的"Send"按钮 -- 判断最后一个字符是否是换行符
    if ([textView.text hasSuffix:@"\n"]) {
        // 发送消息
        [self sendText:textView.text];
        
        // 清空textView中的文字
        textView.text = nil;
        
        // 用户点击"发送"之后,textView高度还原
        textViewH = minH;
    }
    
    // 3.调整整个inputToolbar的高度
    /**
     7为textView到顶部的距离
     6为textView到底部的距离
     */
    self.inputToolbarHeightConstraint.constant = 7 + 6 + textViewH;
    [UIView animateWithDuration:0.25 animations:^{
        [self.view layoutIfNeeded];
    }];
    
    
    // 4.让光标回到原位
    [textView setContentOffset:CGPointZero animated:YES];
    [textView scrollRangeToVisible:textView.selectedRange];
}

#pragma mark - 发送消息(文本、语音、图片)
/**
 *  发送消息
 *
 *  @param body 消息体
 */
- (void)sendMessage:(id<IEMMessageBody>)body
{
    // 1.构造消息对象
    EMMessage *msg = [[EMMessage alloc] initWithReceiver:self.buddy.username bodies:@[body]];
    msg.messageType = eMessageTypeChat;
    
    // 2.发送消息
    [[EaseMob sharedInstance].chatManager asyncSendMessage:msg progress:nil prepare:^(EMMessage *message, EMError *error) {
        // 准备发送
    } onQueue:nil completion:^(EMMessage *message, EMError *error) {
        // 发送成功
    } onQueue:nil];
    
    // 4.把消息添加到数据源
    [self addDataSourcesWithMessage:msg];
    
    // 5.刷新表格
    [self.tableView reloadData];
    
    // 6.滚动tableView
    [self scrollToBottom];

}

/**
 *  发送文本消息
 */
- (void)sendText:(NSString *)text
{
    // 0.处理字符串中的换行符
    text = [text substringToIndex:text.length -1];
    
    // 1.准备创建消息实例需要的参数
    EMChatText *chatText = [[EMChatText alloc] initWithText:text];
    EMTextMessageBody *textBody = [[EMTextMessageBody alloc] initWithChatObject:chatText];
    
    // 2.发送
    [self sendMessage:textBody];
}

/**
 *  发送语音消息
 *
 *  @param recordPath 语音文件路径
 *  @param duration   时间
 */
- (void)sendVoice:(NSString *)recordPath duration:(NSInteger)duration
{
    // 1.构造一个 语音消息体
    EMChatVoice *chatVoice = [[EMChatVoice alloc] initWithFile:recordPath displayName:@"[语音]"];
    EMVoiceMessageBody *voiceBody = [[EMVoiceMessageBody alloc] initWithChatObject:chatVoice];
    voiceBody.duration = duration;
    
    // 2.发送
    [self sendMessage:voiceBody];
}

/**
 *  发送图片消息
 */
- (void)sendImage:(UIImage *)selectedImage
{
    // 1.构造图片消息体
    EMChatImage *orginalChatImg = [[EMChatImage alloc] initWithUIImage:selectedImage displayName:@"[图片]"];
    EMImageMessageBody *imgBody = [[EMImageMessageBody alloc]initWithImage:orginalChatImg thumbnailImage:nil];
    
    // 2.发送
    [self sendMessage:imgBody];
}

/**
 *  tableView滚动到最后一行
 */
- (void)scrollToBottom
{
    if (self.dataSources.count == 0) return;
    
    NSIndexPath *lastIndex = [NSIndexPath indexPathForRow:self.dataSources.count-1 inSection:0];
    [self.tableView scrollToRowAtIndexPath:lastIndex atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

#pragma mark - EMChatManagerDelegate
/**
 *  接收好友回复消息
 */
- (void)didReceiveMessage:(EMMessage *)message
{
    if ([message.from isEqualToString:self.buddy.username]) {
        // 把好友回复的消息添加到数据源
        [self addDataSourcesWithMessage:message];
        
        // 刷新表格
        [self.tableView reloadData];
        
        // 滚动tablView
        [self scrollToBottom];
    }
}

#pragma mark - action
/**
 *  点击上传图片
 */
- (IBAction)showImgPickerAction:(UIButton *)sender
{
    // 图片选择控制器
    UIImagePickerController *imgPicker = [[UIImagePickerController alloc] init];
    imgPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    imgPicker.delegate = self;
    [self presentViewController:imgPicker animated:YES completion:nil];
}

#pragma mark - UIImagePickerControllerDelegate
/**
 *  用户选中图片的回调
 */
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    // 获取选中的图片
    UIImage *selectedImg = info[UIImagePickerControllerOriginalImage];
    
    // 发送图片消息
    [self sendImage:selectedImg];
    
    // 退出当前图片选择的控制器
    [self dismissViewControllerAnimated:YES completion:nil];
}

/**
 *  VoiceBtn被点击了
 */
- (IBAction)clickedVoiceBtn:(UIButton *)sender
{
    // 按钮图片切换
    self.voiceBtn.selected = !self.voiceBtn.selected;
    self.textView.hidden = !self.textView.hidden;
    
    // 1.显示录音按钮
    self.recordBtn.hidden = !self.recordBtn.hidden;
    if (self.recordBtn.hidden == NO) { //录音按钮要显示
        // inputToolbar的高度要回到默认高度
        self.inputToolbarHeightConstraint.constant = 46;
        // 退出键盘
        [self.view endEditing:YES];
    }else{
        // 当不录音的时候,键盘显示
        [self.textView becomeFirstResponder];
        // 恢复inputToolbar的高度
        [self textViewDidChange:self.textView];
    }
}

/**
 *  手指按下去开始录音
 */
- (IBAction)touchDownRecordBtn:(id)sender
{
    // 文件名以时间命令
    int x = arc4random() % 100000;
    NSTimeInterval time = [[NSDate date] timeIntervalSince1970];
    NSString *fileName = [NSString stringWithFormat:@"%d%d",(int)time,x];
    
    [[EMCDDeviceManager sharedInstance] asyncStartRecordingWithFileName:fileName completion:^(NSError *error) {
        if (!error) {
            //开始录音成功
        }
    }];
}

/**
 *  手指松开结束录音
 */
- (IBAction)touchUpInsideRecordBtn:(id)sender
{
    [[EMCDDeviceManager sharedInstance] asyncStopRecordingWithCompletion:^(NSString *recordPath, NSInteger aDuration, NSError *error) {
        if (!error) {
            //录音完成
            //发送语音
            [self sendVoice:recordPath duration:aDuration];
        }
    }];
}

/**
 *  手指从按钮的外面松开结束录音

 */
- (IBAction)touchUpOutsideRecordBtn:(id)sender
{
    
}

/**
 *  加入数据源的方法
 */
- (void)addDataSourcesWithMessage:(EMMessage *)message
{
    // 1.判断EMMessage对象前面是否要加『时间』
    NSString *timeStr = [TimeTool timeStr:message.timestamp];
    if (![self.currentTimeStr isEqualToString:timeStr]) {
        [self.dataSources addObject:timeStr];
        self.currentTimeStr = timeStr;
    }
   
    // 2.再加EMMessage
    [self.dataSources addObject:message];
    
    // 3.设置消息为已读
    [self.conversation markMessageWithId:message.messageId asRead:YES];
}

@end

自此,环信项目已完结!

MATLAB闪退别慌!5分钟搞定Windows系统预设文件修复(附详细截图) 本文针对MATLAB在Windows系统下频繁闪退的问题,提供了快速有效的解决方案。核心在于修复或重置损坏的MATLAB预设文件,通过图文并茂的五步法,指导用户定位AppData目录、重命名预设文件夹,从而让MATLAB自动生成全新配置以解决闪退。文章还涵盖了问题诊断、设置备份及进阶排查方法,帮助用户彻底根除启动故障。 阅读详情

相关推荐

东方财富API隐藏技巧:这样获取分时均价线数据更稳定(避坑指南)

本文深入解析东方财富API获取股票分时数据,特别是分时均价线的实战技巧与避坑指南。针对高频调用中常见的请求失败、数据错位及IP限制等问题,提供了从接口参数深层解析、稳健请求与错误处理机制,到系统优化与数据校验的全流程解决方案,帮助开发者构建稳定可靠的数据采集系统。

rice5的博客 2521

【小程序】微信小程序集成环信im

环信im集成小程序的使用

zxsy19966的博客 2148

从零到一:Mx_yolo本地训练与K210部署实战指南

本文详细介绍了从零开始进行Mx_yolo本地模型训练到K210部署的全流程实战指南。涵盖环境配置、数据准备、模型训练与调优、模型转换及K210部署等关键步骤,并提供了常见问题解决方案和性能优化技巧,帮助开发者高效完成AI模型在嵌入式设备的落地应用。

weixin_29081163的博客 251

电脑聊天监控软件哪个好用(有哪些值得推荐的电脑聊天监控软件?)

随着互联网的普及,人们越来越依赖网络进行沟通。在工作和生活中,我们经常使用各种即时通讯工具进行聊天、讨论和协作。然而,这些工具也可能带来隐私泄露的风险。为了保护个人信息和商业机密,一些人可能会选择使用电脑聊天记录监控软件。本文将为您介绍一些常见的电脑聊天记录监控软件。

DCY18033745888的博客 1482

华为AC外置Portal认证方案配置步骤指南

华为AC外置Portal认证方案配置步骤指南

m0_46129105的博客 7659

基于Spring Boot框架的网上蛋糕销售系统

随着互联网的普及和电子商务的迅猛发展,网上购物已经成为人们日常生活的一部分。蛋糕作为一种受欢迎的食品,其销售市场也逐渐向网络化方向发展。网上蛋糕销售系统能够打破时间和地域的限制,让消费者随时随地浏览和购买蛋糕,同时也为蛋糕商家提供了一个展示和销售产品的平台。该系统的开发旨在提高蛋糕预定和销售的便捷性,节约时间和经济成本,并推动蛋糕市场的进一步发展。开发语言:Java,一种面向对象的编程语言,具有跨平台性、稳定性和安全性等特点,适用于构建大型、复杂的Web应用程序。 框架:Spring Boot,一个基于Sp

2301_81127431的博客 995

从环信拉取消息记录,写入本地数据库

明天补上内容

泽漆 4141

2026年数学建模国赛B题无线电干扰源的快速自动定位与清除 论文详细解析

数学建模竞赛论文无线电干扰源的快速自动定位与清除2026 年高教社杯全国大学生数学建模竞赛 B 题完整求解论文交会定位、极大极小测点优化、自适应多频道搜索与定向源恢复的统一建模模型、算法、代码、图表与验证一体化交付本文研究圆形目标区域中多个未知无线电干扰源的快速检测、定位与清除问题,建立了由误差楔交会模型、角度观测信息模型、分层勘测模型、鲁棒射线交会模型和定向覆盖反馈模型组成的统一求解框架。模型同时覆盖题目中的四个子问题:多测向射线形成的多边形定位区域及直径、第二个测点的极大极小优选、全向源自动搜索清除策略

YOLO 356

Android基于环信实现聊天功能(一)——了解环信

本系列知识点来自于:b站尚硅谷Android通信实战。 一、环信集成概述 环信和用户体系的集成主要发生在2个地方,服务器端集成和客户端集成。 1.1发送消息过程 客户端通过环信服务器进行消息发送。如下图所示: 在线状态:客户端A发送消息到环信服务器,环信服务器推送消息到客户端B。 离线状态:客户端A发送消息到环信服务器,环信服务器检测到客户端B不在线,会添加到离线缓存中,当客户端B登录后服务器会把离线消息推送给客户端B。 1.2聊天记录保存 ...

indeedes的博客 3247

宇瞳杯安防镜头设计

宇瞳杯安防镜头设计

weixin_72050316的博客 1507

RK3399平台开发系列讲解(内核入门篇)1.2、如何高效的阅读Linux内核设备驱动

内核开发在很大程度上并不是重新造“轮子”的过程,而是深入理解并尽量复用现有的内核设计框架,然后参照相似的功能模块去添加或改写某项需要的功能。在对内核整体框架以及某些子系统融会贯通以后,我们才有可能站在巨人的肩膀上去改进框架本身,实现自主创新。如果过分强调不必要的“自主创新”,可能会让内核的可维护性变差,最终结果反而得不偿失。 那么此次就分享就围绕如何高效的阅读内核驱动代码:

内核笔记 7341

实现从“环信”下载聊天记录,显示在本地项目页面的功能(二)—— 获取环信聊天记录

一、环信即时云简介 网址:http://docs.easemob.com/doku.php?id=start:100serverintegration:30chatlog 本项目通过“安卓客户端”进行专家和用户的聊天功能,聊天内容以json格式保存在环信即时云数据库中。格式大致如下: 二、JavaBean设计: 因为环信中实际的聊天内容是三层嵌套的模式,所以需要

快雪时晴天的专栏 1万+

C# NX二次开发-查找连续倒圆角面

可以通过ufun函数 UF_MODL_ask_face_type 和 UF_MODL_ask_face_props 可判断处理选择相应的一组圆角面。在QQ群里有人问怎么通过一个选择一个倒圆角面来自动选中一组倒圆角面。这个是初步思路,如果有更好的思路可回复讨论一下。

fhqiwmc的博客 617

如何实现调用Ollama一次性聊天,不会有历史聊天记录?

在使用Ollama进行多角色或多会话的聊天时,为了避免聊天内容混淆,可以通过以下几种方法进行上下文管理: 重置上下文:在每次API调用时不传递历史消息,确保每次会话独立。 控制上下文保留时间:通过设置keep_alive参数为极短时间,使模型在请求完成后立即释放上下文。 手动清除模型实例:通过REST API删除当前模型实例,强制释放内存中的上下文。 独立会话:为每个角色初始化独立的会话对象,避免不同角色的聊天内容交叉污染。 修改启动配置:调整Ollama的配置文件,优化上下文管理策略。

本博客,博文仅代表个人操作经验,不能完全解决你的问题,仅供参考,佛系回复。 2426

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

04环信聊天界面 - 加载本地聊天记录

1在聊天控制器.加载聊天记录,并添加到数据源:/** * 加载本地的聊天记录 */ - (void)loadLocalChatRecords { EMConversation *conversation = [[EaseMob sharedInstance].chatManager conversationForChatter:self.buddy.username conversat

6386

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

【VSCode远程协作必看】:如何永久保存并导出聊天历史记录

轻松解决VSCode聊天历史保存难题,支持远程协作回溯与知识沉淀。本文介绍如何通过扩展插件自动记录并导出VSCode聊天历史,适用于团队协作、代码审查等场景,确保交流内容永久可查。方法简单高效,值得收藏。

Instrulink的博客 1154

Ubuntu 10.04下如何保存和查看信使(iptux)的聊天历史

默认情况下,Ubuntu 10.04中的信使(iptux)0.5.1无法保存和查看聊天历史。其实在首选项中有相应的功能设置。开启步骤如下: 软件主界面->工具->系统->保存聊天历史,将复选框选中即可。 如果要查看聊天历史,可以到当前用户的工作目录中查看:~/.config/iptux/log/communicate.log,此文件中包含所有的聊天记录。

冰雪世界 791

Dify如何实现会话状态持久化?用户历史记录存储机制

Dify通过会话ID和分层存储机制,让AI应用具备记忆能力。系统自动管理上下文拼接与历史加载,结合异步写入、缓存加速和数据加密,确保高效、安全、可扩展。开发者无需编写复杂逻辑,即可构建具有长期记忆的智能对话应用。

weixin_29859471的博客 973

Open Interpreter会话管理技巧:聊天历史保存与恢复实战

本文介绍了如何在星图GPU平台上自动化部署open interpreter镜像,实现本地AI编程协作者的会话持久化管理。通过保存与恢复聊天历史,用户可高效复用数据清洗、可视化分析等中长周期任务上下文,显著提升数据分析与脚本开发效率。

weixin_42103128的博客 123

【SpringAI篇04】:基于 ChatMemory 查询聊天记录

在上文聊天记录持久化基础上,增加查询聊天记录功能,由于聊天记录基于 ChatMemory 接口存储,通过注入该接口并传入对话 ID,可调用其get(String conversationId)方法查询历史消息。示例中HistoryController通过@GetMapping映射接口,接收chatId参数并调用chatMemory.get(String.valueOf(chatId)),实现按会话 ID 查询对话历史,该功能依赖于此前配置的持久化存储方案(如 MySQL),可从数据库中读取已保存的对话记录。

lyh2004_08的博客 708

Longene的wine-QQ并与windowsQQ聊天记录同步

Longene的Wine-QQ用户登录之后,相关的聊天记录等信息是存放在如下位置的: [fan3838@isoft Users]$ pwd /home/fan3838/.longene/qq/drive_c/Program Files/Tencent/QQ/Users     此目录下有以QQ号码明明的文件夹,我只需要将此文件夹软连接到我数

myKernel 2747

Dify会话历史查询API深度解析:开发者必备的8个调用技巧

掌握Dify用户会话历史记录查询的高效方法,本文解析8个关键API调用技巧,涵盖多场景应用、权限控制与数据过滤策略。提升开发效率,实现精准会话追踪,值得收藏。

InstrFun的博客 1361

Ubuntu 24.04服务器实战:部署AI聊天机器人

通过调整uvicorn的worker数量(CPU核心数*2+1)和调整Nginx的keepalive_timeout,最终在2核4G服务器上稳定支持100+并发。能省去很多麻烦——它的在线编辑器直接预装好了Python环境,调试时还能实时看到请求响应。最惊喜的是部署功能,不用自己折腾Nginx配置,点个按钮就能生成可访问的临时域名,特别适合快速验证想法。为控制成本,在代码中加入自动截断过长对话历史的逻辑,保留最近5轮对话上下文。整个过程踩了不少坑,也总结了些实用经验,记录下这个从零开始的实战过程。

BlackStone33的博客 940

Clawdbot整合Qwen3-32B实战教程:支持文件上传、多会话管理、历史记录持久化的Web平台

本文介绍了如何在星图GPU平台自动化部署Clawdbot整合Qwen3:32B代理直连Web网关配置Chat平台镜像,快速搭建支持文件上传与智能对话的Web应用。该方案特别适用于企业文档分析场景,用户可上传PDF、Word等文件,由Qwen3-32B大模型自动解析内容并生成智能回复,实现高效的知识管理与对话交互。

CrystalwaveTiger37的博客 745

使用Redis存储聊天消息历史:高效的内存数据库解决方案

Redis是一个开源的、支持网络、基于内存、可选持久性的键值对存储数据库。高性能:所有数据都存储在内存中,提供极低的读写延迟。多样的数据结构:支持字符串、哈希、列表、集合等多种数据类型。可选的持久化:支持数据快照和AOF日志。主从复制和集群:提供高可用性和可扩展性。这些特性使Redis成为缓存、消息代理和实时分析等场景的理想选择。Redis作为高性能的内存数据库,为存储聊天消息历史提供了一个高效的解决方案。结合LangChain框架,我们可以轻松实现持久化的对话系统。

ppoojjj的博客 1156

【VSCode聊天历史管理秘籍】:5个你必须掌握的高效协作技巧

掌握VSCode聊天历史管理技巧,提升团队协作效率。本文分享5个实用方法,涵盖代码审查、远程协作等场景,轻松查找记录、同步上下文、避免信息丢失。高效开发必备,值得收藏。

PixelShoal的博客 872

VSCode协作开发痛点解决(聊天历史同步难题一文搞定)

解决VSCode协作开发中聊天历史不同步的痛点,通过集成实时同步方案,确保团队沟通记录无缝共享。适用于远程协作、结对编程等场景,提升开发效率与沟通连贯性。VSCode聊天历史一键同步,操作简单稳定,值得收藏。

SimSolve的博客 638
上一篇: 04环信聊天界面 - 显示时间的cell
下一篇: MySQL用户授权
学习笔记666
博客等级 码龄12年 1047粉丝 1369原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值