全部产品
Search
文档中心

:Praktik Terbaik: Manajemen Tabel dengan API

更新时间:Oct 16, 2025

DataWorks menyediakan serangkaian operasi API yang komprehensif untuk manajemen metadata. Panduan ini menjelaskan cara mengkueri tabel, mengambil detail tabel, dan mengelola metadata menggunakan API DataWorks.

Prasyarat

Sebelum memulai, tinjau sumber daya berikut:

1. Daftar tabel

Bagian ini menjelaskan cara menggunakan API untuk mengkueri daftar tabel di bawah proyek/skema MaxCompute dengan dukungan kueri halaman. Prosedur utamanya adalah sebagai berikut:

Backend: Gunakan MetaServiceProxy untuk mengkueri detail tabel

  1. Tulis metode ListTables di MetaServiceProxy untuk memproses parameter frontend dan mengirim permintaan ke API ListTables guna mendapatkan informasi daftar tabel metadata.

    • Parameter Kueri yang Didukung: ID entitas induk, nama (pencocokan kabur), komentar (pencocokan kabur), tipe, parameter pengurutan, dan parameter halaman.

      Catatan

      Rujuk ke Deskripsi Konsep Terkait Entitas Metadata. ID entitas induk dapat berada dalam dua format.

      • ID entitas induk adalah ID Proyek MaxCompute, format: maxcompute-project:${ID akun Alibaba Cloud}::{projectName}.

      • ID entitas induk adalah ID Skema MaxCompute, format: maxcompute-schema:${ID akun Alibaba Cloud}::{projectName}:{schemaName}.

    • Hasil Kueri Mencakup: Jumlah total tabel, informasi paginasi, serta untuk setiap tabel: ID, ID entitas induk, nama tabel, komentar, nama database, nama skema, tipe, daftar bidang partisi, waktu pembuatan, dan waktu modifikasi.

    • Dapatkan ID Akun Alibaba Cloud: Baik menggunakan Pengguna RAM atau Akun Alibaba Cloud, Anda dapat melihat ID akun di pojok kanan atas halaman.

      1. Masuk ke Konsol DataWorks menggunakan Akun Alibaba Cloud atau Pengguna RAM Anda.

      2. Arahkan mouse ke foto profil di pojok kanan atas untuk melihat ID akun Alibaba Cloud.

        • Akun Alibaba Cloud: Account ID adalah ID akun Alibaba Cloud yang perlu diperoleh.

        • Pengguna RAM: Anda dapat langsung melihat Alibaba Cloud Account ID.

    • Kode Contoh:

      /**
       * @author dataworks demo
       */
      @Service
      public class MetaServiceProxy {
           
          @Autowired
          private DataWorksOpenApiClient dataWorksOpenApiClient;
           
          /**
                * DataWorks OpenAPI : ListTables
                *
                * @param listTablesDto
                */
          public ListTablesResponseBodyPagingInfo listTables(ListTablesDto listTablesDto) {
            try {
              Client client = dataWorksOpenApiClient.createClient();
              ListTablesRequest listTablesRequest = new ListTablesRequest();
              // ID entitas induk (lihat konsep entitas metadata)
              listTablesRequest.setParentMetaEntityId(listTablesDto.getParentMetaEntityId());
              // Nama tabel, mendukung pencocokan kabur
              listTablesRequest.setName(listTablesDto.getName());
              // Komentar tabel, mendukung pencocokan kabur
              listTablesRequest.setComment(listTablesDto.getComment());
              // Jenis tabel, mengembalikan semua jenis secara default
              listTablesRequest.setTableTypes(listTablesDto.getTableTypes());
              // Metode pengurutan, CreateTime (default) / ModifyTime / Name / TableType
              listTablesRequest.setSortBy(listTablesDto.getSortBy());
              // Arah pengurutan, mendukung Asc (default) / Desc
              listTablesRequest.setOrder(listTablesDto.getOrder());
              // Nomor halaman, default adalah 1
              listTablesRequest.setPageNumber(listTablesDto.getPageNumber());
              // Ukuran halaman, default adalah 10, maksimum 100
              listTablesRequest.setPageSize(listTablesDto.getPageSize());
              ListTablesResponse response = client.listTables(listTablesRequest);
              // Dapatkan jumlah total tabel yang memenuhi persyaratan
              System.out.println(response.getBody().getPagingInfo().getTotalCount());
              for (Table table : response.getBody().getPagingInfo().getTables()) {
                // ID tabel
                System.out.println(table.getId());
                // ID entitas induk
                System.out.println(table.getParentMetaEntityId());
                // Nama tabel
                System.out.println(table.getName());
                // Komentar tabel
                System.out.println(table.getComment());
                // Nama Database/MaxCompute project
                System.out.println(table.getId().split(":")[3]);
                // Nama skema
                System.out.println(table.getId().split(":")[4]);
                // Jenis tabel
                System.out.println(table.getTableType());
                // Waktu pembuatan tabel
                System.out.println(table.getCreateTime());
                // Waktu modifikasi tabel
                           System.out.println(table.getModifyTime());
                           // Daftar bidang partisi tabel
                           System.out.println(table.getPartitionKeys());
                       }
                       return response.getBody().getPagingInfo();
                   } catch (TeaException error) {
                       // Hanya untuk demonstrasi. Tangani pengecualian dengan benar dalam kode produksi.
                       // Pesan kesalahan
                       System.out.println(error.getMessage());
                       // Alamat diagnostik
                       System.out.println(error.getData().get("Recommend"));
                       com.aliyun.teautil.Common.assertAsString(error.message);
                   } catch (Exception _error) {
                       TeaException error = new TeaException(_error.getMessage(), _error);
                       // Hanya untuk demonstrasi. Tangani pengecualian dengan benar dalam kode produksi.
                       // Pesan kesalahan
                       System.out.println(error.getMessage());
                       // Alamat diagnostik
                       System.out.println(error.getData().get("Recommend"));
                       com.aliyun.teautil.Common.assertAsString(error.message);
                   }
                   return null;
           }
      }
  2. Tambahkan metode listTables di MetaRestController sebagai titik masuk untuk akses antarmuka depan.

    /**
     * Platform metadata kustom menggunakan DataWorks API
     *
     * @author dataworks demo
     */
    @RestController
    @RequestMapping("/meta")
    public class MetaRestController {
       
        @Autowired
        private MetaServiceProxy metaService;
       
        /**
            * Query data tabel per halaman
            *
            * @param listTablesDto
            * @return {@link ListTablesResponseBodyPagingInfo}
            */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/listTables")
        public ListTablesResponseBodyPagingInfo listTables(ListTablesDto listTablesDto) {
          System.out.println("listTablesDto = " + listTablesDto);
          return metaService.listTables(listTablesDto);
        }
    }

Frontend: Menampilkan informasi metadata tabel

import React from "react";
import cn from "classnames";
import {
  Table,
  Form,
  Field,
  Input,
  Button,
  Pagination,
  Dialog,
  Card,
  Grid,
  Select,
} from "@alifd/next";
import type { TableListInput, TableEntity } from "../services/meta";
import * as services from "../services";
import DetailContent from "./detailContent";
import LineageContent from "./lineageContent";
import classes from "../styles/app.module.css";
import moment from "moment";

export interface Props {}

const { Column } = Table;
const { Item } = Form;
const { Header, Content } = Card;
const { Row, Col } = Grid;
const { Option } = Select;

const App: React.FunctionComponent<Props> = () => {
  const field = Field.useField();
  const [datasource, setDatasource] = React.useState<TableEntity[]>([]);
  const [loading, setLoading] = React.useState<boolean>(false);
  const [total, setTotal] = React.useState<number>(0);
  const [current, setCurrent] = React.useState<number>(1);
  const onSubmit = React.useCallback(
    (pageNumber: number = 1) => {
      field.validate(async (errors, values) => {
        if (errors) {
          return;
        }
        setLoading(true);
        try {
          const response = await services.meta.getTableList({
            pageNumber,
            ...values,
          } as TableListInput);
          setDatasource(response.tables);
          setCurrent(pageNumber);
          setTotal(response.totalCount);
        } catch (e) {
          throw e;
        } finally {
          setLoading(false);
        }
      });
    },
    [field]
  );
  const onViewDetail = React.useCallback((record: TableEntity) => {
    Dialog.show({
      title: "Detail Tabel Data",
      content: <DetailContent item={record} />,
      shouldUpdatePosition: true,
      footerActions: ["ok"],
    });
  }, []);
  const onViewLineage = React.useCallback((record: TableEntity) => {
    Dialog.show({
      title: "Hubungan Lineage Tabel",
      content: <LineageContent item={record} />,
      shouldUpdatePosition: true,
      footerActions: ["ok"],
    });
  }, []);
  React.useEffect(() => {
    field.setValue("dataSourceType", "odps");
  }, []);
  return (
    <div className={cn(classes.appWrapper)}>
      <Card free hasBorder={false}>
        <Header title="Demo Skenario Manajemen Tabel Metadata (MaxCompute)" />
        <Content style={{ marginTop: 24 }}>
          <Form field={field} colon fullWidth>
            <Row gutter="1">
              <Col>
                <Item
                  label="ID Proyek/Skema MaxCompute"
                  name="parentMetaEntityId"
                  required
                  help="ID"
                >
                  <Input />
                </Item>
              </Col>
            </Row>

            <Row gutter="4">
              <Col>
                <Item label="Nama" name="name">
                  <Input placeholder="Masukkan nama tabel, pencocokan kabur" />
                </Item>
              </Col>
              <Col>
                <Item label="Komentar" name="comment">
                  <Input placeholder="Masukkan komentar tabel, pencocokan kabur" />
                </Item>
              </Col>
              <Col>
                <Item label="Tipe" name="tableTypes">
                  <Select mode="multiple" style={{ width: "100%" }}>
                    <Option value="TABLE">Tabel</Option>
                    <Option value="VIEW">Tampilan</Option>
                    <Option value="MATERIALIZED_VIEW">Tampilan Materialized</Option>
                  </Select>
                </Item>
              </Col>
              <Col>
                <Item label="Metode Pengurutan" name="sortBy">
                  <Select defaultValue="CreateTime" style={{ width: "100%" }}>
                    <Option value="CreateTime">Waktu Pembuatan</Option>
                    <Option value="ModifyTime">Waktu Modifikasi</Option>
                    <Option value="Name">Nama</Option>
                    <Option value="TableType">Tipe</Option>
                  </Select>
                </Item>
              </Col>
              <Col>
                <Item label="Arah Pengurutan" name="order">
                  <Select defaultValue="Asc" style={{ width: "100%" }}>
                    <Option value="Asc">Naik</Option>
                    <Option value="Desc">Turun</Option>
                  </Select>
                </Item>
              </Col>
            </Row>
            <div
              className={cn(
                classes.searchPanelButtonWrapper,
                classes.buttonGroup
              )}
            >
              <Button type="primary" onClick={() => onSubmit()}>
                Kueri
              </Button>
            </div>
          </Form>
          <div>
            <Table
              dataSource={datasource}
              loading={loading}
              className={cn(classes.tableWrapper)}
              emptyContent={
                <div className={cn(classes.noDataWrapper)}>Tidak Ada Data</div>
              }
            >
              <Column
                title="Nama Proyek"
                dataIndex="id"
                cell={(value) => {
                  const parts = value.split(":");
                  return parts.length > 1 ? parts[parts.length - 3] : "";
                }}
              />
              <Column
                title="skema"
                dataIndex="id"
                cell={(value) => {
                  const parts = value.split(":");
                  return parts.length > 1 ? parts[parts.length - 2] : "";
                }}
              />
              <Column title="Nama Tabel" dataIndex="name" />
              <Column title="Komentar Tabel" dataIndex="comment" />
              <Column title="Tipe Tabel" dataIndex="tableType" />
              <Column
                title="Status Partisi"
                dataIndex="partitionKeys"
                cell={(value) => {
                  return value != null && value.length > 0 ? "Ya" : "Tidak";
                }}
              />
              <Column
                title="Waktu Pembuatan"
                dataIndex="createTime"
                cell={(value) => {
                  return moment(value).format("YYYY-MM-DD HH:mm:ss");
                }}
              />
              <Column
                title="Waktu Modifikasi"
                dataIndex="modifyTime"
                cell={(value) => {
                  return moment(value).format("YYYY-MM-DD HH:mm:ss");
                }}
              />
              <Column
                title="Operasi"
                width={150}
                cell={(value, index, record) => (
                  <div className={cn(classes.buttonGroup)}>
                    <Button
                      type="primary"
                      onClick={() => onViewDetail(record)}
                      text
                    >
                      Lihat Detail
                    </Button>
                    <Button
                      type="primary"
                      onClick={() => onViewLineage(record)}
                      text
                    >
                      Lihat Lineage Tabel
                    </Button>
                  </div>
                )}
              />
            </Table>
            <Pagination
              current={current}
              total={total}
              onChange={onSubmit}
              showJump={false}
              className={cn(classes.tablePaginationWrapper)}
            />
          </div>
        </Content>
      </Card>
    </div>
  );
};

export default App;

Setelah menyelesaikan pengembangan kode di atas, Anda dapat menerapkan dan menjalankan kode proyek. Masukkan ID Proyek MaxCompute dan parameter lainnya untuk melakukan kueri dan mendapatkan daftar semua tabel dalam ruang proyek, dengan dukungan untuk kueri berhalaman.

2. Kueri detail tabel

Praktik berikut menggabungkan tiga operasi API: GetTable, ListColumns, dan ListPartitions dalam metadata untuk mengkueri detail tabel. Gunakan API UpdateColumnBusinessMetadata untuk memperbarui deskripsi bisnis dari bidang. Alur kerja praktik adalah sebagai berikut:

Backend: Gunakan MetaServiceProxy untuk mengkueri detail tabel

  1. Buat metode getTable, listColumns, listPartitions di MetaServiceProxy untuk memanggil operasi mendapatkan informasi dasar tabel, informasi bidang tabel, dan informasi partisi tabel. Buat metode updateColumnBusinessMetadata untuk memperbarui deskripsi bisnis dari bidang.

    /**
     * @author dataworks demo
     */
    @Service
     public class MetaServiceProxy {
       
        @Autowired
        private DataWorksOpenApiClient dataWorksOpenApiClient;
       
        /**
            * DataWorks OpenAPI : getTableDto
            *
            * @param getTableDto
            */
        public Table getTable(GetTableDto getTableDto) {
          try {
            Client client = dataWorksOpenApiClient.createClient();
            GetTableRequest getTableRequest = new GetTableRequest();
            // Table ID
            getTableRequest.setId(getTableDto.getId());
            // Whether to return business metadata
            getTableRequest.setIncludeBusinessMetadata(getTableDto.getIncludeBusinessMetadata());
       
            GetTableResponse response = client.getTable(getTableRequest);
            // Corresponding data table
            Table table = response.getBody().getTable();
            // Table ID
            System.out.println(table.getId());
            // Parent entity ID
            System.out.println(table.getParentMetaEntityId());
            // Table name
            System.out.println(table.getName());
            // Table comment
            System.out.println(table.getComment());
            // Database/MaxCompute project name
            System.out.println(table.getId().split(":")[3]);
            // Schema name
            System.out.println(table.getId().split(":")[4]);
            // Table type
            System.out.println(table.getTableType());
            // Table creation time
            System.out.println(table.getCreateTime());
            // Table modification time
            System.out.println(table.getModifyTime());
            // Check if table is partitioned
            System.out.println(table.getPartitionKeys() != null && !table.getPartitionKeys().isEmpty());
            // Technical metadata of the table
            TableTechnicalMetadata technicalMetadata = table.getTechnicalMetadata();
            // Table owner (name)
            System.out.println(technicalMetadata.getOwner());
            // Whether it is a compressed table
            System.out.println(technicalMetadata.getCompressed());
            // Input format (HMS, DLF and other types support)
            System.out.println(technicalMetadata.getInputFormat());
            // Output format (HMS, DLF and other types support)
            System.out.println(technicalMetadata.getOutputFormat());
            // Class used for table serialization (HMS, DLF and other types support)
            System.out.println(technicalMetadata.getSerializationLibrary());
            // Table storage location (HMS, DLF and other types support)
            System.out.println(technicalMetadata.getLocation());
            // Other parameters
            Map<String, String> parameters = technicalMetadata.getParameters();
                   // Last DDL update time (millisecond-level timestamp), only MaxCompute type supports
                   System.out.println(parameters.get("lastDDLTime"));
                   // Lifecycle (unit: days), only MaxCompute type supports
                   System.out.println(parameters.get("lifecycle"));
                   // Storage size (unit: bytes), not real-time, only MaxCompute type supports
                   System.out.println(parameters.get("dataSize"));
                   // Business metadata of the table
                   TableBusinessMetadata businessMetadata = table.getBusinessMetadata();
                   if (businessMetadata != null) {
                       // Usage instructions
                       System.out.println(businessMetadata.getReadme());
                       // Custom tag information
                       List<TableBusinessMetadataTags> tags = businessMetadata.getTags();
                       if (tags != null && !tags.isEmpty()) {
                           for (TableBusinessMetadataTags tag : tags) {
                               System.out.println(tag.getKey() + ":" + tag.getValue());
                           }
                       }
                       // Upstream production tasks
                       List<TableBusinessMetadataUpstreamTasks> upstreamTasks = businessMetadata.getUpstreamTasks();
                       if (upstreamTasks != null && !upstreamTasks.isEmpty()) {
                           for (TableBusinessMetadataUpstreamTasks upstreamTask : upstreamTasks) {
                               // Task ID and task name, task details can be obtained through GetTask API
                               System.out.println(upstreamTask.getId() + ":" + upstreamTask.getName());
                           }
                       }
                       // Categories
                       List<List<TableBusinessMetadataCategories>> categories = businessMetadata.getCategories();
                       if (categories != null && !categories.isEmpty()) {
                           // Traverse multi-level categories
                           for (List<TableBusinessMetadataCategories> category : categories) {
                               // Output a single multi-level category
                               System.out.println(
                                       category.stream()
                                               .map(TableBusinessMetadataCategories::getName)
                                               .collect(Collectors.joining("->"))
                               );
                           }
                       }
                       // Extension information, only MaxCompute type supports
                       TableBusinessMetadataExtension extension = businessMetadata.getExtension();
                       if (extension != null) {
                           // Project ID
                           System.out.println(extension.getProjectId());
                           // Environment (Prod: production / Dev: development)
                           System.out.println(extension.getEnvType());
                           // Favorite count
                           System.out.println(extension.getFavorCount());
                           // Read count
                           System.out.println(extension.getReadCount());
                           // View count
                           System.out.println(extension.getViewCount());
                       }
                   }
                   return table;
               } catch (TeaException error) {
                   // For demonstration only. Handle exceptions properly in production code.
                   // Error message
                   System.out.println(error.getMessage());
                   // Diagnostic address
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               } catch (Exception _error) {
                   TeaException error = new TeaException(_error.getMessage(), _error);
                   // For demonstration only. Handle exceptions properly in production code.
                   // Error message
                   System.out.println(error.getMessage());
                   // Diagnostic address
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               }
               return null;
           }
       
           /**
            * DataWorks OpenAPI : ListColumns
            *
            * @param listColumnsDto
            */
           public ListColumnsResponseBodyPagingInfo listColumns(ListColumnsDto listColumnsDto) {
               try {
                   Client client = dataWorksOpenApiClient.createClient();
                   ListColumnsRequest listColumnsRequest = new ListColumnsRequest();
                   // Table ID (from ListTables API)
                   listColumnsRequest.setTableId(listColumnsDto.getTableId());
                   // Field name, supports fuzzy matching
                   listColumnsRequest.setName(listColumnsDto.getName());
                   // Field comment, supports word segmentation matching
                   listColumnsRequest.setComment(listColumnsDto.getComment());
                   // Sort method, supports Name / Position (default)
                   listColumnsRequest.setSortBy(listColumnsDto.getSortBy());
                   // Sort direction, supports Asc (default) / Desc
                   listColumnsRequest.setOrder(listColumnsDto.getOrder());
                   // Page number, default is 1
                   listColumnsRequest.setPageNumber(listColumnsDto.getPageNumber());
                   // Page size, default is 10, maximum 100
                   listColumnsRequest.setPageSize(listColumnsDto.getPageSize());
                   ListColumnsResponse response = client.listColumns(listColumnsRequest);
                   // Get the total number of fields that meet the requirements
                   System.out.println(response.getBody().getRequestId());
                   System.out.println(response.getBody().getPagingInfo().getTotalCount());
                   for (Column column : response.getBody().getPagingInfo().getColumns()) {
                       // Field ID
                       System.out.println(column.getId());
                       // Field name
                       System.out.println(column.getName());
                       // Field comment
                       System.out.println(column.getComment());
                       // Field type
                       System.out.println(column.getType());
                       // Field position
                       System.out.println(column.getPosition());
                       // Whether it is a partition field
                       System.out.println(column.getPartitionKey());
                       // Whether it is a primary key, only MaxCompute type supports
                       System.out.println(column.getPrimaryKey());
                       // Whether it is a foreign key, only MaxCompute type supports
                       System.out.println(column.getForeignKey());
                       // Business description for the field (supported by MaxCompute, HMS, and DLF) 
                       if (column.getBusinessMetadata() != null) {
                           System.out.println(column.getBusinessMetadata().getDescription());
                       }
                   }
                   return response.getBody().getPagingInfo();
               } catch (TeaException error) {
                   // For demonstration only. Handle exceptions properly in production code.
                   // Error message
                   System.out.println(error.getMessage());
                   // Diagnostic address
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               } catch (Exception _error) {
                   TeaException error = new TeaException(_error.getMessage(), _error);
                   // For demonstration only. Handle exceptions properly in production code.
                   // Error message
                   System.out.println(error.getMessage());
                   // Diagnostic address
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               }
               return null;
           }
       
           /**
            * DataWorks OpenAPI : UpdateColumnBusinessMetadata
            *
            * @param updateColumnBusinessMetadataDto
            */
           public boolean updateColumnBusinessMetadata(UpdateColumnBusinessMetadataDto updateColumnBusinessMetadataDto) {
               try {
                   // Currently supports MaxCompute, DLF, HMS (EMR cluster) types
                   Client client = dataWorksOpenApiClient.createClient();
                   UpdateColumnBusinessMetadataRequest updateColumnBusinessMetadataRequest = new UpdateColumnBusinessMetadataRequest();
                   // Field ID
                   updateColumnBusinessMetadataRequest.setId(updateColumnBusinessMetadataDto.getId());
                   // Field business description
                   updateColumnBusinessMetadataRequest.setDescription(updateColumnBusinessMetadataDto.getDescription());
                   UpdateColumnBusinessMetadataResponse response = client.updateColumnBusinessMetadata(updateColumnBusinessMetadataRequest);
                   System.out.println(response.getBody().getRequestId());
                   // Whether the update is successful
                   return response.getBody().getSuccess();
               } catch (TeaException error) {
                   // For demonstration only. Handle exceptions properly in production code.
                   // Error message
                   System.out.println(error.getMessage());
                   // Diagnostic address
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               } catch (Exception _error) {
                   TeaException error = new TeaException(_error.getMessage(), _error);
                   // For demonstration only. Handle exceptions properly in production code.
                   // Error message
                   System.out.println(error.getMessage());
                   // Diagnostic address
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               }
               return false;
           }
       
           /**
            * DataWorks OpenAPI : ListPartitions
            *
            * @param listPartitionsDto
            */
           public ListPartitionsResponseBodyPagingInfo listPartitions(ListPartitionsDto listPartitionsDto) {
               try {
                   // Currently supports MaxCompute type and HMS (EMR cluster) type
                   Client client = dataWorksOpenApiClient.createClient();
                   ListPartitionsRequest listPartitionsRequest = new ListPartitionsRequest();
                   // Table ID (from ListTables API)
                   listPartitionsRequest.setTableId(listPartitionsDto.getTableId());
                   // Partition name, supports fuzzy matching
                   listPartitionsRequest.setName(listPartitionsDto.getName());
                   // Sort method, supports Name (HMS method default, MaxCompute type supports) / CreateTime (MaxCompute type default) / ModifyTime (MaxCompute type supports) / RecordCount (MaxCompute type supports) / DataSize (MaxCompute type supports)
                   listPartitionsRequest.setSortBy(listPartitionsDto.getSortBy());
                   // Sort direction, supports Asc (default) / Desc
                   listPartitionsRequest.setOrder(listPartitionsDto.getOrder());
                   // Page number, default is 1
                   listPartitionsRequest.setPageNumber(listPartitionsDto.getPageNumber());
                   // Page size, default is 10, maximum 100
                   listPartitionsRequest.setPageSize(listPartitionsDto.getPageSize());
                   ListPartitionsResponse response = client.listPartitions(listPartitionsRequest);
                   // Get the total number of partitions that meet the requirements
                   System.out.println(response.getBody().getPagingInfo().getTotalCount());
                   for (Partition partition : response.getBody().getPagingInfo().getPartitionList()) {
                       // Table ID
                       System.out.println(partition.getTableId());
                       // Partition name
                       System.out.println(partition.getName());
                       // Creation time (millisecond-level timestamp)
                       System.out.println(partition.getCreateTime());
                       // Modification time (millisecond-level timestamp)
                       System.out.println(partition.getModifyTime());
                       // Partition record count, only MaxCompute type supports
                       System.out.println(partition.getRecordCount());
                       // Partition storage size (unit: bytes), only MaxCompute type supports
                       System.out.println(partition.getDataSize());
                   }
                   return response.getBody().getPagingInfo();
               } catch (TeaException error) {
                   // For demonstration only. Handle exceptions properly in production code.
                   // Error message
                   System.out.println(error.getMessage());
                   // Diagnostic address
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               } catch (Exception _error) {
                   TeaException error = new TeaException(_error.getMessage(), _error);
                   // For demonstration only. Handle exceptions properly in production code.
                   // Error message
                   System.out.println(error.getMessage());
                   // Diagnostic address
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               }
               return null;
           }
       }
  2. Sediakan empat metode di MetaRestController: getTable, listColumns, listPartitions, dan updateColumnBusinessMetadata untuk pemanggilan frontend.

    /**
     * Custom metadata platform menggunakan API DataWorks
     *
     * @author dataworks demo
     */
    @RestController
      @RequestMapping("/meta")
      public class MetaRestController {
    
        @Autowired
        private MetaServiceProxy metaService;
    
        /**
         * Dapatkan detail tabel
         *
         * @param getTableDto
         * @return {@link Table}
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/getTable")
        public Table getTable(GetTableDto getTableDto) {
          System.out.println("getTableDto = " + getTableDto);
          return metaService.getTable(getTableDto);
        }
    
        /**
         * Daftar kolom dengan halaman
         *
         * @param listColumnsDto
         * @return {@link ListColumnsResponseBodyPagingInfo}
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/listColumns")
        public ListColumnsResponseBodyPagingInfo listColumns(ListColumnsDto listColumnsDto) {
          System.out.println("listColumnsDto = " + listColumnsDto);
          return metaService.listColumns(listColumnsDto);
        }
    
        /**
         * Perbarui deskripsi bisnis kolom
         *
         * @param updateColumnBusinessMetadataDto
         * @return {@link Boolean}
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @PostMapping("/updateColumnBusinessMetadata")
        public Boolean updateColumnBusinessMetadata(@RequestBody UpdateColumnBusinessMetadataDto updateColumnBusinessMetadataDto) {
          System.out.println("updateColumnBusinessMetadataDto = " + updateColumnBusinessMetadataDto);
          return metaService.updateColumnBusinessMetadata(updateColumnBusinessMetadataDto);
        }
    
        /**
         * Kueri data partisi berdasarkan halaman
         *
         * @param listPartitionsDto
         * @return {@link ListPartitionsResponseBodyPagingInfo}
         */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/listPartitions")
        public ListPartitionsResponseBodyPagingInfo listPartitions(ListPartitionsDto listPartitionsDto) {
          System.out.println("listPartitionsDto = " + listPartitionsDto);
          return metaService.listPartitions(listPartitionsDto);
        }
      }

Frontend: Menampilkan informasi tabel dasar, informasi bidang, dan informasi partisi

import React from "react";
import moment from "moment";
import {
  Form,
  Grid,
  Table,
  Pagination,
  Tag,
  Field,
  Input,
  Button,
  Select,
  Dialog,
} from "@alifd/next";
import cn from "classnames";
import * as services from "../services";
import {
  type ListColumnsInput,
  type TableColumn,
  type TableEntity,
  type TablePartition,
} from "../services/meta";
import classes from "../styles/detailContent.module.css";

export interface Props {
  item: TableEntity;
}

const formItemLayout = {
  labelCol: {
    fixedSpan: 6,
  },
  wrapperCol: {
    span: 18,
  },
  labelTextAlign: "left" as const,
  colon: true,
  className: cn(classes.formItemWrapper),
};
const { Row, Col } = Grid;
const { Item } = Form;
const { Column } = Table;
const { Option } = Select;
const DetailContent: React.FunctionComponent<Props> = (props) => {
  let columnsPageSize = 10;
  let partitionsPageSize = 5;
  const listColumnsField = Field.useField();
  const listPartitionsField = Field.useField();
  const labelAlign = "top";
  const [detail, setDetail] = React.useState<Partial<TableEntity>>({});
  const [columns, setColumns] = React.useState<Partial<TableColumn[]>>([]);
  const [partitions, setPartitions] = React.useState<Partial<TablePartition[]>>(
    []
  );
  const [columnsTotal, setColumnsTotal] = React.useState<number>(0);
  const [partitionTotal, setPartitionTotal] = React.useState<number>(0);
  const [editingKey, setEditingKey] = React.useState<string>("");
  const [editingDescription, setEditingDescription] =
    React.useState<string>("");

  const [updateColumnDialogVisible, setUpdateColumnDialogVisible] =
    React.useState<boolean>(false);
  const onUpdateColumnDialogOpen = () => {
    setUpdateColumnDialogVisible(true);
  };
  const onUpdateColumnDialogOk = () => {
    handleColumnDescriptionSave();
    onUpdateColumnDialogClose();
  };
  const onUpdateColumnDialogClose = () => {
    setUpdateColumnDialogVisible(false);
  };
  const handleEditColumnDescription = async (record: TableColumn) => {
    setEditingKey(record.id);
    setEditingDescription(record.businessMetadata?.description);
    onUpdateColumnDialogOpen();
  };
  const handleColumnDesrciptionChange = async (e: string) => {
    setEditingDescription(e);
  };
  const handleColumnDescriptionSave = async () => {
    try {
      const res = await services.meta.updateColumnDescription({
        id: editingKey,
        description: editingDescription,
      });
      if (!res) {
        console.log("Permintaan pembaruan gagal");
      }
    } catch (e) {
      console.error("Pembaruan gagal", e);
    } finally {
      listColumns();
      setEditingKey("");
    }
  };
  const getTableDetail = React.useCallback(async () => {
    const response = await services.meta.getTableDetail({
      id: props.item.id,
      includeBusinessMetadata: true,
    });
    setDetail(response);
  }, [props.item.id]);
  const listColumns = React.useCallback(
    async (pageNumber: number = 1) => {
      listColumnsField.setValue("tableId", props.item.id);
      listColumnsField.setValue("pageSize", columnsPageSize);
      listColumnsField.validate(async (errors, values) => {
        if (errors) {
          return;
        }
        try {
          const response = await services.meta.getMetaTableColumns({
            pageNumber,
            ...values,
          } as ListColumnsInput);
          setColumns(response.columns);
          setColumnsTotal(response.totalCount);
        } catch (e) {
          throw e;
        } finally {
        }
      });
    },
    [listColumnsField]
  );
  const listPartitions = React.useCallback(
    async (pageNumber: number = 1) => {
      listPartitionsField.setValue("tableId", props.item.id);
      listPartitionsField.setValue("pageSize", partitionsPageSize);
      listPartitionsField.validate(async (errors, values) => {
        if (errors) {
          return;
        }
        try {
          const response = await services.meta.getTablePartition({
            pageNumber,
            ...values,
          } as ListColumnsInput);
          setPartitions(response.partitionList);
          setPartitionTotal(response.totalCount);
        } catch (e) {
          throw e;
        } finally {
        }
      });
    },
    [listPartitionsField]
  );
  React.useEffect(() => {
    if (props.item.id) {
      getTableDetail();
      listColumns();
      listPartitions();
    }
  }, [props.item.id]);
  return (
    <div className={cn(classes.detailContentWrapper)}>
      <Dialog
        v2
        title="Perbarui Metadata Bisnis Bidang"
        visible={updateColumnDialogVisible}
        onOk={onUpdateColumnDialogOk}
        onClose={onUpdateColumnDialogClose}
      >
        <Row>
          <Col>
            <Item
              {...formItemLayout}
              labelAlign={labelAlign}
              label="Deskripsi Bisnis Bidang"
            >
              <Input
                value={editingDescription}
                onChange={(e) => handleColumnDesrciptionChange(e.toString())}
              ></Input>
            </Item>
          </Col>
        </Row>
      </Dialog>
      <Form labelTextAlign="left">
        <Row>
          <Col>
            <Item {...formItemLayout} label="ID Tabel">
              
                {detail.id}
              
                {detail?.id?.split(":").slice(-3, -2)[0]}
              
                {detail?.id?.split(":").slice(-2, -1)[0]}
              
                {detail.name}
              
                {detail.comment}
              
                {detail.tableType}
              
                {detail.technicalMetadata?.compressed ? "Ya" : "Tidak"}
              
                {detail.businessMetadata?.extension?.projectId}
              
                {detail.businessMetadata?.extension?.envType === "Dev"
                  ? "Pengembangan"
                  : "Produksi"}
              
                {detail.technicalMetadata?.parameters["lifecycle"]
                  ? `${detail.technicalMetadata?.parameters["lifecycle"]} hari`
                  : "Permanen`}
              {`${detail.technicalMetadata?.parameters["dataSize"]} Bytes`}
                {moment(
                  Number(detail.technicalMetadata?.parameters["lastDDLTime"])
                ).format("YYYY-MM-DD HH:mm:ss")}
              
                {detail.partitionKeys != null && detail.partitionKeys.length > 0
                  ? "Ya"
                  : "Tidak"}
              
                    <Tag type="primary" size="small">
                      {tag.key + (tag.value ? ":" + tag.value : "")}
                    </Tag>
                  
                {detail.businessMetadata?.categories?.map((category, index) => (
                  <span key={index} className={cn(classes.tag)}>
                    <Tag type="normal" color="gray" size="small">
                      {category.map((obj) => obj.name).join("->")}
                    </Tag>
                  
                {detail.businessMetadata?.upstreamTasks?.map((task, index) => (
                  <span key={index} className={cn(classes.tag)}>
                    <Tag type="primary" size="small">
                      {task.name + " (" + task.id + ")"}
                    </Tag>
                  
                <Tag type="primary" size="small">
                  {detail.businessMetadata?.extension?.viewCount}
                </Tag>
              
                <Tag type="primary" size="small">
                  {detail.businessMetadata?.extension?.readCount}
                </Tag>
              
                <Tag type="primary" size="small">
                  {detail.businessMetadata?.extension?.favorCount}
                </Tag>
              
                {moment(detail.createTime).format("YYYY-MM-DD HH:mm:ss")}
              
                {moment(detail.modifyTime).format("YYYY-MM-DD HH:mm:ss")}
              {value?.description}Tabel Non-partisi

Setelah menyelesaikan pengembangan kode di atas, Anda dapat menerapkan dan menjalankan kode proyek secara lokal. Untuk operasi penerapan dan menjalankan, lihat Operasi Umum: Penerapan Lokal dan Menjalankan.

3. Kueri lineage tabel

Ketika definisi tabel berubah, Anda dapat menggunakan API DataWorks untuk melakukan analisis lineage tugas hulu dan hilir guna menemukan node yang sesuai dengan tabel.

Catatan

Gunakan ListLineages untuk mengkueri lineage hulu dan hilir dari tabel data.

Backend: Gunakan MetaServiceProxy untuk mengkueri lineage tabel

  1. Buat metode listTableLineages di MetaServiceProxy untuk memanggil API mendapatkan informasi entitas dan hubungan lineage hulu dan hilir dari tabel.

    /**
     * @author dataworks demo
     */
    @Service
      public class MetaServiceProxy {
       
        @Autowired
        private DataWorksOpenApiClient dataWorksOpenApiClient;
       
        /**
            * DataWorks OpenAPI : ListLineages
            *
            * @param listTableLineagesDto
            */
        public ListLineagesResponseBodyPagingInfo listTableLineages(ListTableLineagesDto listTableLineagesDto) {
          try {
            Client client = dataWorksOpenApiClient.createClient();
            ListLineagesRequest listLineagesRequest = new ListLineagesRequest();
            // Arah kueri lineage, apakah hulu
            boolean needUpstream = "up".equalsIgnoreCase(listTableLineagesDto.getDirection());
            // ID Tabel (dari ListTables API)
            // Nama partisi, mendukung pencocokan kabur
            if (needUpstream) {
              listLineagesRequest.setDstEntityId(listTableLineagesDto.getTableId());
              listLineagesRequest.setSrcEntityName(listTableLineagesDto.getName());
            } else {
              listLineagesRequest.setSrcEntityId(listTableLineagesDto.getTableId());
              listLineagesRequest.setDstEntityName(listTableLineagesDto.getName());
            }
            // Metode pengurutan, mendukung Nama
            listLineagesRequest.setSortBy(listTableLineagesDto.getSortBy());
            // Arah pengurutan, mendukung Asc (default) / Desc
            listLineagesRequest.setOrder(listTableLineagesDto.getOrder());
            // Nomor halaman, default adalah 1
            listLineagesRequest.setPageNumber(listTableLineagesDto.getPageNumber());
            // Ukuran halaman, default adalah 10, maksimum 100
            listLineagesRequest.setPageSize(listTableLineagesDto.getPageSize());
            // Apakah akan mengembalikan informasi hubungan lineage spesifik
            listLineagesRequest.setNeedAttachRelationship(true);
            ListLineagesResponse response = client.listLineages(listLineagesRequest);
            // Dapatkan jumlah total entitas lineage yang memenuhi persyaratan
            System.out.println(response.getBody().getPagingInfo().getTotalCount());
            if (response.getBody().getPagingInfo().getLineages() == null) {
              response.getBody().getPagingInfo().setLineages(Collections.emptyList());
            }
            if (response.getBody().getPagingInfo().getLineages() != null) {
              for (ListLineagesResponseBodyPagingInfoLineages lineage : response.getBody().getPagingInfo().getLineages()) {
                LineageEntity entity;
                // Dapatkan entitas lineage yang sesuai
                if (needUpstream) {
                  entity = lineage.getSrcEntity();
                } else {
                  entity = lineage.getDstEntity();
                }
                // ID Entitas
                System.out.println(entity.getId());
                // Nama Entitas
                System.out.println(entity.getName());
                // Informasi atribut entitas
                System.out.println(entity.getAttributes());
                // Daftar hubungan lineage antar entitas
                           List<LineageRelationship> relationships = lineage.getRelationships();
                           if (relationships != null) {
                               relationships.forEach(relationship -> {
                                   // ID Hubungan Lineage
                                   System.out.println(relationship.getId());
                                   // Waktu Pembuatan
                                   System.out.println(relationship.getCreateTime());
                                   // Tugas yang sesuai dengan hubungan lineage
                                   LineageTask task = relationship.getTask();
                                   if (task != null) {
                                       // ID Tugas, untuk tugas DataWorks, detail tugas dapat diperoleh melalui GetTask
                                       System.out.println(task.getId());
                                       // Tipe Tugas
                                       System.out.println(task.getType());
                                       // Informasi atribut tugas
                                       Map<String, String> attributes = task.getAttributes();
                                       if (attributes != null) {
                                           // Untuk tugas DataWorks, informasi atribut tugas dan instans dapat diperoleh darinya
                                           // ID Proyek
                                           System.out.println(attributes.get("projectId"));
                                           // ID Tugas
                                           String taskId = attributes.containsKey("taskId") ? attributes.get("taskId") : attributes.get("nodeId");
                                           System.out.println(taskId);
                                           // ID Instans Tugas
                                           String taskInstanceId = attributes.containsKey("taskInstanceId") ? attributes.get("taskInstanceId") : attributes.get("jobId");
                                           System.out.println(taskInstanceId);
                                       }
                                   }
                               });
                           }
                       }
                       return response.getBody().getPagingInfo();
                   }
               } catch (TeaException error) {
                   // Hanya untuk demonstrasi. Tangani pengecualian dengan benar dalam kode produksi.
                   // Pesan kesalahan
                   System.out.println(error.getMessage());
                   // Alamat diagnostik
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               } catch (Exception _error) {
                   TeaException error = new TeaException(_error.getMessage(), _error);
                   // Hanya untuk demonstrasi. Tangani pengecualian dengan benar dalam kode produksi.
                   // Pesan kesalahan
                   System.out.println(error.getMessage());
                   // Alamat diagnostik
                   System.out.println(error.getData().get("Recommend"));
                   com.aliyun.teautil.Common.assertAsString(error.message);
               }
               return null;
           }
       }
  2. Sediakan metode listTableLineages di MetaRestController untuk pemanggilan frontend.

    /**
     * Custom metadata platform menggunakan API DataWorks
     *
     * @author dataworks demo
     */
    @RestController
      @RequestMapping("/meta")
      public class MetaRestController {
       
        @Autowired
        private MetaServiceProxy metaService;
       
        /**
            * Kueri hubungan lineage tabel
            *
            * @param listTableLineagesDto
            * @return
            */
        @CrossOrigin(origins = "http://localhost:8080")
        @GetMapping("/listTableLineages")
        public ListLineagesResponseBodyPagingInfo listTableLineages(ListTableLineagesDto listTableLineagesDto) {
          System.out.println("listTableLineagesDto = " + listTableLineagesDto);
          return metaService.listTableLineages(listTableLineagesDto);
        }
       
      }

Frontend: Menampilkan lineage tabel

import React from 'react';
import Graphin, { Behaviors } from '@antv/graphin';
import type { IUserNode, IUserEdge, GraphinData, GraphEvent } from '@antv/graphin';
import cn from 'classnames';
import * as services from '../services';
import type { TableEntity, ListTableLineageOutput, LineageEntity } from '../services/meta';
import classes from '../styles/lineageContent.module.css';
import '@antv/graphin/dist/index.css';

export interface Props {
  item: TableEntity;
}

function transNode(entity: LineageEntity | TableEntity, direction?: string): IUserNode {
  return {
    id: entity.id,
    label: entity.name,
    data: {
      ...entity,
      direction,
    },
    style: {
     label: {
        value: entity.name,
      }
    },
  }
}
function transEdge(source: LineageEntity | TableEntity, target: LineageEntity | TableEntity): IUserEdge {
  return {
    source: source.id,
    target: target.id,
    style: {
      keyshape: {
        lineDash: [8, 4],
        lineWidth: 2,
      },
      animate: {
        type: 'line-dash',
        repeat: true,
      },
    }
  };
}
function parse(
  source: LineageEntity | TableEntity,
  data: ListTableLineageOutput,
  direction: string,
): [IUserNode[], IUserEdge[]] {
  const nodes: IUserNode[] = [];
  const edges: IUserEdge[] = [];
  data.lineages.forEach((lineageRelationship) => {
    if (direction === 'down') {
      nodes.push(transNode(lineageRelationship.dstEntity, direction));
      edges.push(transEdge(source, lineageRelationship.dstEntity));
    }  else {
      nodes.push(transNode(lineageRelationship.srcEntity, direction));
      edges.push(transEdge(lineageRelationship.srcEntity, source));
    }
  });
  return [nodes, edges];
}
function mergeNodes(prev: IUserNode[], next: IUserNode[]) {
  const result: IUserNode[] = prev.slice();
  next.forEach((item) => {
    const hasValue = prev.findIndex(i => i.id === item.id) >= 0;
    !hasValue && result.push(item);
  });
  return result;
}
function mergeEdges(source: IUserEdge[], target: IUserEdge[]) {
  const result: IUserEdge[] = source.slice();
  target.forEach((item) => {
    const hasValue = source.findIndex(i => i.target === item.target && i.source === item.source) >= 0;
    !hasValue && result.push(item);
  });
  return result;
}

const { ActivateRelations, DragNode, ZoomCanvas } = Behaviors;
const LineageContent: React.FunctionComponent = (props) => {
  const ref = React.useRef();
  const [data, setData] = React.useState({ nodes: [], edges: [] });
  const getTableLineage = async (
    collection: GraphinData,
    target: TableEntity | LineageEntity,
    direction: string,
  ) => {
    if (!direction) {
      return collection;
    }
    const response = await services.meta.getTableLineage({
      direction,
      tableId: target.id,
    });
    const [nodes, edges] = parse(target, response, direction);
    collection.nodes = mergeNodes(collection.nodes!, nodes);
    collection.edges = mergeEdges(collection.edges!, edges);
    return collection;
  };
  const init = async () => {
    let nextData = Object.assign({}, data, { nodes: [transNode(props.item)] });
    nextData = await getTableLineage(nextData, props.item, 'up');
    nextData = await getTableLineage(nextData, props.item, 'down');
    setData(nextData);
    ref.current!.graph.fitCenter();
  };
  React.useEffect(() => {
    ref.current?.graph && init();
  }, [
    ref.current?.graph,
  ]);
  React.useEffect(() => {
    const graph = ref.current?.graph;
    const event = async (event: GraphEvent) => {
      const source = event.item?.get('model').data;
      let nextData = Object.assign({}, data);
      nextData = await getTableLineage(nextData, source, source.direction);
      setData(nextData);
    };
    graph?.on('node:click', event);
    return () => {
      graph?.off('node:click', event);
    };
  }, [
    data,
    ref.current?.graph,
  ]);
  return (
    
}
        layout={{
          type: 'dagre',
          rankdir: 'LR',
          align: 'DL',
          nodesep: 10,
          ranksep: 40,
        }}
      >
        




  );
};

export default LineageContent;

Setelah menyelesaikan pengembangan kode di atas, Anda dapat menerapkan dan menjalankan kode proyek secara lokal. Untuk operasi penerapan dan menjalankan, lihat Operasi Umum: Penerapan Lokal dan Menjalankan.

4. Temukan tugas yang sesuai dengan tabel

Ketika definisi tabel berubah, Anda dapat melakukan analisis lineage tugas hilir melalui API DataWorks, OpenData, dan langganan pesan untuk menemukan node yang sesuai dengan tabel. Operasi spesifik adalah sebagai berikut:

Catatan

Pesan saat ini mendukung perubahan tabel, perubahan tugas, dll. Pengguna Edisi Perusahaan dapat terhubung ke pesan perubahan tabel. Ketika Anda menerima pesan perubahan tabel, Anda dapat melihat hubungan lineage tabel tersebut.

Tugas lineage

  1. Gunakan ListLineages untuk melihat lineage tabel, dan dapatkan ID Tugas DataWorks (dan ID Instans Tugas) dari Task dalam hubungan lineage.

  2. Berdasarkan ID tugas yang diperoleh, gunakan GetTask untuk mendapatkan detail tugas.

Tugas produksi

  1. Berdasarkan ID entitas, gunakan API GetTable untuk mendapatkan metadata bisnis tabel, yang berisi ID Tugas DataWorks dalam tugas produksi hulu.

  2. Berdasarkan ID tugas yang ditentukan, gunakan GetTask untuk mendapatkan detail tugas.

Operasi umum: Penerapan lokal dan menjalankan

  1. Persiapkan dependensi.

    Anda perlu menyiapkan lingkungan dependensi berikut: Java 8 ke atas, alat pembuatan Maven, lingkungan Node, alat pnpm. Anda dapat menjalankan perintah berikut untuk menentukan apakah Anda memiliki lingkungan di atas:

    npm -v // Jika instalasi berhasil, versi akan ditampilkan di output perintah. Jika instalasi gagal, kesalahan bahwa tidak ada perintah yang tersedia akan dilaporkan.
    java -version // Jika instalasi berhasil, versi akan ditampilkan di output perintah. Jika instalasi gagal, kesalahan bahwa tidak ada perintah yang tersedia akan dilaporkan.
    pnpm -v // Jika instalasi berhasil, versi akan ditampilkan di output perintah. Jika instalasi gagal, kesalahan bahwa tidak ada perintah yang tersedia akan dilaporkan.
  2. Unduh kode proyek dan jalankan perintah berikut.

    Tautan unduhan kode proyek: meta-api-demo.zip.

    pnpm i
  3. Temukan file application.properties di jalur backend/src/main/resources proyek contoh, dan modifikasi parameter inti dalam file tersebut.

    • api.access-key-id dan api.access-key-secret perlu dimodifikasi menjadi AccessKey ID dan AccessKey Secret dari akun Alibaba Cloud Anda.

      Catatan

      Anda dapat merujuk ke Manajemen Pasangan AccessKey untuk mendapatkan informasi terkait AccessKey dan lainnya dari akun Alibaba Cloud Anda.

    • api.region-id dan api.endpoint perlu dimodifikasi menjadi informasi wilayah API yang akan dipanggil.

    Parameter lainnya dapat dimodifikasi sesuai dengan situasi aktual. Contoh pengisian setelah dimodifikasi adalah sebagai berikut.Contoh Penerapan Lokal

  4. Jalankan perintah berikut di direktori root proyek untuk menjalankan kode contoh.

    npm run dev