当前位置: 首页 > news >正文

图数据库Neo4j——SpringBoot使用Neo4j 简单增删改查 复杂查询初步

在这里插入图片描述

前言


图形数据库是专门用于存储图形数据的数据库,它使用图形模型来存储数据,并且支持复杂的图形查询。常见的图形数据库有Neo4j、OrientDB等。

Neo4j是用Java实现的开源NoSQL图数据库,本篇博客介绍如何在SpringBoot中使用Neo4j图数据库,如何进行简单的增删改查,以及如何进行复杂的查询。

本篇博客相关代码的git网址如下:

https://gitee.com/pet365/spring-boot-neo4j

关于Neo4j的博客文章如下:

  • 图数据库Neo4j——Neo4j简介、数据结构 & Docker版本的部署安装 & Cypher语句的入门

目录

  • 前言
  • 引出
  • springBoot整合
    • 1、引入依赖
    • 2、配置文件
    • 3、实体类定义
    • 4、dao继承Neo4jRepository
    • 复杂查询
      • 最短路径查询
      • 最小成本查询
  • 总结

引出


1.Neo4j是用Java实现的开源NoSQL图数据库;
2.SpringBoot使用Neo4j,继承Neo4jRepository进行简单增删改查;
3.使用Neo4jClient进行复杂的查询;

springBoot整合

1、引入依赖

<!--        neo4j的包--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-neo4j</artifactId></dependency>

2、配置文件

server:port: 9902
logging:level:org.springframework.data.neo4j: debug
spring:application:name: spring-neo4jdata:neo4j:database: neo4jneo4j:authentication:username: neo4jpassword: neo4j123uri: neo4j://192.168.150.101:7687

3、实体类定义

在这里插入图片描述

提取抽象类

在这里插入图片描述

不同的节点类,网点、一级转运中心、二级转运中心

在这里插入图片描述

4、dao继承Neo4jRepository

进行自定义查询:

KeywordSampleCypher snippet
AfterfindByLaunchDateAfter(Date date)n.launchDate > date
BeforefindByLaunchDateBefore(Date date)n.launchDate < date
Containing (String)findByNameContaining(String namePart)n.name CONTAINS namePart
Containing (Collection)findByEmailAddressesContains(Collection addresses) findByEmailAddressesContains(String address)ANY(collectionFields IN [addresses] WHERE collectionFields in n.emailAddresses) ANY(collectionFields IN address WHERE collectionFields in n.emailAddresses)
InfindByNameIn(Iterable names)n.name IN names
BetweenfindByScoreBetween(double min, double max) findByScoreBetween(Range range)n.score >= min AND n.score <= max Depending on the Range definition n.score >= min AND n.score <= max or n.score > min AND n.score < max
StartingWithfindByNameStartingWith(String nameStart)n.name STARTS WITH nameStart
EndingWithfindByNameEndingWith(String nameEnd)n.name ENDS WITH nameEnd
ExistsfindByNameExists()EXISTS(n.name)
TruefindByActivatedIsTrue()n.activated = true
FalsefindByActivatedIsFalse()NOT(n.activated = true)
IsfindByNameIs(String name)n.name = name
NotNullfindByNameNotNull()NOT(n.name IS NULL)
NullfindByNameNull()n.name IS NULL
GreaterThanfindByScoreGreaterThan(double score)n.score > score
GreaterThanEqualfindByScoreGreaterThanEqual(double score)n.score >= score
LessThanfindByScoreLessThan(double score)n.score < score
LessThanEqualfindByScoreLessThanEqual(double score)n.score <= score
LikefindByNameLike(String name)n.name =~ name
NotLikefindByNameNotLike(String name)NOT(n.name =~ name)
NearfindByLocationNear(Distance distance, Point point)distance( point(n),point({latitude:lat, longitude:lon}) ) < distance
RegexfindByNameRegex(String regex)n.name =~ regex
AndfindByNameAndDescription(String name, String description)n.name = name AND n.description = description
OrfindByNameOrDescription(String name, String description)n.name = name OR n.description = description (Cannot be used to OR nested properties)

在这里插入图片描述

package com.tianju.mapper;import com.tianju.entity.AgencyEntity;
import org.mapstruct.Mapper;
import org.springframework.data.neo4j.repository.Neo4jRepository;/*** 网点的mapper,比如菜鸟驿站*/
@Mapper
public interface AgencyMapper extends Neo4jRepository<AgencyEntity,Long> {/*** 根据bid 查询* @param bid 业务id* @return 网点数据*/AgencyEntity findByBid(Long bid);/*** 根据bid删除** @param bid 业务id* @return 删除的数据条数*/Long deleteByBid(Long bid);
}

在这里插入图片描述

复杂查询

在这里插入图片描述

最短路径查询

//查询两个网点之间最短路径,查询深度最大为10
MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))WHERE n.name = "北京市昌平区定泗路" AND m.name = "上海市浦东新区南汇"RETURN path

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.tianju.mapper.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.meta.Column;
import com.tianju.dto.OrganDTO;
import com.tianju.dto.TransportLineNodeDTO;
import com.tianju.entity.AgencyEntity;
import com.tianju.enums.OrganTypeEnum;
import com.tianju.mapper.TransportLineRepository;
import org.neo4j.driver.internal.InternalPoint2D;
import org.neo4j.driver.types.Path;
import org.neo4j.driver.types.Relationship;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.Optional;@Component
public class TransportLineRepositoryImpl implements TransportLineRepository {@Autowiredprivate Neo4jClient neo4jClient;/*** 查询最短路线* @param start 开始网点* @param end   结束网点* @return*/@Overridepublic TransportLineNodeDTO findShortestPath(AgencyEntity start, AgencyEntity end) {// 获取网点数据在Neo4j中的类型 @Node("AGENCY") @Node("OLT")String type = AgencyEntity.class.getAnnotation(Node.class).value()[0];// 构造Sql语句 $startId
//        String cql = "MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))\n" +
//                "WHERE n.bid = $startId AND m.bid = $endId\n" +
//                "RETURN path";String cql = StrUtil.format("MATCH path = shortestPath((n:{}) -[*..10]->(m:{})) " +"WHERE n.bid = $startId AND m.bid = $endId " +"RETURN path",type,type);// 执行自定义查询Neo4jClient.RecordFetchSpec<TransportLineNodeDTO> recordFetchSpec = neo4jClient.query(cql).bind(start.getBid()).to("startId") // 替换 $startId.bind(end.getBid()).to("endId")  // 替换 $endId.fetchAs(TransportLineNodeDTO.class)   // 设置响应类型,指定为 TransportLineNodeDTO 类型.mappedBy((typeSystem, record) -> {    // 设置结果集映射Path path = record.get(0).asPath();// 得到第一条路线TransportLineNodeDTO transportLineNodeDTO = new TransportLineNodeDTO();path.nodes().forEach(node -> { // 将每个节点信息封装成一个 OrganDto// 获得节点的 键值对 address: 上海市转运中心;bid:8002Map<String, Object> map = node.asMap();// {name=北京市昌平区定泗路,// location=Point{srid=4326, x=116.37212849638287, y=40.11765281246394},// address=北七家镇定泗路苍龙街交叉口, bid=100280, phone=010-86392987}System.out.println("map: "+map);// 把键值对转换成对象 OrganDTOOrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class);// organDTO:// OrganDTO(id=100280, name=北京市昌平区定泗路, type=null, phone=010-86392987,// address=北七家镇定泗路苍龙街交叉口, latitude=null, longitude=null)// type,latitude,longitude 没有映射成功System.out.println("organDTO: "+organDTO);// 获得标签的名称 OLT,TLT,String first = CollUtil.getFirst(node.labels());// 根据OLT获得枚举类型 OLT(1, "一级转运中心"),OrganTypeEnum organTypeEnum = OrganTypeEnum.valueOf(first);// 再获得枚举类型的 code :1、2、3organDTO.setType(organTypeEnum.getCode()); // 设置类型的映射// 经纬度 "location": point({srid:4326, x:121.59815370294322, y:31.132409729356993})InternalPoint2D location = MapUtil.get(map, "location", InternalPoint2D.class); // 经纬度 BeanUtil.getProperty(map.get("location"),"x");organDTO.setLatitude(location.x());  // 设置经纬度映射organDTO.setLongitude(location.y()); // 经纬度映射// OrganDTO(id=100280, name=北京市昌平区定泗路, type=3,// phone=010-86392987, address=北七家镇定泗路苍龙街交叉口,// latitude=116.37212849638287, longitude=40.11765281246394)System.out.println("organDTO: "+organDTO);transportLineNodeDTO.getNodeList().add(organDTO);});System.out.println("transportLineNodeDTO: "+transportLineNodeDTO);path.relationships().forEach(relationship -> {// 路径下面的关系Map<String, Object> map = relationship.asMap();Double cost = MapUtil.get(map, "cost", Double.class);transportLineNodeDTO.setCost(cost + transportLineNodeDTO.getCost());});System.out.println("transportLineNodeDTO: "+transportLineNodeDTO);return transportLineNodeDTO;});Optional<TransportLineNodeDTO> one = recordFetchSpec.one(); // Optional,1.8提供的,可以处理null的情况return one.orElse(null); // 如果为null,就返回null,如果不是null,就返回结果}
}

最小成本查询

在这里插入图片描述

package com.tianju.mapper.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.meta.Column;
import com.tianju.dto.OrganDTO;
import com.tianju.dto.TransportLineNodeDTO;
import com.tianju.entity.AgencyEntity;
import com.tianju.enums.OrganTypeEnum;
import com.tianju.mapper.TransportLineRepository;
import org.neo4j.driver.internal.InternalPoint2D;
import org.neo4j.driver.types.Path;
import org.neo4j.driver.types.Relationship;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.Optional;@Component
public class TransportLineRepositoryImpl implements TransportLineRepository {@Autowiredprivate Neo4jClient neo4jClient;@Overridepublic TransportLineNodeDTO findCostLeastPath(AgencyEntity start, AgencyEntity end) {String type = AgencyEntity.class.getAnnotation(Node.class).value()[0];String cqlB = "MATCH path = (n:{}) -[*..10]->(m:{}) " +"WHERE n.bid = $startId AND m.bid = $endId " +"UNWIND relationships(path) AS r " +"WITH sum(r.cost) AS cost, path " +"RETURN path ORDER BY cost ASC, LENGTH(path) ASC LIMIT 1";String cql = StrUtil.format(cqlB, type,type);Optional<TransportLineNodeDTO> one = neo4jClient.query(cql).bind(start.getBid()).to("startId").bind(end.getBid()).to("endId").fetchAs(TransportLineNodeDTO.class).mappedBy(((typeSystem, record) -> {Path path = record.get(0).asPath();TransportLineNodeDTO transportLineNodeDTO = new TransportLineNodeDTO();path.nodes().forEach(node -> {Map<String, Object> map = node.asMap();OrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class);// 获得标签的名称 OLT,TLT,String first = CollUtil.getFirst(node.labels());// 根据OLT获得枚举类型 OLT(1, "一级转运中心"),OrganTypeEnum organTypeEnum = OrganTypeEnum.valueOf(first);// 再获得枚举类型的 code :1、2、3organDTO.setType(organTypeEnum.getCode()); // 设置类型的映射// 经纬度 "location": point({srid:4326, x:121.59815370294322, y:31.132409729356993})InternalPoint2D location = MapUtil.get(map, "location", InternalPoint2D.class); // 经纬度 BeanUtil.getProperty(map.get("location"),"x");organDTO.setLatitude(location.x());  // 设置经纬度映射organDTO.setLongitude(location.y()); // 经纬度映射transportLineNodeDTO.getNodeList().add(organDTO);});path.relationships().forEach(relationship -> {// 路径下面的关系Map<String, Object> map = relationship.asMap();Double cost = MapUtil.get(map, "cost", Double.class);transportLineNodeDTO.setCost(cost + transportLineNodeDTO.getCost());});return transportLineNodeDTO;})).one();return one.orElse(null);}private void findShortestPathMy(){String cql = "MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY)) " +"WHERE n.bid = 210127 AND m.bid = 100260 " +"RETURN path";// 执行自定义查询Neo4jClient.UnboundRunnableSpec query = neo4jClient.query(cql);ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();}
}

在这里插入图片描述


总结

1.Neo4j是用Java实现的开源NoSQL图数据库;
2.SpringBoot使用Neo4j,继承Neo4jRepository进行简单增删改查;
3.使用Neo4jClient进行复杂的查询;

相关文章:

图数据库Neo4j——SpringBoot使用Neo4j 简单增删改查 复杂查询初步

前言 图形数据库是专门用于存储图形数据的数据库&#xff0c;它使用图形模型来存储数据&#xff0c;并且支持复杂的图形查询。常见的图形数据库有Neo4j、OrientDB等。 Neo4j是用Java实现的开源NoSQL图数据库&#xff0c;本篇博客介绍如何在SpringBoot中使用Neo4j图数据库&…...

-- Could NOT find livox_ros_driver (missing: livox_ros_driver_DIR)

原因 缺少livox_ros_driver 包 解决办法如下 livox_ros_driver 地址 https://github.com/Livox-SDK/livox_ros_driver 下载下来放入ros的工作目录...

山东餐饮类行业可以办理那些认证?

在招投标中很多企业会因为缺少一些证书从而丢失加分项&#xff0c;所以很多行业都会关注那些针对性比较强的企业认证&#xff0c;今天就来讲一下餐饮类行业招投标有哪些证书可以帮助企业中标&#xff1f; 一、ISO三体系认证 ISO9001 质量管理体系 质量是取得成功的关键。由…...

【抖音自动评论的软件】评论888无偿分享,和其开发技术与开发流程的分享

先来看成果&#xff0c;↑↑需要的同学可看我名字↖↖↖↖↖&#xff0c;或评论888无偿分享 短视频作为互联网时代的重要产物&#xff0c;已经成为人们生活中不可或缺的一部分。那么&#xff0c;如何通过短视频平台进行有效的运营和评论呢&#xff1f;本文将为您详细解析。 一…...

挑战100天 AI In LeetCode Day02(2)

挑战100天 AI In LeetCode Day02&#xff08;2&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-42.1 题目2.2 题解 三、面试经典 150 题-43.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&#xff0c;面向程序…...

《尚医通》Vue3 项目+TypeScript 前端项目(持续更新,附带源码)

尚硅谷vue项目实战《尚医通》&#xff0c;Vue3项目TypeScript前端项目_哔哩哔哩_bilibili尚硅谷vue项目实战《尚医通》&#xff0c;Vue3项目TypeScript前端项目共计71条视频&#xff0c;包括&#xff1a;001_开篇介绍、002_尚医通项目的简介、003_Vite构建化工具初始化项目等&a…...

仪表盘 gauge

option {tooltip: {formatter: {a} <br/>{b} : {c}%},series: [{name: Pressure,type: gauge,startAngle: 225, // 起始角度&#xff0c;同极坐标endAngle: -45, // 终止角度&#xff0c;同极坐标// axisLine: {// //坐标轴轴线// show: false// },// splitLine: {//…...

常见面试题-MySQL专栏(三)MVCC、BufferPool

typora-copy-images-to: imgs 了解 MVCC 吗&#xff1f; 答&#xff1a; MVCC&#xff08;Multi-Version Concurrency Control&#xff09; 是用来保证 MySQL 的事务隔离性的&#xff0c;对一行数据的读和写两个操作默认是不会通过加锁互斥来保证隔离性&#xff0c;避免了频…...

CDN加速:国内外价格与企业云服务最佳搭配方案

随着互联网的快速发展&#xff0c;CDN&#xff08;内容分发网络&#xff09;已经成为了企业提供高质量、高速度内容传递的不可或缺的工具。CDN通过将内容分发到离用户更近的服务器上&#xff0c;提高了网站性能&#xff0c;减少了加载时间&#xff0c;改善了用户体验。在本文中…...

uni-app小程序使用vant

步骤一&#xff1a;安装 Vant Weapp # 通过 npm 安装 npm i vant/weapp -S --production# 通过 yarn 安装 yarn add vant/weapp --production# 安装 0.x 版本 npm i vant-weapp -S --production步骤二&#xff1a;在根目录下创建“wxcomponents”文件夹 步骤三&#xff1a;找…...

C-DS二叉树_另一棵树的子树

Description 给你两棵二叉树tree1和tree2,检验tree1中是否包含和tree2具有相同结构和结点值的子树。如果存在,输出true;否则,输出false。 Input 第一行输入t,表示有t个测试样例。 第二行首先输入n1,接着输入n1个整数,表示二叉树tree1。 第三行首先输入n2,接着输入n…...

祝贺璞华大数据产品入选中国信通院“铸基计划”

武汉璞华大数据技术有限公司HawkEye设备数字化管理平台产品&#xff0c;凭借优秀的产品技术能力&#xff0c;通过评估后&#xff0c;入选中国信通院“铸基计划”《高质量数字化转型产品及服务全景图(2023&#xff09;》的工业数字化领域。 “铸基计划”是中国信通院推出的高质量…...

WebDAV之π-Disk派盘 + MiXplorer

MiXplorer是一款非常强大实用的手机文档管理器,能给用户提供了一系列的文档处理功能,包括本地文件浏览、文件排序、文件筛选、切换视图、新建文件、添加收藏等等,同时还能将你手机里的所有文件都罗列出来,简洁明了,让用户一眼就能够找到相应的文件并对其进行编辑,或是删除…...

java依赖的jar包下载

不需要依赖maven&#xff0c;直接下载jar。 仓库服务...

苹果加大对印度的扶持,提高在其生产iphone的比重

KlipC报道&#xff1a;跟踪苹果产业链&#xff0c;有分析师预计2023年全球约12%-14%的iphone在印度生产&#xff0c;预计2024年&#xff0c;印度将生产20%-25%的iphone。 KlipC的合伙人Andi D表示&#xff1a;“近年来随着苹果对中国的以来&#xff0c;印度已经成为高科技制造和…...

【漏洞复现】typecho_v1.0-14.10.10_unserialize

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 漏洞利用GetShell 下载链接&#xff1a;https://pan.baidu.com/s/1z0w7ret-uXHMuOZpGYDVlw 提取码&#xff1a;lt7a 首页 漏洞点&#xff1a;/install.php?finish 漏洞利用 …...

Linux常见面试题

1、 冯诺依曼体系的要点是&#xff1a; 数字计算机的数制采用二进制&#xff0c;bit 位, byte 字节 1 byte 8 bit计算机应该按照程序顺序执行计算机硬件由运算器、控制器、存储器、输入设备和输出设备五大部分组成 2、计算机的硬件五个组件 控制器: 指挥系统运算器: 数学和逻…...

HarmonyOS ArkTS基础知识

概述 上一节&#xff0c;学习了TypeScript的基础语法&#xff0c;而在鸿蒙开发当中&#xff0c;有基于自己的编程语言&#xff0c;便是ArkTS。它是一种声明式UI的编程范式的语言&#xff0c;开发框架如下图所示&#xff1a; 根据框架图&#xff0c;分析&#xff0c;我将它大致…...

嵌入式课后习题第一章解答

嵌入式系统是一种以应用为中心&#xff0c;以计算机技术为基础&#xff0c;软/硬件可裁剪&#xff0c;适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机。它与通用PC的区别主要体现在以下几个方面&#xff1a; 应用针对性&#xff1a;嵌入式系统是专为特定应…...

postMessage

A:端口3000 import React, { useEffect } from react;function App() {useEffect(() > {const childWindow document.getElementById(child).contentWindow;const sendMessageToChild () > {childWindow.postMessage("主页面消息", "http://localhost:…...

SMUDebugTool终极指南:如何深度调试AMD Ryzen处理器底层硬件

SMUDebugTool终极指南&#xff1a;如何深度调试AMD Ryzen处理器底层硬件 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: http…...

避坑指南:UE5 VaRest插件处理JSON数组和嵌套对象的几个常见错误

UE5 VaRest插件处理JSON数组和嵌套对象的避坑指南 在UE5开发中&#xff0c;VaRest插件因其便捷的HTTP请求和JSON处理能力而广受欢迎。然而&#xff0c;当面对复杂的JSON数据结构时&#xff0c;许多开发者会遇到各种"坑"。本文将深入剖析VaRest在处理JSON数组和嵌套对…...

SMUDebugTool终极指南:轻松解锁AMD Ryzen处理器的隐藏性能

SMUDebugTool终极指南&#xff1a;轻松解锁AMD Ryzen处理器的隐藏性能 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https:…...

AI深度学习中的张量计算理论与实践

AI深度学习中的张量计算理论与实践...

Qt图形界面开发:Phi-3-mini生成UI代码片段与信号槽连接示例

Qt图形界面开发&#xff1a;Phi-3-mini生成UI代码片段与信号槽连接示例 1. 引言&#xff1a;当AI遇上Qt界面开发 作为一名Qt开发者&#xff0c;你是否经常陷入这样的困境&#xff1a;每次新建一个对话框或窗口&#xff0c;都要重复编写相似的UI初始化代码&#xff1f;特别是当…...

告别繁琐:用快马生成openclaw自动化安装脚本,效率提升300%

最近在折腾openclaw这个工具时&#xff0c;发现手动安装过程实在太磨人了。每次都要反复查文档、处理各种依赖报错&#xff0c;光是环境配置就能耗掉大半天。于是琢磨着能不能搞个自动化方案&#xff0c;把安装流程标准化。试了几个方法后&#xff0c;终于在InsCode(快马)平台上…...

如何高效管理ComfyUI插件:完整指南与最佳实践

如何高效管理ComfyUI插件&#xff1a;完整指南与最佳实践 【免费下载链接】ComfyUI-Manager ComfyUI-Manager is an extension designed to enhance the usability of ComfyUI. It offers management functions to install, remove, disable, and enable various custom nodes …...

OpenClaw数据清洗神器:Qwen3-14b_int4_awq智能修复CSV文件异常值

OpenClaw数据清洗神器&#xff1a;Qwen3-14b_int4_awq智能修复CSV文件异常值 1. 为什么需要智能数据清洗 作为经常处理实验数据的科研狗&#xff0c;我每天至少要花2小时在数据清洗上。上周处理一组气候观测数据时&#xff0c;发现某个气象站的温度记录里混入了几个"999…...

3步搞定PDF处理难题:Windows版Poppler让文档操作变得如此简单

3步搞定PDF处理难题&#xff1a;Windows版Poppler让文档操作变得如此简单 【免费下载链接】poppler-windows Download Poppler binaries packaged for Windows with dependencies 项目地址: https://gitcode.com/gh_mirrors/po/poppler-windows 您是否经常需要从PDF文件…...

Autovisor:5分钟快速上手的智慧树自动化学习终极指南

Autovisor&#xff1a;5分钟快速上手的智慧树自动化学习终极指南 【免费下载链接】Autovisor 2025智慧树刷课脚本 基于Python Playwright的自动化程序 [有免安装版] 项目地址: https://gitcode.com/gh_mirrors/au/Autovisor Autovisor是一款专为智慧树在线课程平台设计的…...