Metabase学习教程:系统管理-7

news2024/11/19 5:31:57

使用MetabaseAPI

MetabaseAPI简介。

本文介绍如何使用Metabase的API。我们自己使用该API连接前端和后端,因此您可以编写Metabase几乎可以执行的所有操作。

警告:MetabaseAPI可能会更改

开始之前有两个注意事项:

  1. API可能会更改API与前端紧密耦合,在不同版本之间可能会发生变化。端点可能不会有那么大的变化(现有的API端点很少被更改,也很少被删除),但是如果您编写代码来使用API,您可能需要在将来更新它。
  2. API没有版本。意思是:它可以在不同的版本之间进行更改,因此不要期望为了使用稳定API而停留在Metabase的特定版本上。

MetabaseAPI入门

为了简单起见,我们将使用古老的命令行实用程序curl对于我们的API调用示例;您还可以考虑使用一个专用工具来开发API请求(比如Postman). 要跟上,你可以启动一个新的Metabase本地实例到处玩。

使用session token验证您的请求

你需要一个session token以验证您的请求,否则Metabase将拒绝与您交谈。若要获取session token,请向/api/session具有用户名和密码的终结点:

curl -X POST \

  -H "Content-Type: application/json" \

  -d '{"username": "person@metabase.com", "password": "fakepassword"}' \

  http://localhost:3000/api/session

如果使用的是远程服务器,则需要替换localhost:3000你的服务器地址。此请求将返回一个JSON对象,该对象的键名为id以及令牌作为密钥的值,例如:

{“id”:“38f4939c-ad7f-4cbe-ae54-30946daf8593”}

您将需要在后续请求的标头中包括该session token,如下所示:

"X-Metabase-Session: 38f4939c-ad7f-4cbe-ae54-30946daf8593"

关于会话的一些注意事项:

  • 默认情况下,会话有效期为14。您可以通过设置环境变量来配置此会话持续时间MB_SESSION_AGE(值以分钟为单位)。
  • 你应该缓存凭据重复使用它们直到它们过期,因为登录是安全的速率限制的。
  • 无效和过期的session token返回401(未授权)状态代码。
  • 优雅地处理401状态码。我们建议您编写代码以获取新的session token,并在API返回401 status code.
  • 有些端点要求用户是管理员,也称为超级用户。需要管理员或超级用户状态(admin=superuser)的终结点通常在其文档中这样说。他们将返回一个403 (Forbidden) status code 如果当前用户不是管理员。
  • 如果您想使用替代身份验证机制请随意投票功能请求.

获取请求示例

下面是一个示例API请求(注意session token),它将/api/user/current端点,它返回有关当前用户的信息:

curl -X GET \

  -H "Content-Type: application/json" \

  -H "X-Metabase-Session: 38f4939c-ad7f-4cbe-ae54-30946daf8593" \

  http://localhost:3000/api/user/current

上面的请求返回一个JSON对象(格式化为可读性):

{

    "email": "person@metabase.com",

    "ldap_auth": false,

    "first_name": "Human",

    "locale": null,

    "last_login": "2020-08-31T13:08:50.203",

    "is_active": true,

    "is_qbnewb": false,

    "updated_at": "2020-08-31T13:08:50.203",

    "group_ids": [

        1,

        2

    ],

    "is_superuser": true,

    "login_attributes": null,

    "id": 1,

    "last_name": "Person",

    "date_joined": "2020-08-19T10:50:46.547",

    "personal_collection_id": 1,

    "common_name": "Human Person",

    "google_auth": false

}

POST请求示例

您还可以使用一个文件来存储POST请求的JSON负载。这使得您可以很容易地对API发出一组预定义的请求。

curl -H @header_file.txt -d @payload.json http://localhost/api/card

下面是一个JSON文件的示例@payload.json在上面的命令中)创建一个问题:

{

  "visualization_settings": {

    "table.pivot_column": "QUANTITY",

    "table.cell_column": "SUBTOTAL"

  },

  "description value": "A card generated by the API",

  "collection_position": null,

  "result_metadata": null,

  "metadata_checksum": null,

  "collection_id": null,

  "name": "API-generated question",

  "dataset_query": {

    "database": 1,

    "query": {

      "source-table": 2

    },

    "type": "query"

  },

  "display": "table"

}

该请求生成了如图1所示的问题。

图1.API生成的Metabase中的问题:一个命令示例数据库中的表

使用开发人员工具查看Metabase如何发出请求

如果自动生成的API文档不清楚,可以使用FirefoxChromeEdge等浏览器附带的开发工具来查看Metabase的请求和响应(图2)。

图2。使用Firefox的“网络”选项卡检查用户单击以保存在查询编辑器中创建的问题时Metabase发送的JSON请求负载。

Metabase应用程序中,执行要编写脚本的操作,例如添加用户或创建仪表板。然后使用浏览器中的开发人员工具查看执行该操作时向服务器发出的请求Metabase

使用MetabaseAPI可以做的一些事情

设置Metabase实例

除了使用环境变量,您可以使用MetabaseAPI来设置Metabase的实例。一旦您使用首选方法,并且Metabase服务器已启动并正在运行,您可以通过发布到特定端点来创建第一个用户(作为管理员),/api/setup.这个/api/setup终结点:

  • 将第一个用户创建为管理员(超级用户)。
  • 让他们登录。
  • 返回会话ID。

然后可以使用/api/setup端点,使用/api/emai端点,并使用/api/setup/admin_checklist验证安装进度的终结点。有关管理面板中检查表的图形表示,请参见图3

图3.Admin用于设置Metabase以充分利用应用程序的清单。

添加数据源

您可以使用POST /api/database/,并使用/api/setup/validate终结点。将数据库连接到Metabase实例后,可以重新扫描数据库并更新架构元数据。你甚至可以加上我们的信任示例数据库作为实例的新数据库POST /api/database/sample_database.

下面是一个示例数据库创建调用红移数据库。

curl -s -X POST \

    -H "Content-type: application/json" \

    -H "X-Metabase-Session: ${MB_TOKEN}" \

    http://localhost:3000/api/database \

    -d '{

        "engine": "redshift",

        "name": "Redshift",

        "details": {

            "host": "redshift.aws.com",

            "port": "5432",

            "db": "dev",

            "user": "root",

            "password": "password"

        }

    }'

设置用户、组和权限

你可以使用/api/user要创建、更新和禁用用户的终结点,或/api/permissions要设置组或的终结点向其添加用户。以下是创建用户的curl命令示例:

curl -s "http://localhost:3000/api/user" \

    -H 'Content-Type: application/json' \

    -H "X-Metabase-Session: ${MB_TOKEN}" \

    -d '{

    "first_name":"Basic",

    "last_name":"User",

    "email":"basic@somewhere.com",

    "password":"Sup3rS3cure_:}"

}'

生成报告

Metabase中,报告被称为仪表板。您可以使用/api/dashboard终结点。你可以创建新仪表板具有POST /api/dashboard/,和将保存的问题添加到仪表板[POST/api/dashboard/:id/cards].

有用的端点

下面端点列中的链接将带您转到该端点可用的第一个操作,该操作通常按字母顺序为删除操作。您可以在API文档中向下滚动查看该端点的操作和url的完整列表,并查看每个操作和url的描述。

说明

终结点

集合

集合是组织仪表盘、保存的问题和定时器的好方法。

/api/collection

仪表板

仪表板是由一组问题和文本卡组成的报告。

/api/dashboard

数据库

获取数据库、字段、模式、主(实体)键、表列表等。

/api/database

电子邮件

更新电子邮件设置并发送测试电子邮件。

/api/email

嵌入

使用签名的jwt获取嵌入式卡和仪表板上的信息。

/api/embed

指标

指标是节省的计算(如收入)。创建和更新指标,返回相关实体,恢复到以前的版本,等等。

/api/metric

权限

Metabase使用组管理对数据库和集合的权限。创建权限组、向组添加和删除用户、检索所有权限组的图形,等等。

/api/permissions

搜索

在卡片(问题)、仪表板、集合和脉冲中搜索子字符串。

/api/search

分段

分段是过滤器的命名集(如活动用户)。创建和更新段,恢复到以前的版本,等等。

/api/segment

会话

使用令牌重置密码,使用Google Auth登录,发送密码重置电子邮件,等等。

/api/sessions

设置

创建/更新全局应用程序设置。

/api/setting

查询

使用API执行查询并以指定格式返回查询结果。

/api/dataset

问题

问题(API中称为卡片)是查询及其可视化结果。

/api/card

还有一些很酷的端点需要检查,比如api/database/:virtual-db/metadata,这是用来愚弄前端,以便它可以治疗保存的问题就好像它们是虚拟数据库中的表一样。这就是Metabase如何让您将保存的问题当作数据源使用。

文件包含API端点的完整列表以及每个端点的文档,所以仔细研究一下,看看您能找到哪些其他很酷的端点。

端点引用会使用Metabase的新版本定期更新。也可以通过运行以下命令生成引用:

java -jar metabase.jar api-documentation

运行自定义查询

用我们的查询编辑器保存在我们自定义的基于JSON的查询语言MBQL中。您可以查看MBQL的限定语法,以及一个(不完整)MBQL参考文件了解MBQL背后的一些设计理念。

为了熟悉MBQL,我们建议使用Metabase应用程序来构建GUI问题,然后使用浏览器的开发人员工具查看Metabase如何使用查询格式化请求正文。

三种语言示例

Curl是探索api的一个方便工具,但是如果您要将Metabase集成到一个大型数据生态系统中,您可能会使用其他工具。演示如何使用PythonRNode访问APIjs,让我们提出两个问题。第一个(如图4所示)按类别查找超过100美元的订单的平均税前价值。它是公开共享的-本教程解释如何做到这一点。第二个问题,如图5所示,统计数据库中的人数。它是共享的:我们把它包括进来,以展示如何区分共享问题和非共享问题。

图4。按产品类别计算100美元以上订单平均价值的公开问题查询编辑器

图5。计算数据库中人数的非公开问题的查询编辑器。

Python

我们的第一个示例是用Python编写的。像大多数数据科学项目一样,它使用请求用于发送HTTP请求和Pandas为了管理表格数据,我们从导入这些数据开始:

import requests

import pandas as pd

下一步是获取一个session token来验证将来的所有请求。(该示例使用我的凭据您可以用您的用户名和密码替换它们,但请确保别这样将这些值提交到版本控制存储库。)为了从结果中获取令牌,我们将后者转换为JSON并查找ID。我们将其存储在字典中,并使用正确的密钥以备将来使用:

response = requests.post('http://localhost:3000/api/session',

                         json={'username': 'greg@metabase.com',

                               'password': 'database1'})

session_id = response.json()['id']

headers = {'X-Metabase-Session': session_id}

我们现在可以询问Metabase哪些问题具有公共id,也就是说,哪些问题已经被共享,以便我们可以远程调用它们。正如下面的代码所示,当我们要求所有的卡片时,我们会得到一个包含所有问题信息的列表;只有那些有public_uuid字段可调用:

response = requests.get('http://localhost:3000/api/card',

                         headers=headers).json()

questions = [q for q in response if q['public_uuid']]

print(f'{len(questions)} public of {len(response)} questions')

果然,输出告诉我们有两个问题,但只有一个是公开的:

1 public of 2 questions

让我们获取一些关于公共问题的信息并打印标题:

uuid=questions[0]['public_uuid']
response= requests.get(f'http://localhost:3000/api/public/card/{uuid}',
                        headers=headers)
print(f'First title: {response.json()["name"]}')
First title: Average value of orders over $100 grouped by category
 

最后,我们可以从列表中的第一个问题中提取数据。这个'数据'keyJSON响应中有很多信息;我们最感兴趣的是子关键字下的值'行',它将结果表存储在通常的列表列表表单中。让我们将其转换为Pandas数据帧并打印它:

response = requests.get(f'http://localhost:3000/api/public/card/{uuid}/query',
                        headers=headers)
rows = response.json()['data']['rows']
data = pd.DataFrame(rows, columns=['Category', 'Average'])
print('First data')
print(data)
First data
    Category     Average
0  Doohickey  114.679742
1     Gadget  123.530916
2      Gizmo  120.897286
3     Widget  122.078721
 

和潮人在一起

我们示例的R版本与Python版本具有相同的结构。像大多数数据科学家一样,我们使用tidyverse库族,让我们一起加载这些库httr对于管理HTTP请求,jsonlite用于解析JSON,以及glue对于字符串格式:

library(tidyverse)

library(httr)

library(jsonlite)

library(glue)

我们再次获得会话ID并将其保存以备将来使用:

data <- POST(

    'http://localhost:3000/api/session',

    body = list(username = 'greg@metabase.com', password = 'database1'),

    encode = 'json'

  ) %>%

  content(as = 'text', encoding = 'UTF-8') %>%

  fromJSON()

session_id <- data$id

headers <- add_headers('X-Metabase-Session' = session_id)

然后我们得到所有问题的信息,并询问哪些问题是公开的:

data <- GET('http://localhost:3000/api/card', headers) %>%
  content(as = 'text', encoding = 'UTF-8') %>%
  fromJSON()
num_questions <- data %>%
  nrow()
num_public <- data %>%
  pull(public_uuid) %>%
  discard(is.na) %>%
  length()
glue('{num_public} public of {num_questions} questions')
1 public of 2 questions

显示第一张公共卡的标题会产生与Python相同的结果,这令人放心:

uuid <- data %>%
  pull(public_uuid) %>%
  discard(is.na) %>%
  first()
data <- glue('http://localhost:3000/api/public/card/{uuid}') %>%
  GET(headers) %>%
  content(as = 'text', encoding = 'UTF-8') %>%
  fromJSON()
glue('First title: {data$name}')
First title: Average value of orders over $100 grouped by category

当我们将卡转换为tibble时,与该卡相关联的数据也是相同的,尽管R的默认显示没有提供足够多的小数位数:

data <- glue('http://localhost:3000/api/public/card/{uuid}/query') %>%
  GET(headers) %>%
  content(as = 'text', encoding = 'UTF-8') %>%
  fromJSON()
rows <- data$data$rows
colnames(rows) <- c('Category', 'Average')
rows <- rows %>% as_tibble()
rows$Average <- as.numeric(rows$Average)
glue('First data')
rows
First data
# A tibble: 4 x 2
  Category  Average
  <chr>       <dbl>
1 Doohickey    115.
2 Gadget       124.
3 Gizmo        121.
4 Widget       122.

Node.js

JavaScript是一种越来越流行的服务器端脚本语言,但与PythonR不同,JavaScript缺少一个用于数据表的单一主流库。对于我们喜欢的大型项目data-forge,但对于小例子,我们坚持Dataframe-js我们也使用 got对于HTTP请求而不是旧的request包,因为后者现在已被弃用。最后,因为我们发现async/await语法比承诺或回调更容易阅读,我们将所有代码放在async然后立即调用的函数:

const got = require("got");

const DataFrame = require("dataframe-js").DataFrame;

const main = async () => {

  // ...program goes here...

};

main();
 

我们再次从自我认证开始:

// Get a session token to authenticate all future requests.

let response = await got.post("http://localhost:3000/api/session", {

  json: { username: "greg@metabase.com", password: "database1" },

  responseType: "json",

});

session_id = response.body.id;

headers = { "X-Metabase-Session": session_id };

然后,我们会询问完整的问题列表,并对其进行筛选以选择公共问题:

response = await got.get("http://localhost:3000/api/card", {
  responseType: "json",
  headers: headers,
});
// filter for public questions
questions = response.body.filter((q) => q.public_uuid);
console.log(`${questions.length} public of ${response.body.length} questions`);
1 public of 2 questions

第一张公共卡仍然有我们以前见过的标题:

const uuid = questions[0].public_uuid;
response = await got.get(`http://localhost:3000/api/public/card/${uuid}`, {
  responseType: "json",
  headers: headers,
});
console.log(`First title: ${response.body.name}`);
First title: Average value of orders over $100 grouped by category

当我们把它的数据拉下来时,我们得到的是相同的值,尽管数字的显示方式略有不同:

response = await got.get(
  `http://localhost:3000/api/public/card/${uuid}/query`,
  {
    responseType: "json",
    headers: headers,
  },
);
const rows = response.body.data.rows;
const df = new DataFrame(rows, ["Category", "Average"]);
df.show();
| Category  | Average   |
------------------------
| Doohickey | 114.67... |
| Gadget    | 123.53... |
| Gizmo     | 120.89... |
| Widget    | 122.07... |

玩得高兴

如果您觉得本教程很有趣,您可以启动Metabase的本地实例,尝试API,玩得开心!如果你被卡住了,看看我们的论坛看看是否有人遇到了类似的问题,或张贴新的问题。

​​​​​​​Metabase中文社区

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

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

相关文章

软件测试要学会哪些东西才能拿2w+的工资?

软件开发人员的月薪达到2万还是比较轻松的&#xff0c;但是软件测试人员想要月薪过万的话&#xff0c;我认为可以从两个方面去考虑&#xff1a; 1. 一种就是项目的测试负责人&#xff1a;测试人员需要对软件的整体性能改进提出建设性方案&#xff0c;所以很多软件测试人员最终…

数据结构和算法之《栈》详解

标题&#xff1a;栈的思路及代码实现 作者&#xff1a;Ggggggtm 寄语&#xff1a;与其忙着诉苦&#xff0c;不如低头赶路&#xff0c;奋路前行&#xff0c;终将遇到一番好风景 文章目录&#xff1a; 一、栈的概念及结构 1、1 栈的概念 1、2 栈的结构 二、栈的思路及代码实现详解…

电脑技巧:推荐5个非常实用的软件

❤️作者主页&#xff1a;IT技术分享社区 ❤️作者简介&#xff1a;大家好,我是IT技术分享社区的博主&#xff0c;从事C#、Java开发九年&#xff0c;对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️个人荣誉&#xff1a; 数据库领域优质创作者&#x1f3c6;&#x…

uniapp 之使用 u-upload 组件来实现图片上传

uniapp 之使用 u-upload 组件来实现图片上传前言一、官方示例用法分析二、关闭自动上传,使用手动上传的方式,代码html 代码js 代码css 代码总结分析前言 在使用 uniapp 开发的微信小程序中使用了图片上传功能,使用了 uniapp 的图片上传组件 注意&#xff1a;我这里后端接口接收…

小程序开发平台

小程序开发平台顾名思义就是一个可以开发小程序的地方。 小程序开发平台&#xff1a;【电脑浏览器输入3M.FKW.COM了解详情】 适合群体&#xff1a;企业、机构、个体户 小程序开发方式&#xff1a; 自建——可以通过套用小程序模板&#xff0c;利用拖拽式小程序开发工具&…

某组态软件工程文件加密机制探究

某组态软件工程文件加密机制探究 前言 在工业自动化控制领域&#xff0c;组态软件是数据采集与过程控制的专用软件&#xff0c;是实现人机交互必不可少的工具。工程设计人员使用组态软件在PC机上进行工程画面组态的编辑&#xff0c;然后把编译后的组态逻辑通过以太网或串口下载…

HSRP协议(思科私有)/VRRP协议(公有)

数据来源 1、HSRP热备份路由协议&#xff08;备份网关&#xff09; 出现背景&#xff1a; 如下图一个公司拉两条网线一条用来备份网关是192.168.0.253&#xff0c;平时用的网关是254&#xff0c;如果网关是254的这条网线出问题了就可以使用备份不影响公司让人员上网&#xff…

2022最新版 Java 学习线路图

第 1 阶段 - 企业级开发 - java 基础 学习掌握本阶段的内容&#xff0c;可以实现诸如迅雷下载软件、QQ 聊天客户端、中小网站&#xff0c;例如&#xff1a;小型旅游网站、小型电商网站的开发 第 2 阶段 - 企业及开发 - 基础框架 学习掌握本阶段内容&#xff0c;可以快速、规范的…

CRC校验——以SHT4xA温湿度传感器为例

CRC校验——以SHT4xA温湿度传感器为例一、简介二、计算方法&#xff08;一&#xff09;步骤&#xff08;二&#xff09;参考代码&#xff08;C语言&#xff09;&#xff08;三&#xff09;检验&#xff1a;CRC(0xBEEF) 0x92三、参考一、简介 循环冗余校验码&#xff08;CRC&am…

[附源码]计算机毕业设计JAVA疫情防控下高校教职工健康信息管理系统

[附源码]计算机毕业设计JAVA疫情防控下高校教职工健康信息管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

试剂盒和示踪剂—艾美捷FLIVO探针活体凋亡检测分析

针对FLIVO(FLuorescence in vIVO)探针的研究&#xff0c;本篇文章推荐艾美捷ImmunoChemistry&#xff08;ICT&#xff09;FLIVO探针系列的&#xff1a;天冬氨酸蛋白酶(Caspases)活性检测试剂盒&#xff0c;及 FLIVO示踪剂&#xff0c;主要用于细胞凋亡活体检测&#xff0c;助力…

大型分布式系统下缓存应该怎么玩,才能用得高效

大家好&#xff0c;今天我们来聊一聊在大型分布式系统中&#xff0c;缓存应该怎么玩&#xff0c;从毕业到现在也有三年多了&#xff0c;大大小小的系统也经历了几十个&#xff0c;今天就从各个角度来讨论一下&#xff0c;我们的不同的缓存应该怎么玩&#xff0c;才能用的高效。…

N皇后问题(分支限界法)

问题描述&#xff1a; 在 n * n 格的棋盘上放置彼此不受攻击的 n 个皇后。按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题等价于在 n * n 的棋盘上放置 n 个皇后&#xff0c;任何 2个皇后不放在同一行或同一列或同一斜线上…

Ansys Speos | 新型计算方法:使用 GPU 提升计算速率

前言 Speos 在2022R2版本中正式推出 GPU 计算功能&#xff0c;相比于 CPU 计算&#xff0c;相同HPC32配置&#xff0c;高性能显卡在仿真计算中将会更显计算优势&#xff0c;在仿真数据量大、材料属性复杂、光源种类多的条件下&#xff0c;Speos 视觉模拟会消耗更多仿真计算时间…

ARM异常处理(1):异常类型、优先级分组和异常向量表

本系列文章将以Cortex-M3内核为例&#xff0c;对ARM的异常(exception)进行分析。 文章目录1 异常类型2 优先级3 向量表1 异常类型 Cortex-M3提供了一个功能丰富的异常体系结构&#xff0c;它支持很多系统异常和外部中断。异常编号1-15表示系统异常&#xff0c;16及以上表示外部…

0116 查找算法 Day5

剑指 Offer 04. 二维数组中的查找 在一个 n * m 的二维数组中&#xff0c;每一行都按照从左到右 非递减 的顺序排序&#xff0c;每一列都按照从上到下 非递减 的顺序排序。请完成一个高效的函数&#xff0c;输入这样的一个二维数组和一个整数&#xff0c;判断数组中是否含有该…

2022快手电商短视频运营白皮书:Q2对比Q1GMV总值增长率达12%

1、2022快手电商短视频运营白皮书&#xff1a;Q2对比Q1GMV总值增长率达12%新榜讯 12月2日&#xff0c;快手电商发布《2022快手电商短视频运营白皮书》。白皮书数据显示&#xff0c;2022年4-6月&#xff0c;随着平台商家对短视频渠道的认知提升&#xff0c;挂车短视频生产量不断…

【人脸识别】MVFace:一个优于CosFace和ArcFace的人脸识别损失

论文题目&#xff1a;《Mis-classifified Vector Guided Softmax Loss for Face Recognition》 论文地址&#xff1a;https://arxiv.org/pdf/1912.00833v1.pdf 代码地址&#xff1a;http://www.cbsr.ia.ac.cn/users/xiaobowang/ 1.背景 迄今为止&#xff0c;提出了几种基于mar…

Hashing to elliptic curve算法改进

1. 引言 前序博客有&#xff1a; ECDSA VS Schnorr signature VS BLS signature 第3节“BLS签名” 私钥pkpkpk&#xff0c;对应的公钥为PpkGPpk\times GPpkG。待签名消息mmm。 BLS signature的签名流程为&#xff1a; 1&#xff09;通过H(m)H(m)H(m)将消息mmm映射为point o…

计算机毕业论文java毕业设计选题源代码javaweb企业门户网站官网

&#x1f496;&#x1f496;更多项目资源&#xff0c;最下方联系我们✨✨✨✨✨✨ 目录 Java项目介绍 资料获取 Java项目介绍 《javaweb企业门户网站》该项目采用技术&#xff1a;jsp servlet mysqljdbccssjsjQuery等相关技术&#xff0c;项目含有源码、文档、配套开发软件…