基于MongoDB实现聊天记录的存储

news2024/12/25 8:58:44

一、mongodb简介

1.1 mongodb简介

MongoDB是一个基于分布式文件存储的数据库,使用C++语言编写。它旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB介于关系数据库和非关系数据库之间,是非关系数据库当中功能最丰富、最像关系数据库的。

MongoDB将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB文档类似于JSON对象,字段值可以包含其他文档、数组及文档数组。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。

MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

此外,MongoDB还具有以下特点:

  1. 面向集合存储,易存储对象类型的数据。
  2. 模式自由。
  3. 支持动态查询。
  4. 支持完全索引,包含内部对象。
  5. 支持查询。
  6. 支持复制和故障恢复。
  7. 使用高效的二进制数据存储,包括大型对象(如视频等)。
  8. 自动处理碎片,以支持云计算层次的扩展性。
  9. 可通过网络访问。

在高负载情况下,添加更多的节点可以保证服务器性能。MongoDB也易于部署和使用,存储数据非常方便。

总的来说,MongoDB是一个高性能、易部署、易使用的数据库系统,具有丰富的功能和特点,适用于各种规模的应用程序和场景。

1.2 mongodb利弊

优点:

  1. 灵活性:MongoDB采用文档存储方式,这意味着数据以键值对的形式存储在BSON(二进制JSON)格式中,这使得它能够存储复杂的数据类型,包括数组、嵌套文档等。这种灵活性使得MongoDB能够轻松地适应各种数据模型。
  2. 易扩展性:MongoDB支持自动分片,这使得它能够轻松地扩展到大量数据和复杂查询场景。通过添加更多的节点,MongoDB可以自动地将数据分布到不同的节点上,从而提高整体性能。
  3. 高性能:MongoDB使用内存映射机制,将数据暂时存储在内存中,提高了IO效率,MongoDB支持快速的读写操作,尤其适用于大规模数据和高并发场景。它还提供了多种查询方式,包括范围查询、排序、聚合等,这使得查询操作比传统的关系型数据库更加快速。
  4. 社区支持:MongoDB有一个活跃的开源社区,这意味着用户可以很容易地找到帮助和资源,以及最新的技术更新和最佳实践。

缺点:

  1. 缺乏事务支持:MongoDB不支持传统的事务处理特性,这意味着在处理多个文档或集合之间的复杂操作时可能会遇到问题。虽然MongoDB提供了乐观并发控制和文档级锁定来解决并发问题,但在需要完整的事务支持的场景下可能不够用。
  2. 复杂性:由于MongoDB的灵活性,它可能比传统的关系型数据库更复杂。对于初学者来说,可能需要更长的时间来学习和理解其数据模型和查询语言。
  3. 数据一致性:MongoDB采用最终一致性模型,而不是强一致性模型。这意味着在某些情况下,数据可能不会立即反映所有的更改。这对于需要强一致性的应用来说可能是一个问题。
  4. 磁盘空间占用:由于MongoDB使用文件存储数据,因此可能会占用大量的磁盘空间。特别是在高写入负载的情况下,由于数据文件的增长和收缩,可能会导致磁盘碎片的产生。
1.3 mongodb使用场景

MongoDB的使用场景非常广泛,包括以下几个方面:

  1. 内容管理和发布系统:MongoDB的灵活文档模型和高性能写入能力使其成为内容管理和发布系统的理想选择。它可以存储和检索各种类型的内容,如文章、图片、视频等。
  2. 个性化推荐系统:MongoDB可以存储和查询用户的个人偏好和行为数据,从而支持个性化推荐。通过使用MongoDB的高性能索引和聚合功能,可以快速地分析和提供个性化的推荐结果。
  3. 实时分析和大数据处理:MongoDB的分布式架构和高可扩展性使其非常适合实时分析和大数据处理任务。它可以处理大量的并发读写操作,并且支持复杂的查询和聚合操作。
  4. 时序数据管理:MongoDB的存储引擎和索引结构对时序数据的管理非常高效。它可以存储和查询大量的时间序列数据,如传感器数据、日志数据等。
  5. 实时数据分析和监控:MongoDB的副本集和分片功能可以实现实时数据分析和监控。它可以处理大量的并发写入操作,并提供实时的查询结果。
  6. 社交网络和协作平台:MongoDB的文档模型非常适合存储和查询社交网络和协作平台的数据。它可以存储用户的个人资料、关系图谱、消息等。
  7. 位置数据管理和地理信息系统:MongoDB的地理空间索引和查询功能使其成为管理位置数据和地理信息系统的理想选择。它可以存储和查询地理位置、地理边界、地理特征等数据。
  8. 游戏场景:使用MongoDB存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新。
  9. 物流场景:使用MongoDB存储订单信息,订单状态在运送过程中会不断更新,以MongoDB内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。

总的来说,MongoDB适用于各种场景,从网站数据到大数据处理,再到社交网络和游戏等领域,它都表现出强大的灵活性和可扩展性。

1.4 mongodb存储聊天记录和mysql存储的抉择

选择使用MySQL还是MongoDB来存储聊天记录取决于具体需求和场景。以下是两者的一些比较:

MySQL

  • 结构化数据:适用于存储结构化数据,如聊天记录中的文本、时间戳等。
  • 事务处理:支持事务处理,可以保证数据的一致性和完整性。
  • 成熟度与社区支持:是一个成熟的关系型数据库管理系统,拥有庞大的用户基础和丰富的社区支持。
  • 查询优化:适合对复杂查询和性能要求较高的场景。

MongoDB

  • 非结构化数据:适用于存储非结构化数据,如图片、语音消息等。
  • 灵活性:具有灵活的数据模型,可以轻松处理聊天记录中的各种格式和结构。
  • 水平扩展性:适用于大规模数据的存储和管理,具有水平扩展性。
  • 实时性:适合需要实时处理和快速响应的场景,如实时聊天应用。

综上所述,如果聊天记录主要是结构化数据并且需要事务处理和复杂查询,MySQL可能是一个更好的选择。如果聊天记录包含大量非结构化数据并且需要水平扩展和实时处理能力,对事务的完整性要求不高对存取速度要求较高我建议使用新兴的nosql类型数据 MongoDB可能更适合。

二、业务场景

需求:我们的需求是实现一个与AI对话的聊天系统,大概分为两个部分,一个是会话,一个是聊天
我给大家放张图帮助理解(左边是会话,右边是聊天)
在这里插入图片描述

三、聊天记录的存储和查询

3.1 聊天记录数据集合的设计,可以理解为数据表

会话collection:

@Data
@Document(value = "agents_session")
public class AgentsSession implements Serializable {
    private static final long serialVersionUID = 198529858452480909L;


    private String id;

    private String agentId;

    /**
     * session id
     */
    private String sessionId;
    /**
     * 发送者id
     */
    private String senderCode;

    /**
     * 消息(当前会话组中最早的一次提问(也就是用户想AI提问))
     */
    private String message;
    /**
     * 发送时间
     */
    private String sendTime;

    /**
     * 是否删除
     */
    private Boolean isDeleted;

}

聊天记录collection:

@Data
@Document(value = "agents_chat_messages")
public class AgentsChatMessages implements Serializable {
    private static final long serialVersionUID = 823228953137629152L;

    private String id;
    /**
     * 会话id
     */
    private String sessionId;
    /**
     * 消息内容
     */
    private String message;
    /**
     * 接收状态
     */
    private Integer receiveStatus;
    /**
     * 发送者id
     */
    private String senderCode;
    /**
     * 接收者id
     */
    private String recipientCode;
    /**
     * 发送时间
     */
    private String sendTime;
    /**
     * 消息类型 文本、图片、文件、语音等
     */
    private String messageType;
    /**
     * 消息内容汉字个数
     */
    private Integer tokens;

    /**
     * 当前支持以下:
     * user: 表示用户
     * assistant: 表示对话助手
     */
    private String role;

    /**
     * 是否已读
     */
    private Boolean isRead;

    /**
     * 是否删除
     */
    private Boolean isDeleted;

    /**
     * 问答对匹配id
     */
    private String questionAnswerId;
}
3.2 聊天记录存取的实现

service实现

public interface ChatMessagesService {

    /**
     * 分页获取会话列表
     * @param dto
     * @return
     */
    PageModel<AgentsSessionVO> queryAgentSessionPage(AgentsSessionDTO dto, PageRequestDTO page);

    /**
     * 通过会话id分页获取会话列表
     * @param dto
     * @return
     */
    PageModel<AgentsChatMessagesVO> queryAgentsChatMessagesPage(AgentsChatMessagesDTO dto, PageRequestDTO page);

    /**
     * 保存会话和聊天
     * @param messagesDTO
     */
    void saveSessionAndMessages(AgentsChatMessagesDTO messagesDTO);
}

实现类:

@Service
@Slf4j
public class ChatMessagesServiceImpl implements ChatMessagesService {

    @Resource
    private MongoTemplate mongoTemplate;


    /**
     * 获取会话列表
     *
     * @param dto
     * @return
     */
    @Override
    public PageModel<AgentsSessionVO> queryAgentSessionPage(AgentsSessionDTO dto, PageRequestDTO page) {
        try {
            // 创建分页对象
            Pageable pageable = PageRequest.of(page.getPage() - 1, page.getSize(), Sort.Direction.DESC, "sendTime"); // 注意:页码从0开始,所以需要减1
            // 创建查询对象
            Query query = new Query();
            query.addCriteria(Criteria.where("senderCode").is(dto.getUserCode()).and("isDeleted").is(false));

            //设置模糊查询
            if (StringUtils.isNotEmpty(dto.getMessage())) {
                query.addCriteria(Criteria.where("message").regex(dto.getMessage()));
            }
            if (!CollectionUtils.isEmpty(dto.getAgentsIds())) {
                // in 条件查询
                Criteria criteria = Criteria.where("agentId").in(dto.getAgentsIds());
                query.addCriteria(criteria);
            }
            // 排序
            query.with(Sort.by(Sort.Order.desc("sendTime")));
            // 设置分页
            query.with(pageable);

            List<AgentsSessionVO> list = mongoTemplate.find(query, AgentsSessionVO.class, CommonConstant.AGENTS_SESSION);
            list.forEach(s ->{
                try {
                    s.setMessage(AesEncryptionUtil.decrypt(s.getMessage()));
                } catch (Exception e) {
                    throw new HxyAgentsXException("数据加载失败", e);
                }
            });
            long count = mongoTemplate.count(query, AgentsSessionVO.class, CommonConstant.AGENTS_SESSION);

            return new PageModel<AgentsSessionVO>(list, count, pageable);
        } catch (Exception e) {
            log.error("获取会话列表异常");
            throw new HxyAgentsXException("获取会话列表异常", e);
        }
    }

    /**
     * 通过会话id分页获取聊天记录
     *
     * @param dto
     * @return
     */
    @Override
    public PageModel<AgentsChatMessagesVO> queryAgentsChatMessagesPage(AgentsChatMessagesDTO dto, PageRequestDTO page) {

        try {
            // 创建分页对象
            Pageable pageable = PageRequest.of(page.getPage() - 1, page.getSize(), Sort.Direction.ASC,"sendTime"); // 注意:页码从0开始,所以需要减1
            // 创建查询对象
            Query query = new Query();
            //设置模糊查询
            if (StringUtils.isNotEmpty(dto.getMessage())) {
                query.addCriteria(Criteria.where("message").regex(dto.getMessage()));
            }
            query.addCriteria(Criteria.where("sessionId").is(dto.getSessionId()).and("isDeleted").is(false));

            query.addCriteria(new Criteria().orOperator(Criteria.where("senderCode").is(dto.getUserCode()),Criteria.where("recipientCode").is(dto.getUserCode())));
            // 排序
            query.with(Sort.by(Sort.Order.asc("sendTime")));
            // 设置分页
            query.with(pageable);

            List<AgentsChatMessagesVO> list = mongoTemplate.find(query, AgentsChatMessagesVO.class, CommonConstant.AGENTS_CHAT_MESSAGES);
            list.forEach(m ->{
                try {
                    m.setMessage(AesEncryptionUtil.decrypt(m.getMessage()));
                } catch (Exception e) {
                    throw new HxyAgentsXException("数据加载失败", e);
                }
            });
            long count = mongoTemplate.count(query, AgentsChatMessagesVO.class, CommonConstant.AGENTS_CHAT_MESSAGES);

            return new PageModel<AgentsChatMessagesVO>(list, count, pageable);
        } catch (Exception e) {
            log.error("获取聊天记录列表异常");
            throw new HxyAgentsXException("获取聊天记录列表异常", e);
        }
    }

    /**
     * 存会话聊天
     * @param messagesDTO
     */
    @Override
    public void saveSessionAndMessages(AgentsChatMessagesDTO messagesDTO) {
        try {
            Criteria.where("sessionId").is(messagesDTO.getSessionId());
            AgentsSession agentsSessionOne = mongoTemplate.findOne(new Query(Criteria.where("sessionId").is(messagesDTO.getSessionId()).
                    and("isDeleted").is(false)), AgentsSession.class);

            // 会话
            if (agentsSessionOne == null){
                AgentsSession agentsSession = new AgentsSession();
                agentsSession.setId(UUIDUtils.getUUID());
                agentsSession.setSessionId(messagesDTO.getSessionId());
                agentsSession.setAgentId(messagesDTO.getAgentId());
                agentsSession.setMessage(AesEncryptionUtil.encrypt(messagesDTO.getMessage()));
                agentsSession.setSenderCode(messagesDTO.getSenderCode());
                agentsSession.setIsDeleted(false);
                agentsSession.setSendTime(LocalDateUtil.localDateTimeToString(LocalDateUtil.getLocalDateTime(),"yyyy-MM-dd HH:mm:ss"));
                mongoTemplate.insert(agentsSession);
            }

            // 聊天
            AgentsChatMessages agentsChatMessages = new AgentsChatMessages();
            agentsChatMessages.setId(UUIDUtils.getUUID());
            agentsChatMessages.setSessionId(messagesDTO.getSessionId());
            agentsChatMessages.setMessage(AesEncryptionUtil.encrypt(messagesDTO.getMessage()));
            agentsChatMessages.setMessageType("text");
            agentsChatMessages.setRole(messagesDTO.getRole());
            agentsChatMessages.setIsRead(true);
            agentsChatMessages.setIsDeleted(false);
            agentsChatMessages.setSenderCode(messagesDTO.getSenderCode());
            agentsChatMessages.setRecipientCode(messagesDTO.getRecipientCode());
            agentsChatMessages.setSendTime(LocalDateUtil.localDateTimeToString(LocalDateUtil.getLocalDateTime(),"yyyy-MM-dd HH:mm:ss"));
            agentsChatMessages.setQuestionAnswerId(messagesDTO.getQuestionAnswerId());
            mongoTemplate.insert(agentsChatMessages);

        } catch (Exception e) {
            log.error("保存会话聊天失败",e);
            throw new HxyAgentsXException("保存会话聊天失败",e);
        }

    }
}

聊天内容加密:

public class AesEncryptionUtil {

    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";
    private static final byte[] keyValue = "yourSecretKey".getBytes(StandardCharsets.UTF_8);

    public static String encrypt(String valueToEncrypt) throws Exception {
        SecretKeySpec key = new SecretKeySpec(keyValue, ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] encryptedByteValue = cipher.doFinal(valueToEncrypt.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedByteValue);
    }

    public static String decrypt(String encryptedValue) throws Exception {
        SecretKeySpec key = new SecretKeySpec(keyValue, ALGORITHM);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] originalValue = cipher.doFinal(Base64.getDecoder().decode(encryptedValue));
        return new String(originalValue, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws NoSuchAlgorithmException {
        // 创建AES密钥生成器
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        // 设置密钥长度为256位
        keyGenerator.init(128);
        // 生成密钥
        SecretKey secretKey = keyGenerator.generateKey();

        // 将密钥转换为字符串
        String keyString = Base64.getEncoder().encodeToString(secretKey.getEncoded());
        System.out.println("Generated AES key (Base64): " + keyString);
    }
}
3.3 保存问答聊天

在用户问答的时候保存聊天内容
在这里插入图片描述

在模型回答结束的时候保存聊天内容
在这里插入图片描述
最后大家可以结合自己的业务来实现聊天记录的存取。

最后送大家一句话白驹过隙,沧海桑田

文末送福利啦~

1、Java(SE、JVM)、算法数据结构、数据库(Mysql、redis)、Maven、Netty、RocketMq、Zookeeper、多线程、IO、SSM、Git、Linux、Docker、Web前端相关学习笔记
2、2023最新BATJ大厂面试题集
3、项目源码
4、学习小组
领取方式:关注下方公主号,回复:【笔记】、【面试】获取相关福利。

文章持续更新,可以关注下方公众号或者微信搜一搜「 迷迭香编程 」获取项目源码、干货笔记、面试题集,第一时间阅读,获取更完整的链路资料。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1424901.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【TCP】三次握手(建立连接)

前言 在网络通信的世界里&#xff0c;可靠传输协议&#xff08;TCP&#xff09;扮演着重要的角色&#xff0c;它保证了数据包能够按顺序、完整地从发送端传送到接收端。TCP协议中有一个至关重要的机制——三次握手。这一过程确保了两个TCP设备在开始数据传输之前建立起一个稳定…

计算机毕业设计 | SpringBoot 房屋租赁网 房屋租赁平台(附源码)

1&#xff0c;绪论 1.1 背景调研 在房地产行业持续火热的当今环境下&#xff0c;房地产行业和互联网行业协同发展&#xff0c;互相促进融合已经成为一种趋势和潮流。本项目实现了在线房产平台的功能&#xff0c;多种技术的灵活运用使得项目具备很好的用户体验感。 这个项目的…

【Linux】环境基础开发工具的使用(一)

前言&#xff1a;在此之前我们学习了一些Linux的权限&#xff0c;今天我们进一步学习Linux下开发工具的使用。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:Linux的深度刨析 &#x1f448; &#x1f4af;代码仓库:卫卫周大胖的学习日记…

长时间佩戴骨传导耳机会损害听力吗?骨传导耳机有什么优点?

如果是正常的使用&#xff0c;是不会对损伤听力的&#xff0c;如果错误的使用&#xff0c;比如说高音量使用&#xff0c;长时间也会出现一定的危害&#xff0c;而且骨传导耳机相比于传统耳机&#xff0c;其优点也要多一些&#xff0c;下面一起看下骨传导耳机都有哪些优点吧。 佩…

dockerpipwork相关测试过程

pipework可以减轻docker实施过程中的工作量&#xff0c;在网上也找了几篇类似的文章&#xff0c;按照相应配置&#xff0c;结果并不相同 如下测试过程记录下&#xff1a; docker run -it --rm --name c1 busybox docker run -it --rm --name c2 busyboxpipework br1 c1 192…

玛格全屋定制携手君子签,实现业务信息、流程、合同全面数字化

中国定制家居领导品牌——玛格全屋定制携手君子签&#xff0c;部署玛格业务系统&#xff0c;将电子签章系统与供应链上下游业务合同签署场景融合&#xff0c;通过无纸化、电子化的签署环境&#xff0c;打造业务“线上审批、签署、归档”闭环&#xff0c;助推业务减负提效。 电…

Redis 布隆过滤器

布隆过滤器 这一篇文章主要是记录布隆过滤器的使用和认识 主要参考了如下的blog https://blog.csdn.net/weixin_42972832/article/details/131211665 他讲的还不错 简单的来说,布隆过滤器,实际上就像是一个集合,拿redis的key来举例来说,布隆过滤器的设置就是去过滤不属于redi…

5、混合模型(Hybrid Models)

将两个预测模型的优势结合起来。 文章目录 1、简介2、组件和残差3、混合预测与残差3.1设计混合模型4、示例 - 美国零售销售1、简介 线性回归擅长推断趋势,但不能学习交互。XGBoost擅长学习交互,但不能推断趋势。在这个课程中,我们将学习如何创建"混合"预测器,这…

分割头篇 | 原创自研 | YOLOv8 更换 SEResNeXtBottleneck 头 | 附详细结构图

左图:ResNet 的一个模块。右图:复杂度大致相同的 ResNeXt 模块,基数(cardinality)为32。图中的一层表示为(输入通道数,滤波器大小,输出通道数)。 1. 思路 ResNeXt是微软研究院在2017年发表的成果。它的设计灵感来自于经典的ResNet模型,但ResNeXt有个特别之处:它采用…

BeanFactory创建过程(基于Servlet)

BeanFactory创建过程&#xff08;基于Servlet&#xff09; 1. 概述1.2 那么问题就来了1.2.1 谁负责AnnotationConfigServletWebServerApplicationContext实例呢&#xff1f;1.2.2 ApplicationContextFactory初始化过程又是怎么样的&#xff1f; 1.3 总结 2. 最后 1. 概述 Anno…

【C++零散小记录】类中重载返回类型为常量引用和非常量引用的函数

#include <iostream>class hyp { public://这里后面必须要加上const修饰符&#xff0c;否则这两个test函数不能重载int const& test() const{std::cout << "const" << std::endl;return num;}//int &test()//{// std::cout << &qu…

吉大计网笔记

Osi七层模型 物理层&#xff1a;比特流的透明传输 数据链路层&#xff1a;ip数据包装成帧并传输&#xff0c;处理相邻节点的数据传输。 网络层&#xff1a;选择路由和交换节点&#xff0c;处理任意节点的数据传输。 运输层&#xff1a;主机进程的数据传输服务&#xff0c;端到端…

雷达DoA估计的跨行业应用--麦克风阵列声源定位(Matlab仿真)

一、概述 麦克风阵列&#xff1a; 麦克风阵列是由一定数目的声学传感器&#xff08;麦克风&#xff09;按照一定规则排列的多麦克风系统&#xff0c;而基于麦克风阵列的声源定位是指用麦克风拾取声音信号&#xff0c;通过对麦克风阵列的各路输出信号进行分析和处理&#xff0c;…

Kubernetes实战(二十一)-event事件持久化

默认情况下&#xff0c; K8S 会将事件保留在 etcd 中一个小时&#xff0c;超过1小时的事件将无法看到&#xff0c;所以 K8S 默认保留事件的时间不足以来更深入的了解集群&#xff0c;所以将事件导出到集群外存储是有必要的&#xff0c;以实现可观测性和告警。 Event事件持久化…

央视见证|“看见中国汽车”走进首家汽车供应链企业东软睿驰

由工业和信息化部支持指导&#xff0c;中国汽车工业协会和央视网联合出品的2023《看见中国汽车》专题东软睿驰篇正式上线&#xff0c;记录品牌向上专项行动走进首家汽车供应链企业&#xff0c;展示东软睿驰围绕“成为OEM 软件定义汽车时代最可信赖的合作伙伴”核心战略的创新发…

IT行业证书大揭秘:哪些证书含金量最高?

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f4d2; 证书 &#x1f4d2; &#x1f4d6; 介绍 &#x1f4d6; 在IT行业&#xff0c;有许多证书被认为是含金量高的&#xff0c;可以帮助个人在职业发展中取得重要的竞争优势。以下是一些IT行业中被认为含金量高的证书&#x…

信用飞品牌升级——全方位优化用户体验 更周到更暖心

在当今社会,金融科技的迅速发展让信贷服务变得更加便捷。信贷品牌的崛起和发展,正在改变着人们的金融生活。消费者在选择信贷服务时,不仅仅关注信贷的额度和利率,服务体验也成为了一大关注点。一个优秀的信贷品牌,不仅要能满足用户的基本信贷需求,更要在服务体验上赢得用户的认…

基于 Echarts 的 Python 图表库:Pyecahrts交互式的日历图和3D柱状图

文章目录 概述一、日历图和柱状图介绍1. 日历图基本概述2. 日历图使用场景3. 柱状图基本概述4. 柱状图使用场景 二、代码实例1. Pyecharts绘制日历图2. Pyecharts绘制2D柱状图3. Pyecharts绘制3D柱状图 总结 概述 本文将引领读者深入了解数据可视化领域中的两个强大工具&#…

【2024全网最详细】Google 搜索命令终极指南

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 你是否尝试过使用 Google 搜索作为免费的 SEO …

【解刊】审稿人极其友好!中科院2区SCI,3个月录用,论文质量要求宽松!

计算机类 • 高分快刊 今天带来Springer旗下计算机领域高分快刊&#xff0c;有投稿经验作者表示期刊审稿人非常友好&#xff0c;具体情况一起来看看下文解析。如有投稿意向可重点关注&#xff1a; 01 期刊简介 Complex & Intelligent Systems ✅出版社&#xff1a;Sprin…