commit e6e4cd8119568eb49f1b9b2273d9f5b59cbfda06
Author: 0Xiao0 <511201264@qq.com>
Date: Mon Mar 23 10:45:02 2026 +0800
first commit
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..41fa906
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+.dockerignore
+.gitignore
+.git
+README.md
\ No newline at end of file
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..5ddfeda
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,24 @@
+# 核心凭证配置 (必填)
+APP_ID=your_app_id
+APP_SECRET=your_app_secret
+LARK_HOST=https://open.feishu.cn
+
+# 单表查询配置 (get_records, batch_get_records)
+DEFAULT_APP_TOKEN=your_default_app_token
+DEFAULT_TABLE_ID=your_default_table_id
+BATCH_TABLE_ID=your_batch_table_id
+
+# 导出网页视图配置 (严格 JSON 数组格式)
+WEB_VIEW_TABS=[{"name": "默认分组", "app_token": "your_app_token", "table_id": "your_table_id"}]
+
+# AI 总结配置 (可选,用于 outline_view.html 生成摘要)
+AI_API_KEY=your_ai_api_key
+AI_BASE_URL=https://api.openai.com/v1
+AI_MODEL=gpt-4o
+
+# 多表合并功能配置 (可选)
+MERGE_SOURCE_APP_TOKEN_1=source_token_1
+MERGE_SOURCE_TABLE_ID_1=source_id_1
+MERGE_SOURCE_APP_TOKEN_2=source_token_2
+MERGE_SOURCE_TABLE_ID_2=source_id_2
+MERGE_TARGET_APP_TOKEN=target_token
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..110b0a6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+# Python-generated files
+__pycache__/
+*.py[oc]
+build/
+dist/
+wheels/
+*.egg-info
+
+# Virtual environments
+.venv
+.env
diff --git a/.python-version b/.python-version
new file mode 100644
index 0000000..24ee5b1
--- /dev/null
+++ b/.python-version
@@ -0,0 +1 @@
+3.13
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..bfe13bd
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,15 @@
+FROM python:3
+
+RUN apt-get update
+
+WORKDIR /home/app
+
+# If we add the requirements and install dependencies first, docker can use cache if requirements don't change
+ADD requirements.txt /home/app
+RUN pip install --no-cache-dir -r requirements.txt
+
+ADD . /home/app
+
+# Run the command on container startup
+CMD python3 bitable_calendar.py
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0cc3989
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+# 基于多维表格的敏捷项目周期管理
+
+[使用说明](https://open.feishu.cn/document/home/agile-project-cycle-management-based-on-bitable/introduction)
+
+
+## 配置说明
+
+本项目依赖环境变量来进行飞书应用授权和数据表定位。你需要根据 `.env.example`(或直接新建一个 `.env` 文件),配置以下环境变量:
+
+### 核心凭证配置 (必填)
+* `APP_ID`: 你的飞书应用的 App ID。
+* `APP_SECRET`: 你的飞书应用的 App Secret。
+* `LARK_HOST`: 飞书开放平台 API 地址,默认为 `https://open.feishu.cn`。
+
+### 单表查询配置 (get_records, batch_get_records)
+* `DEFAULT_APP_TOKEN`: 默认读取的飞书多维表格的 App Token。
+* `DEFAULT_TABLE_ID`: `get_records.py` 默认查询的 Table ID。
+* `BATCH_TABLE_ID`: `batch_get_records.py` 默认批量查询的 Table ID。
+
+### 导出可视化视图配置 (export_web_view)
+* `WEB_VIEW_TABS`: 导出 HTML 时使用的多标签页配置,必须为严格的 **JSON 数组字符串**。例如:
+ `[{"name": "开发组", "app_token": "token1", "table_id": "id1"}, {"name": "测试组", "app_token": "token2", "table_id": "id2"}]`
+* **AI 总结配置**(可选,用于网页自带的 AI 分板):
+ * `AI_API_KEY`: 大模型 API Key。
+ * `AI_BASE_URL`: 大模型 API 请求 Base URL。
+ * `AI_MODEL`: 大模型模型名称(如 `gpt-4o`, `MiniMaxAI` 等)。
+
+### 多表合并功能配置 (merge_bitables, append_bitables)
+* `MERGE_SOURCE_APP_TOKEN_1` / `MERGE_SOURCE_TABLE_ID_1`: 第一个数据源。
+* `MERGE_SOURCE_APP_TOKEN_2` / `MERGE_SOURCE_TABLE_ID_2`: 第二个数据源。
+* `MERGE_TARGET_APP_TOKEN`: 数据合并后的目标 App Token。
+
+## 快速开始模板 (`.env` 文件配置)
+你可以直接在项目根目录创建一个 `.env` 文件,然后复制以下内容并替换成你的真实配置:
+
+```env
+# 核心凭证配置 (必填)
+APP_ID=your_app_id
+APP_SECRET=your_app_secret
+LARK_HOST=https://open.feishu.cn
+
+# 单表查询配置 (get_records, batch_get_records)
+DEFAULT_APP_TOKEN=your_default_app_token
+DEFAULT_TABLE_ID=your_default_table_id
+BATCH_TABLE_ID=your_batch_table_id
+
+# 导出网页视图配置 (严格 JSON 数组格式)
+WEB_VIEW_TABS=[{"name": "默认分组", "app_token": "your_app_token", "table_id": "your_table_id"}]
+
+# AI 总结配置 (可选,用于 outline_view.html 生成摘要)
+AI_API_KEY=your_ai_api_key
+AI_BASE_URL=https://api.openai.com/v1
+AI_MODEL=gpt-4o
+
+# 多表合并功能配置 (可选)
+MERGE_SOURCE_APP_TOKEN_1=source_token_1
+MERGE_SOURCE_TABLE_ID_1=source_id_1
+MERGE_SOURCE_APP_TOKEN_2=source_token_2
+MERGE_SOURCE_TABLE_ID_2=source_id_2
+MERGE_TARGET_APP_TOKEN=target_token
+```
+## 快速开始
+```bash
+uv sync
+uv run export_web_view.py
+uv run server.py
+```
+
+服务启动后,可以通过浏览器直接访问查看交互式数据看板:
+🔗 **[http://localhost:18080](http://localhost:18080)**
diff --git a/api.py b/api.py
new file mode 100644
index 0000000..82b3e5a
--- /dev/null
+++ b/api.py
@@ -0,0 +1,237 @@
+# -*- coding: UTF-8 -*-
+import datetime
+from typing import List
+from utils import request
+import sys
+if sys.version_info.minor >= 8:
+ from typing import Literal
+else:
+ from typing_extensions import Literal
+
+
+class Client(object):
+ def __init__(self, lark_host):
+ self._host = lark_host
+
+ def get_tenant_access_token(self, app_id, app_secret):
+ url = self._host+"/open-apis/auth/v3/app_access_token/internal/"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8'
+ }
+ payload = {
+ 'app_id': app_id,
+ 'app_secret': app_secret
+ }
+ resp = request("POST", url, headers, payload)
+ return resp['tenant_access_token']
+
+ def get_records(self, access_token: str, app_token: str, table_id: str, view_id: str=None):
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+
+ params = {}
+ if view_id: params['view_id'] = view_id
+ resp = request("GET", url, headers, params=params)
+ return resp['data']['items']
+
+
+ def create_calendar(
+ self,
+ access_token: str,
+ summary: str=None,
+ description: str=None,
+ permissions: Literal["private", "show_only_free_busy", "public"]=None,
+ color: int=None,
+ summary_alias: str=None):
+ url = f"{self._host}/open-apis/calendar/v4/calendars"
+
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ payload = {}
+ if summary: payload['summary'] = summary
+ if description: payload['description'] = description
+ if permissions: payload['permissions'] = permissions
+ if color: payload['color'] = color
+ if summary_alias: payload['summary_alias'] = summary_alias
+
+ resp = request("POST", url, headers, payload)
+ return resp['data']['calendar']
+
+ def create_event(
+ self,
+ access_token: str,
+ calendar_id: str,
+ start_time: datetime.datetime,
+ end_time: datetime.datetime,
+ vchat=None,
+ location=None,
+ reminders=None,
+ schemas=None,
+ summary: str=None,
+ description: str=None,
+ need_notification: bool=None,
+ visibility: Literal['default', 'public', 'private']=None,
+ attendee_ability: Literal['none', 'can_see_others', 'can_invite_others', 'can_modify_event']=None,
+ free_busy_status: Literal['busy', 'free']=None,
+ color: int=None,
+ recurrence: str=None,
+ ):
+ url = f"{self._host}/open-apis/calendar/v4/calendars/{calendar_id}/events"
+
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ payload = {
+ 'start_time': {'timestamp': int(start_time.timestamp())},
+ 'end_time': {'timestamp': int(end_time.timestamp())}
+ }
+ if vchat: payload['vchat'] = vchat
+ if location: payload['location'] = location
+ if reminders: payload['reminders'] = reminders
+ if schemas: payload['schemas'] = schemas
+ if summary: payload['summary'] = summary
+ if description: payload['description'] = description
+ if need_notification: payload['need_notification'] = need_notification
+ if visibility: payload['visibility'] = visibility
+ if attendee_ability: payload['attendee_ability'] = attendee_ability
+ if free_busy_status: payload['free_busy_status'] = free_busy_status
+ if color: payload['color'] = color
+ if recurrence: payload['recurrence'] = recurrence
+ resp = request("POST", url, headers, payload)
+ return resp['data']['event']
+
+ def batch_create_records(self, access_token: str, app_token: str, table_id: str, records: List[dict]):
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/batch_create"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ payload = {
+ 'records': records
+ }
+ resp = request("POST", url, headers, payload)
+ return resp['data']['records']
+
+ def get_fields_list(self, access_token: str, app_token: str, table_id: str, view_id: str=None, page_token: str=None, page_size: int=None):
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/fields"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ params = {}
+ if view_id: params['view_id'] = view_id
+ if page_token: params['page_token'] = page_token
+ if page_size: params['page_size'] = page_size
+ resp = request("GET", url, headers, params=params)
+ return resp['data']
+
+ def update_field(self, access_token: str, app_token: str, table_id: str, field_id: str, field_name: str, type: int, property: dict=None):
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/fields/{field_id}"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ payload = {
+ 'field_name': field_name,
+ 'type': type,
+ }
+ if property: payload['property'] = property
+ resp = request("PUT", url, headers, payload)
+ return resp['data']['field']
+
+ def get_records_list(self, access_token: str, app_token: str, table_id: str, view_id: str=None, page_token: str=None, page_size: int=None):
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ params = {}
+ if view_id: params['view_id'] = view_id
+ if page_token: params['page_token'] = page_token
+ if page_size: params['page_size'] = page_size
+ resp = request("GET", url, headers, params=params)
+ return resp['data']
+
+ def get_tables_list(self, access_token: str, app_token: str, page_token: str=None, page_size: int=None):
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ params = {}
+ if page_token: params['page_token'] = page_token
+ if page_size: params['page_size'] = page_size
+ resp = request("GET", url, headers, params=params)
+ return resp.get('data', {})
+
+
+ def batch_update_records(self, access_token: str, app_token: str, table_id: str, records: List[dict], user_id_type: str=None):
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/batch_update"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ payload = {
+ 'records': records
+ }
+ resp = request("POST", url, headers, payload)
+ return resp['data']['records']
+
+ def create_table(self, access_token: str, app_token: str, table_name: str=None) -> str:
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ payload = {}
+ if table_name: payload['table'] = {'name': table_name}
+ resp = request("POST", url, headers, payload)
+ return resp['data']['table_id']
+
+ def add_field(self, access_token: str, app_token: str, table_id: str, field_name: str, type: int, property: dict=None):
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/fields"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+ payload = {
+ 'field_name': field_name,
+ 'type': type
+ }
+ if property: payload['property'] = property
+ resp = request("POST", url, headers, payload)
+ return resp['data']['field']
+
+ def batch_get_records(self, access_token: str, app_token: str, table_id: str, record_ids: List[str], user_id_type: str=None, with_shared_url: bool=None, automatic_fields: bool=None):
+ url = f"{self._host}/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/batch_get"
+ headers = {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Authorization': 'Bearer '+access_token,
+ }
+
+ all_records = []
+ # The API supports a maximum of 100 record IDs per request.
+ for i in range(0, len(record_ids), 100):
+ chunk = record_ids[i:i + 100]
+ payload = {
+ 'record_ids': chunk
+ }
+ if user_id_type is not None: payload['user_id_type'] = user_id_type
+ if with_shared_url is not None: payload['with_shared_url'] = with_shared_url
+ if automatic_fields is not None: payload['automatic_fields'] = automatic_fields
+
+ resp = request("POST", url, headers, payload)
+ if resp and 'data' in resp and 'records' in resp['data']:
+ all_records.extend(resp['data']['records'])
+
+ return all_records
+
+
+
+
diff --git a/append_bitables.py b/append_bitables.py
new file mode 100644
index 0000000..0145c52
--- /dev/null
+++ b/append_bitables.py
@@ -0,0 +1,130 @@
+# -*- coding: UTF-8 -*-
+import api
+import config
+import logging
+import utils
+import copy
+
+LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
+logging.basicConfig(format=LOG_FORMAT, level=logging.INFO)
+
+def get_all_records(client: api.Client, access_token: str, app_token: str, table_id: str):
+ records = []
+ page_token = None
+ while True:
+ resp = client.get_records_list(access_token, app_token, table_id, page_token=page_token)
+ items = resp.get('items', [])
+ if items:
+ records.extend(items)
+ if resp.get('has_more'):
+ page_token = resp.get('page_token')
+ else:
+ break
+ return records
+
+
+def get_all_fields(client: api.Client, access_token: str, app_token: str, table_id: str):
+ fields = []
+ page_token = None
+ while True:
+ resp = client.get_fields_list(access_token, app_token, table_id, page_token=page_token)
+ items = resp.get('items', [])
+ if items:
+ fields.extend(items)
+ if resp.get('has_more'):
+ page_token = resp.get('page_token')
+ else:
+ break
+ return fields
+
+
+def append_to_existing_table(client: api.Client, access_token: str, source_tables: list, target_app_token: str, target_table_id: str):
+ """
+ Appends data from multiple source tables into one existing target table.
+ Missing fields in the target table will be automatically added.
+ """
+ logging.info(f"Appending records from {len(source_tables)} source tables to target {target_app_token} / {target_table_id}")
+
+ # 1. Read existing Target Table schema
+ target_fields = get_all_fields(client, access_token, target_app_token, target_table_id)
+ target_field_map = {f['field_name']: f['field_id'] for f in target_fields}
+
+ # Check if "Source" field exists, if not, create it
+ if "Source" not in target_field_map:
+ try:
+ resp = client.add_field(access_token, target_app_token, target_table_id, "Source", 1, None)
+ target_field_map["Source"] = resp['field_id']
+ except utils.LarkException as e:
+ logging.error(f"Failed to create 'Source' field: {e}")
+
+ for source_app_token, source_table_id in source_tables:
+ logging.info(f"Processing source table {source_app_token} / {source_table_id}...")
+
+ # 2. Merge Missing Fields
+ source_fields = get_all_fields(client, access_token, source_app_token, source_table_id)
+ for f in source_fields:
+ name = f['field_name']
+ if name not in target_field_map:
+ ftype = f['type']
+ fprop = copy.deepcopy(f.get('property'))
+ # Clean up option IDs to prevent insertion errors in target table
+ if fprop and 'options' in fprop:
+ for opt in fprop['options']:
+ if 'id' in opt: del opt['id']
+ # Sometimes a field might be a reference to another table that isn't copied, so we might want to catch creation errors
+ try:
+ resp = client.add_field(access_token, target_app_token, target_table_id, name, ftype, fprop)
+ target_field_map[name] = resp['field_id']
+ except utils.LarkException as e:
+ logging.error(f"Failed to create field '{name}' in target: {e}")
+
+ # 3. Read & Insert Records
+ records = get_all_records(client, access_token, source_app_token, source_table_id)
+ batch = []
+ source_name = f"{source_app_token}/{source_table_id}"
+ for r in records:
+ original_fields = r.get('fields', {})
+ new_fields = {}
+ for fname, fvalue in original_fields.items():
+ if fname in target_field_map:
+ new_fields[fname] = fvalue
+ new_fields['Source'] = source_name
+ batch.append({'fields': new_fields})
+
+ # Feishu limit is usually 500 per request
+ CHUNK_SIZE = 500
+ for i in range(0, len(batch), CHUNK_SIZE):
+ chunk = batch[i:i + CHUNK_SIZE]
+ if chunk:
+ try:
+ client.batch_create_records(access_token, target_app_token, target_table_id, chunk)
+ except utils.LarkException as e:
+ logging.error(f"Failed to insert chunk of records: {e}")
+
+ logging.info(f"Appended records from {source_name} successfully.")
+
+if __name__ == "__main__":
+ client = api.Client(config.LARK_HOST)
+
+ try:
+ access_token = client.get_tenant_access_token(config.APP_ID, config.APP_SECRET)
+ except Exception as e:
+ logging.error(f"Could not get access token: {e}")
+ exit(1)
+
+ logging.info(f"Using App ID: {config.APP_ID}")
+
+ SOURCE_TABLES = [
+ (config.MERGE_SOURCE_APP_TOKEN_1, config.MERGE_SOURCE_TABLE_ID_1),
+ (config.MERGE_SOURCE_APP_TOKEN_2, config.MERGE_SOURCE_TABLE_ID_2),
+ # Add more source tables as needed: ("app_token", "table_id")
+ ]
+
+ TARGET_APP_TOKEN = config.MERGE_TARGET_APP_TOKEN
+ TARGET_TABLE_ID = "target_table_id_to_append_to" # PLEASE UPDATE THIS TO THE EXISTING MERGED TABLE ID
+
+ is_placeholder = (config.MERGE_SOURCE_APP_TOKEN_1 == "source_app_token_1")
+ if is_placeholder:
+ logging.warning("Please update config.py or environment variables with valid MERGE_* tokens to execute.")
+ else:
+ append_to_existing_table(client, access_token, SOURCE_TABLES, TARGET_APP_TOKEN, TARGET_TABLE_ID)
diff --git a/batch_get_records.py b/batch_get_records.py
new file mode 100644
index 0000000..4b829fa
--- /dev/null
+++ b/batch_get_records.py
@@ -0,0 +1,58 @@
+# -*- coding: UTF-8 -*-
+import api
+import config
+import logging
+import json
+
+LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
+logging.basicConfig(format=LOG_FORMAT, level=logging.INFO)
+
+def main():
+ client = api.Client(config.LARK_HOST)
+
+ # Get tenant access token
+ try:
+ access_token = client.get_tenant_access_token(config.APP_ID, config.APP_SECRET)
+ except Exception as e:
+ logging.error(f"Could not get access token: {e}")
+ return
+
+ # Use parameters from config or environment
+ APP_TOKEN = config.DEFAULT_APP_TOKEN
+ TABLE_ID = config.BATCH_TABLE_ID
+
+ # First, let's get some record IDs to demonstrate batch retrieval
+ # In a real scenario, you might already have these IDs.
+ logging.info(f"Fetching some record IDs from App Token: {APP_TOKEN}, Table ID: {TABLE_ID}")
+ try:
+ resp = client.get_records_list(access_token, APP_TOKEN, TABLE_ID, page_size=10)
+ records = resp.get('items', [])
+ record_ids = [r['record_id'] for r in records]
+
+ if not record_ids:
+ logging.warning("No records found in the table to perform batch retrieval.")
+ return
+
+ logging.info(f"Found {len(record_ids)} record IDs. Performing batch retrieval...")
+
+ # Performance batch retrieval
+ batch_records = client.batch_get_records(
+ access_token,
+ APP_TOKEN,
+ TABLE_ID,
+ record_ids,
+ with_shared_url=True,
+ automatic_fields=True
+ )
+
+ logging.info(f"Successfully retrieved {len(batch_records)} records via batch_get.")
+
+ if batch_records:
+ print("\nExample Batch Retrieved Record (First item):")
+ print(json.dumps(batch_records[0], ensure_ascii=False, indent=2))
+
+ except Exception as e:
+ logging.error(f"Error during batch record retrieval: {e}")
+
+if __name__ == "__main__":
+ main()
diff --git a/bitable_calendar.py b/bitable_calendar.py
new file mode 100644
index 0000000..303a906
--- /dev/null
+++ b/bitable_calendar.py
@@ -0,0 +1,108 @@
+# -*- coding: UTF-8 -*-
+import api
+import config
+import logging
+from datetime import datetime
+import utils
+from mock import schema, raw_data, name_map
+
+LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
+logging.basicConfig(format=LOG_FORMAT, level=logging.ERROR)
+
+import os
+logging.info(os.getcwd())
+
+def rgb(r: int, g: int, b: int): return (r<<16) + (g<<8) + b
+
+
+def sync_records_to_calendar(client: api.Client, access_token, calendar_id, app_token, table_id):
+ '''generate calendar events from bitable records'''
+
+ records = client.get_records(access_token, app_token, table_id)
+ for record in records:
+ try:
+ fields = record.get('fields')
+ version = fields.get(config.KEY_VERSION)
+ proposal_date = datetime.fromtimestamp(fields.get(config.KEY_STARTUP) / 1000)
+ delivery_date = datetime.fromtimestamp(fields.get(config.KEY_SUBMIT) / 1000)
+ expected_online_time = datetime.fromtimestamp(fields.get(config.KEY_GREY) / 1000)
+ online_time = datetime.fromtimestamp(fields.get(config.KEY_GA) / 1000)
+ print(version, proposal_date, delivery_date, expected_online_time, online_time)
+ print(client.create_event(access_token, calendar_id, start_time=proposal_date, end_time=delivery_date, summary=config.FORMATTER_DEV.format(version), color=rgb(221, 88, 24)))
+ print(client.create_event(access_token, calendar_id, start_time=delivery_date, end_time=expected_online_time, summary=config.FORMATTER_TEST.format(version), color=rgb(85, 220, 101)))
+ print(client.create_event(access_token, calendar_id, start_time=expected_online_time, end_time=online_time, summary=config.FORMATTER_GREY.format(version), color=rgb(118, 244, 244)))
+ except TypeError:
+ pass
+
+
+
+def write_bitable(client: api.Client, access_token, raw_data, schema, name_map) -> str:
+ '''write project release date info to bitable'''
+ table_id = client.create_table(access_token, config.APP_TOKEN, config.TABLE_NAME)
+ resp = client.get_fields_list(access_token, config.APP_TOKEN, table_id)
+ current_fields = resp['items']
+
+ for index, (field_name, field_type) in enumerate(schema.items()):
+ try:
+ if index >= len(current_fields):
+ print(client.add_field(access_token, config.APP_TOKEN, table_id, name_map[field_name], field_type))
+ else:
+ print(client.update_field(
+ access_token,
+ config.APP_TOKEN,
+ table_id,
+ current_fields[index]['field_id'],
+ name_map[field_name],
+ field_type))
+ except utils.LarkException as e:
+ if e.code == 1254606: # DataNotChange
+ pass
+ else:
+ raise
+
+ resp = client.get_records_list(access_token, config.APP_TOKEN, table_id)
+ current_records = resp['items']
+ if current_records is None:
+ current_records = []
+
+ updated_records = []
+ created_records = []
+
+ for index, version_info in enumerate(raw_data):
+ fields = {}
+ for field_name, field_value in version_info.items():
+ if schema[field_name] == 5:
+ field_value = datetime.strptime(field_value, '%Y-%m-%d').timestamp() * 1000
+ fields[name_map[field_name]] = field_value
+ if index >= len(current_records):
+ created_records.append({'fields': fields})
+ else:
+ updated_records.append({'record_id': current_records[index]['record_id'], 'fields': fields})
+ if updated_records:
+ resp = client.batch_update_records(access_token, config.APP_TOKEN, table_id, updated_records)
+ if created_records:
+ resp = client.batch_create_records(access_token, config.APP_TOKEN, table_id, created_records)
+ return table_id
+
+def bitable_to_calendar():
+ '''write project release date info to bitable and sync it to calendar'''
+ # init api client
+ client = api.Client(config.LARK_HOST)
+
+ # get tenant access token
+ access_token = client.get_tenant_access_token(config.APP_ID, config.APP_SECRET)
+
+ # create a new table and push local json-like version iteration data to it
+ table_id = write_bitable(client, access_token, raw_data, schema, name_map)
+
+ # create new calendar and get its id
+ calendar = client.create_calendar(access_token, permissions='public', summary=config.SUMMARY)
+ print(calendar)
+ calendar_id = calendar['calendar_id']
+
+ # sync records to calendar
+ sync_records_to_calendar(client, access_token, calendar_id, config.APP_TOKEN, table_id)
+
+
+if __name__ == "__main__":
+ bitable_to_calendar()
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..d45c5cc
--- /dev/null
+++ b/config.py
@@ -0,0 +1,54 @@
+# -*- coding: UTF-8 -*-
+import os
+from dotenv import load_dotenv, find_dotenv
+
+load_dotenv(find_dotenv())
+
+# load from env
+APP_ID = os.getenv("APP_ID")
+APP_SECRET = os.getenv("APP_SECRET")
+LARK_HOST = os.getenv("LARK_HOST")
+APP_TOKEN=os.getenv("APP_TOKEN")
+
+# AI API Configurations
+AI_API_KEY = os.getenv("AI_API_KEY", "sk-orez64WkG1NkfksB5j_hGA")
+AI_BASE_URL = os.getenv("AI_BASE_URL", "https://oai.bwgdi.com/v1")
+AI_MODEL = os.getenv("AI_MODEL", "MiniMaxAI")
+
+# merge source and target configs (placeholders)
+MERGE_SOURCE_APP_TOKEN_1 = os.getenv("MERGE_SOURCE_APP_TOKEN_1", "source_app_token_1")
+MERGE_SOURCE_TABLE_ID_1 = os.getenv("MERGE_SOURCE_TABLE_ID_1", "source_table_id_1")
+MERGE_SOURCE_APP_TOKEN_2 = os.getenv("MERGE_SOURCE_APP_TOKEN_2", "source_app_token_2")
+MERGE_SOURCE_TABLE_ID_2 = os.getenv("MERGE_SOURCE_TABLE_ID_2", "source_table_id_2")
+MERGE_TARGET_APP_TOKEN = os.getenv("MERGE_TARGET_APP_TOKEN", "target_app_token")
+
+# localization
+SUMMARY = '项目版本迭代计划日历'
+TABLE_NAME = '项目版本迭代计划表'
+KEY_VERSION='迭代版本'
+KEY_STARTUP='启动时间'
+KEY_SUBMIT='提测时间'
+KEY_GREY='灰度时间'
+KEY_GA='全量时间'
+
+FORMATTER_DEV = '版本{}开工'
+FORMATTER_TEST = '版本{}测试'
+FORMATTER_GREY = '版本{}灰度'
+
+
+import json
+
+# Web View Tabs Configuration
+WEB_VIEW_TABS_ENV = os.getenv("WEB_VIEW_TABS")
+if WEB_VIEW_TABS_ENV:
+ try:
+ WEB_VIEW_TABS = json.loads(WEB_VIEW_TABS_ENV)
+ except Exception:
+ WEB_VIEW_TABS = []
+else:
+ WEB_VIEW_TABS = []
+
+# Default table configuration for single table operations
+DEFAULT_APP_TOKEN = os.getenv("DEFAULT_APP_TOKEN", "")
+DEFAULT_TABLE_ID = os.getenv("DEFAULT_TABLE_ID", "")
+BATCH_TABLE_ID = os.getenv("BATCH_TABLE_ID", "")
diff --git a/exec.ps1 b/exec.ps1
new file mode 100644
index 0000000..60033f3
--- /dev/null
+++ b/exec.ps1
@@ -0,0 +1,2 @@
+docker build -t bitable-calendar .
+docker run --env-file .env -it bitable-calendar
diff --git a/exec.sh b/exec.sh
new file mode 100644
index 0000000..60033f3
--- /dev/null
+++ b/exec.sh
@@ -0,0 +1,2 @@
+docker build -t bitable-calendar .
+docker run --env-file .env -it bitable-calendar
diff --git a/export_web_view.py b/export_web_view.py
new file mode 100644
index 0000000..dbf0d32
--- /dev/null
+++ b/export_web_view.py
@@ -0,0 +1,971 @@
+import api
+import config
+import logging
+import json
+from get_records import get_all_records
+
+LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
+
+TABS = config.WEB_VIEW_TABS
+
+
+logging.basicConfig(format=LOG_FORMAT, level=logging.INFO)
+
+HTML_TEMPLATE = r"""
+
+
+
+
+Bitable Records - Interactive View
+
+
+
+
+
ALL IN AI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
+
+def build_html(client, access_token):
+ tab_data_list = []
+
+ for tab in TABS:
+ logging.info(f"Fetching records for tab [{tab['name']}]...")
+ records = get_all_records(client, access_token, tab['app_token'], tab['table_id'])
+ logging.info(f"Tab [{tab['name']}] retrieved {len(records)} records.")
+ tab_data_list.append({
+ "name": tab['name'],
+ "records": records
+ })
+
+ # Dump records to json
+ records_json = json.dumps(tab_data_list, ensure_ascii=False)
+
+ final_html = HTML_TEMPLATE.replace('{RECORDS_JSON}', records_json)
+ final_html = final_html.replace('{DEFAULT_AI_KEY}', config.AI_API_KEY or '')
+ final_html = final_html.replace('{DEFAULT_AI_URL}', config.AI_BASE_URL or 'https://api.openai.com/v1')
+ final_html = final_html.replace('{DEFAULT_AI_MODEL}', config.AI_MODEL or 'gpt-4o')
+ return final_html
+
+def main():
+ client = api.Client(config.LARK_HOST)
+
+ try:
+ access_token = client.get_tenant_access_token(config.APP_ID, config.APP_SECRET)
+ except Exception as e:
+ logging.error(f"Could not get access token: {e}")
+ return
+
+ final_html = build_html(client, access_token)
+
+ output_file = "outline_view.html"
+ with open(output_file, "w", encoding="utf-8") as f:
+ f.write(final_html)
+
+ logging.info(f"Successfully generated HTML view at {output_file}")
+ print(f"\n✅ HTML view generated! You can open '/home/verachen/Workspace/feishu/bitable_calendar/python/{output_file}' in your browser.")
+
+if __name__ == "__main__":
+ main()
diff --git a/get_records.py b/get_records.py
new file mode 100644
index 0000000..509ccfc
--- /dev/null
+++ b/get_records.py
@@ -0,0 +1,59 @@
+# -*- coding: UTF-8 -*-
+import api
+import config
+import logging
+import json
+
+LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
+logging.basicConfig(format=LOG_FORMAT, level=logging.INFO)
+
+def get_all_records(client: api.Client, access_token: str, app_token: str, table_id: str):
+ """Retrieve all records from a given table, handling pagination if necessary."""
+ records = []
+ page_token = None
+ while True:
+ resp = client.get_records_list(access_token, app_token, table_id, page_token=page_token)
+ items = resp.get('items', [])
+ if items:
+ records.extend(items)
+ if resp.get('has_more'):
+ page_token = resp.get('page_token')
+ else:
+ break
+ return records
+
+
+if __name__ == "__main__":
+ client = api.Client(config.LARK_HOST)
+
+ # Get tenant access token
+ try:
+ access_token = client.get_tenant_access_token(config.APP_ID, config.APP_SECRET)
+ except Exception as e:
+ logging.error(f"Could not get access token: {e}")
+ exit(1)
+
+ logging.info(f"Using App ID: {config.APP_ID}")
+
+ # Please replace these with your actual app_token and table_id
+ APP_TOKEN = config.DEFAULT_APP_TOKEN
+ TABLE_ID = config.DEFAULT_TABLE_ID
+
+ if APP_TOKEN == "your_app_token_here" or TABLE_ID == "your_table_id_here":
+ logging.warning("Please specify APP_TOKEN and TABLE_ID in the script to get records.")
+ logging.info("For example, you can edit this script to set APP_TOKEN and TABLE_ID, then run it again.")
+ else:
+ logging.info(f"Fetching records for App Token: {APP_TOKEN}, Table ID: {TABLE_ID}")
+ records = get_all_records(client, access_token, APP_TOKEN, TABLE_ID)
+ logging.info(f"Successfully retrieved {len(records)} records.")
+
+ # Print the first record as an example of the structure
+ if records:
+ print("\nExample Record (First item):")
+ print(json.dumps(records[0], ensure_ascii=False, indent=2))
+
+ # Example to use batch_get_records if you have specific record IDs:
+ # record_ids = [r['record_id'] for r in records[:5]] # get first 5 ids
+ # if record_ids:
+ # batch_records = client.batch_get_records(access_token, APP_TOKEN, TABLE_ID, record_ids)
+ # logging.info(f"Batch GET returned {len(batch_records)} records.")
diff --git a/import_bitables.py b/import_bitables.py
new file mode 100644
index 0000000..8ef02c1
--- /dev/null
+++ b/import_bitables.py
@@ -0,0 +1,158 @@
+# -*- coding: UTF-8 -*-
+import api
+import config
+import logging
+import utils
+import copy
+import time
+
+LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
+logging.basicConfig(format=LOG_FORMAT, level=logging.INFO)
+
+def get_all_records(client: api.Client, access_token: str, app_token: str, table_id: str):
+ records = []
+ page_token = None
+ while True:
+ resp = client.get_records_list(access_token, app_token, table_id, page_token=page_token)
+ items = resp.get('items', [])
+ if items:
+ records.extend(items)
+ if resp.get('has_more'):
+ page_token = resp.get('page_token')
+ else:
+ break
+ return records
+
+
+def get_all_fields(client: api.Client, access_token: str, app_token: str, table_id: str):
+ fields = []
+ page_token = None
+ while True:
+ resp = client.get_fields_list(access_token, app_token, table_id, page_token=page_token)
+ items = resp.get('items', [])
+ if items:
+ fields.extend(items)
+ if resp.get('has_more'):
+ page_token = resp.get('page_token')
+ else:
+ break
+ return fields
+
+
+def get_all_tables(client: api.Client, access_token: str, app_token: str):
+ tables = []
+ page_token = None
+ while True:
+ resp = client.get_tables_list(access_token, app_token, page_token=page_token)
+ items = resp.get('items', [])
+ if items:
+ tables.extend(items)
+ if resp.get('has_more'):
+ page_token = resp.get('page_token')
+ else:
+ break
+ return tables
+
+
+def copy_single_table(client: api.Client, access_token: str, source_app_token: str, source_table_id: str, target_app_token: str, table_name: str):
+ logging.info(f"Copying table '{table_name}' ({source_table_id}) from {source_app_token} to {target_app_token}")
+
+ fields = get_all_fields(client, access_token, source_app_token, source_table_id)
+
+ # Create Target Table
+ target_table_id = client.create_table(access_token, target_app_token, table_name)
+
+ # Get initial default fields set by Feishu
+ target_initial_fields = get_all_fields(client, access_token, target_app_token, target_table_id)
+ initial_field_names = {f['field_name']: f['field_id'] for f in target_initial_fields}
+
+ target_field_map = {}
+
+ for f in fields:
+ name = f['field_name']
+ ftype = f['type']
+ fprop = copy.deepcopy(f.get('property'))
+
+ # Clean up IDs from options to avoid errors
+ if fprop and 'options' in fprop:
+ for opt in fprop['options']:
+ if 'id' in opt:
+ del opt['id']
+
+ if name in initial_field_names:
+ field_id = initial_field_names[name]
+ try:
+ client.update_field(access_token, target_app_token, target_table_id, field_id, name, ftype, fprop)
+ target_field_map[name] = field_id
+ except utils.LarkException as e:
+ logging.error(f"Failed to update default field '{name}': {e}")
+ else:
+ try:
+ resp = client.add_field(access_token, target_app_token, target_table_id, name, ftype, fprop)
+ target_field_map[name] = resp['field_id']
+ except utils.LarkException as e:
+ logging.error(f"Failed to create field '{name}': {e}")
+
+ # Read Records from Source Table
+ records = get_all_records(client, access_token, source_app_token, source_table_id)
+
+ target_records = []
+ for r in records:
+ original_fields = r.get('fields', {})
+ new_fields = {}
+ for fname, fvalue in original_fields.items():
+ if fname in target_field_map:
+ new_fields[fname] = fvalue
+ target_records.append({'fields': new_fields})
+
+ CHUNK_SIZE = 500
+ for i in range(0, len(target_records), CHUNK_SIZE):
+ chunk = target_records[i:i + CHUNK_SIZE]
+ if chunk:
+ try:
+ client.batch_create_records(access_token, target_app_token, target_table_id, chunk)
+ except utils.LarkException as e:
+ logging.error(f"Failed to insert chunk of records: {e}")
+
+ logging.info(f"Successfully copied table '{table_name}' to {target_table_id}!")
+ return target_table_id
+
+
+def import_all_tables(client: api.Client, access_token: str, source_app_tokens: list, target_app_token: str):
+ """
+ Imports all tables from a list of source bitable Apps into the target App.
+ """
+ for source_app_token in source_app_tokens:
+ logging.info(f"Fetching tables from source app: {source_app_token}")
+ try:
+ tables = get_all_tables(client, access_token, source_app_token)
+ for table in tables:
+ source_table_id = table['table_id']
+ table_name = table['name']
+ copy_single_table(client, access_token, source_app_token, source_table_id, target_app_token, table_name)
+ except Exception as e:
+ logging.error(f"Failed to copy tables from {source_app_token}: {e}")
+
+if __name__ == "__main__":
+ client = api.Client(config.LARK_HOST)
+
+ try:
+ access_token = client.get_tenant_access_token(config.APP_ID, config.APP_SECRET)
+ except Exception as e:
+ logging.error(f"Could not get access token: {e}")
+ exit(1)
+
+ logging.info(f"Using App ID: {config.APP_ID}")
+
+ # Setup source and target tokens. You can put multiple source app tokens in a list.
+ SOURCE_APP_TOKENS = [
+ config.MERGE_SOURCE_APP_TOKEN_1, # Or replace with an actual App token string
+ # "another_source_app_token",
+ ]
+ TARGET_APP_TOKEN = config.MERGE_TARGET_APP_TOKEN
+
+ is_placeholder = (config.MERGE_SOURCE_APP_TOKEN_1 == "source_app_token_1")
+ if is_placeholder:
+ logging.warning("Please update config.py or environment variables with valid MERGE_* tokens to execute.")
+ else:
+ import_all_tables(client, access_token, SOURCE_APP_TOKENS, TARGET_APP_TOKEN)
diff --git a/list_test.py b/list_test.py
new file mode 100644
index 0000000..9f00738
--- /dev/null
+++ b/list_test.py
@@ -0,0 +1,39 @@
+import json
+
+import lark_oapi as lark
+from lark_oapi.api.bitable.v1 import *
+
+APP_ID="cli_a92c04f105f8dcb0"
+APP_SECRET="5ogsQbtBQ1fPDvwdsykdecpVLT2EyXZc"
+
+# SDK 使用说明: https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/server-side-sdk/python--sdk/preparations-before-development
+# 以下示例代码默认根据文档示例值填充,如果存在代码问题,请在 API 调试台填上相关必要参数后再复制代码使用
+# 复制该 Demo 后, 需要将 "YOUR_APP_ID", "YOUR_APP_SECRET" 替换为自己应用的 APP_ID, APP_SECRET.
+def main():
+ # 创建client
+ client = lark.Client.builder() \
+ .app_id("APP_ID") \
+ .app_secret("APP_SECRET") \
+ .log_level(lark.LogLevel.DEBUG) \
+ .build()
+
+ # 构造请求对象
+ request: ListAppTableRequest = ListAppTableRequest.builder() \
+ .page_size(20) \
+ .build()
+
+ # 发起请求
+ response: ListAppTableResponse = client.bitable.v1.app_table.list(request)
+
+ # 处理失败返回
+ if not response.success():
+ lark.logger.error(
+ f"client.bitable.v1.app_table.list failed, code: {response.code}, msg: {response.msg}, log_id: {response.get_log_id()}, resp: \n{json.dumps(json.loads(response.raw.content), indent=4, ensure_ascii=False)}")
+ return
+
+ # 处理业务结果
+ lark.logger.info(lark.JSON.marshal(response.data, indent=4))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..7d486fd
--- /dev/null
+++ b/main.py
@@ -0,0 +1,6 @@
+def main():
+ print("Hello from python!")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/merge_bitables.py b/merge_bitables.py
new file mode 100644
index 0000000..a0e2952
--- /dev/null
+++ b/merge_bitables.py
@@ -0,0 +1,221 @@
+# -*- coding: UTF-8 -*-
+import api
+import config
+import logging
+import utils
+
+LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
+logging.basicConfig(format=LOG_FORMAT, level=logging.INFO)
+
+def get_all_records(client: api.Client, access_token: str, app_token: str, table_id: str):
+ """Retrieve all records from a given table, handling pagination if necessary."""
+ records = []
+ page_token = None
+ while True:
+ resp = client.get_records_list(access_token, app_token, table_id, page_token=page_token)
+ items = resp.get('items', [])
+ if items:
+ records.extend(items)
+ if resp.get('has_more'):
+ page_token = resp.get('page_token')
+ else:
+ break
+ return records
+
+
+def get_all_fields(client: api.Client, access_token: str, app_token: str, table_id: str):
+ """Retrieve all fields from a given table, handling pagination if necessary."""
+ fields = []
+ page_token = None
+ while True:
+ resp = client.get_fields_list(access_token, app_token, table_id, page_token=page_token)
+ items = resp.get('items', [])
+ if items:
+ fields.extend(items)
+ if resp.get('has_more'):
+ page_token = resp.get('page_token')
+ else:
+ break
+ return fields
+
+
+def merge_schema(fields1: list, fields2: list):
+ """
+ Merge two lists of field definitions into one unified schema list.
+ If fields have the same name, we assume they are the same field.
+ We'll keep the definition from fields1 if there's a collision.
+ """
+ merged_fields = {}
+
+ # Process fields from first table
+ for f in fields1:
+ # We use a deep copy-like approach or just assign, but we will mutate properties later
+ import copy
+ merged_fields[f['field_name']] = {
+ 'field_name': f['field_name'],
+ 'type': f['type'],
+ 'property': copy.deepcopy(f.get('property'))
+ }
+
+ # Process fields from second table (add if not exists)
+ for f in fields2:
+ import copy
+ if f['field_name'] not in merged_fields:
+ merged_fields[f['field_name']] = {
+ 'field_name': f['field_name'],
+ 'type': f['type'],
+ 'property': copy.deepcopy(f.get('property'))
+ }
+ else:
+ # Merge options if both are SingleSelect (3) or MultiSelect (4)
+ existing = merged_fields[f['field_name']]
+ if existing['type'] in (3, 4) and existing.get('property', {}).get('options') and f.get('property', {}).get('options'):
+ existing_names = {opt['name'] for opt in existing['property']['options']}
+ for opt in f['property']['options']:
+ if opt['name'] not in existing_names:
+ existing['property']['options'].append(copy.deepcopy(opt))
+ existing_names.add(opt['name'])
+
+ # Clean up option IDs to prevent insertion errors in the target table
+ for f_name, f_def in merged_fields.items():
+ if f_def.get('property') and 'options' in f_def['property']:
+ for opt in f_def['property']['options']:
+ if 'id' in opt:
+ del opt['id']
+
+ # Add a custom "Source" field
+ merged_fields['Source'] = {
+ 'field_name': 'Source',
+ 'type': 1, # Text type
+ 'property': None
+ }
+
+ return list(merged_fields.values())
+
+
+def merge_bitables(
+ client: api.Client,
+ access_token: str,
+ source_app_token_1: str,
+ source_table_id_1: str,
+ source_app_token_2: str,
+ source_table_id_2: str,
+ target_app_token: str,
+ target_table_name: str = "Merged Table"
+):
+ """
+ Core function to merge two bitables into a single one.
+ """
+ logging.info(f"Merging {source_app_token_1}/{source_table_id_1} and {source_app_token_2}/{source_table_id_2} into {target_app_token}")
+
+ # 1. Read schema from both tables
+ logging.info("Reading schema from source tables...")
+ fields1 = get_all_fields(client, access_token, source_app_token_1, source_table_id_1)
+ fields2 = get_all_fields(client, access_token, source_app_token_2, source_table_id_2)
+
+ # 2. Merge schema
+ unified_schema = merge_schema(fields1, fields2)
+
+ # 3. Create Target Table
+ logging.info(f"Creating target table '{target_table_name}'...")
+ target_table_id = client.create_table(access_token, target_app_token, target_table_name)
+
+ # The new table will have some default columns.
+ # Let's get them, so we can update them or ignore them.
+ target_initial_fields = client.get_fields_list(access_token, target_app_token, target_table_id).get('items', [])
+ initial_field_names = {f['field_name']: f['field_id'] for f in target_initial_fields}
+
+ # 4. Create Fields in Target Table
+ logging.info("Creating fields in target table...")
+ target_field_map = {} # Maps field name to its new field_id in the target table
+
+ for field_def in unified_schema:
+ name = field_def['field_name']
+ ftype = field_def['type']
+ fprop = field_def['property']
+
+ # If the default table creation already made this field (e.g. initial '多行文本' / Text), we can update it or skip
+ if name in initial_field_names:
+ field_id = initial_field_names[name]
+ # Optionally update it the type is different (often default is just 'Text')
+ client.update_field(access_token, target_app_token, target_table_id, field_id, name, ftype, fprop)
+ target_field_map[name] = field_id
+ else:
+ try:
+ resp = client.add_field(access_token, target_app_token, target_table_id, name, ftype, fprop)
+ target_field_map[name] = resp['field_id']
+ except utils.LarkException as e:
+ logging.error(f"Failed to create field '{name}': {e}")
+
+ # 5. Read Records from Source Tables
+ logging.info("Reading records from source tables...")
+ records1 = get_all_records(client, access_token, source_app_token_1, source_table_id_1)
+ records2 = get_all_records(client, access_token, source_app_token_2, source_table_id_2)
+
+ # 6. Map and batch insert records
+ logging.info("Mapping and inserting records...")
+
+ def process_records(source_records, source_name):
+ batch = []
+ for r in source_records:
+ original_fields = r.get('fields', {})
+ new_fields = {}
+ for fname, fvalue in original_fields.items():
+ if fname in target_field_map:
+ # In bitable API, it's safer to use the new field_names (or their ids depending on the specific endpoint).
+ # The get_records endpoint returns field names as keys. The batch_create_records also accepts field names as keys if no 'user_id_type' strictly enforces IDs.
+ # We'll use the mapped names to be safe.
+ new_fields[fname] = fvalue
+
+ # Add Source
+ new_fields['Source'] = source_name
+ batch.append({'fields': new_fields})
+ return batch
+
+ target_records = []
+ target_records.extend(process_records(records1, f"{source_app_token_1}/{source_table_id_1}"))
+ target_records.extend(process_records(records2, f"{source_app_token_2}/{source_table_id_2}"))
+
+ # Feishu batch insert has a limit of 500 per request usually, so we chunk it just in case
+ CHUNK_SIZE = 500
+ for i in range(0, len(target_records), CHUNK_SIZE):
+ chunk = target_records[i:i + CHUNK_SIZE]
+ if chunk:
+ try:
+ client.batch_create_records(access_token, target_app_token, target_table_id, chunk)
+ except utils.LarkException as e:
+ logging.error(f"Failed to insert chunk of records: {e}")
+
+ logging.info(f"Successfully merged into {target_table_id}!")
+ return target_table_id
+
+if __name__ == "__main__":
+ # Example execution using vars from config.py
+
+ client = api.Client(config.LARK_HOST)
+
+ # Get tenant access token
+ try:
+ access_token = client.get_tenant_access_token(config.APP_ID, config.APP_SECRET)
+ except Exception as e:
+ logging.error(f"Could not get access token: {e}")
+ exit(1)
+
+ logging.info(f"Using App ID: {config.APP_ID}")
+
+ # These need to be valid app_tokens and table_ids for this script to actually work
+ is_placeholder = (config.MERGE_SOURCE_APP_TOKEN_1 == "source_app_token_1")
+
+ if is_placeholder:
+ logging.warning("Please update config.py or environment variables with valid MERGE_* tokens to execute.")
+ else:
+ merge_bitables(
+ client=client,
+ access_token=access_token,
+ source_app_token_1=config.MERGE_SOURCE_APP_TOKEN_1,
+ source_table_id_1=config.MERGE_SOURCE_TABLE_ID_1,
+ source_app_token_2=config.MERGE_SOURCE_APP_TOKEN_2,
+ source_table_id_2=config.MERGE_SOURCE_TABLE_ID_2,
+ target_app_token=config.MERGE_TARGET_APP_TOKEN,
+ target_table_name="Merged Data"
+ )
diff --git a/mock.py b/mock.py
new file mode 100755
index 0000000..53e0277
--- /dev/null
+++ b/mock.py
@@ -0,0 +1,40 @@
+from config import KEY_SUBMIT, KEY_GREY, KEY_GA, KEY_STARTUP, KEY_VERSION
+schema = {
+ "version": 1,
+ "proposal": 5,
+ "delivery": 5,
+ "expected": 5,
+ "online": 5,
+}
+
+name_map = {
+ "version": KEY_VERSION,
+ "proposal": KEY_STARTUP,
+ "delivery": KEY_SUBMIT,
+ "expected": KEY_GREY,
+ "online": KEY_GA,
+}
+
+raw_data = [
+ {
+ "version": "1.0.0",
+ "proposal": '2022-07-20',
+ "delivery": '2022-7-27',
+ "expected": '2022-07-28',
+ "online": '2022-07-29',
+ },
+ {
+ "version": "1.1.0",
+ "proposal": '2022-07-30',
+ "delivery": '2022-08-04',
+ "expected": '2022-08-05',
+ "online": '2022-08-07',
+ },
+ {
+ "version": "1.2.0",
+ "proposal": '2022-08-09',
+ "delivery": '2022-08-16',
+ "expected": '2022-08-16',
+ "online": '2022-08-16',
+ }
+]
\ No newline at end of file
diff --git a/outline_view.html b/outline_view.html
new file mode 100644
index 0000000..e1d6d5e
--- /dev/null
+++ b/outline_view.html
@@ -0,0 +1,915 @@
+
+
+
+
+
+Bitable Records - Interactive View
+
+
+
+
+
ALL IN AI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..9c1532e
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,10 @@
+[project]
+name = "python"
+version = "0.1.0"
+description = "Add your description here"
+readme = "README.md"
+requires-python = ">=3.13"
+dependencies = [
+ "python-dotenv>=1.2.2",
+ "requests>=2.32.5",
+]
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..323ef8c
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+python-dotenv
+requests
\ No newline at end of file
diff --git a/server.py b/server.py
new file mode 100644
index 0000000..2281353
--- /dev/null
+++ b/server.py
@@ -0,0 +1,76 @@
+# -*- coding: UTF-8 -*-
+import http.server
+import socketserver
+import json
+import logging
+import time
+import os
+import sys
+
+# Import functions from existing scripts
+from export_web_view import HTML_TEMPLATE, build_html
+from get_records import get_all_records
+import api
+import config
+
+PORT = int(os.getenv("PORT", 18080))
+CACHE_DURATION = 60 # Cache data for 60 seconds
+
+# Globals to store cached HTML
+cached_html = None
+last_fetch_time = 0
+
+class ThreadingHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
+ daemon_threads = True
+ allow_reuse_address = True
+
+class FeishuHandler(http.server.SimpleHTTPRequestHandler):
+ def do_GET(self):
+ global cached_html, last_fetch_time
+
+ # Serve dynamic HTML at root or outline_view.html
+ if self.path == '/' or self.path == '/outline_view.html':
+ self.send_response(200)
+ self.send_header('Content-type', 'text/html; charset=utf-8')
+ self.end_headers()
+
+ current_time = time.time()
+ if cached_html is None or (current_time - last_fetch_time) > CACHE_DURATION:
+ logging.info("Fetching fresh records from API (Cache expired or missing)...")
+ try:
+ client = api.Client(config.LARK_HOST)
+ access_token = client.get_tenant_access_token(config.APP_ID, config.APP_SECRET)
+
+ final_html = build_html(client, access_token)
+
+ cached_html = final_html.encode('utf-8')
+ last_fetch_time = current_time
+ logging.info(f"Successfully generated new HTML view. Cached for {CACHE_DURATION}s.")
+ except Exception as e:
+ logging.error(f"Error fetching data: {e}")
+ if cached_html is None:
+ error_msg = f"Internal Server Error: Could not fetch data
{e}
".encode('utf-8')
+ self.wfile.write(error_msg)
+ return
+
+ # Write response
+ self.wfile.write(cached_html)
+ else:
+ # Fallback to serving static files for any other paths
+ super().do_GET()
+
+if __name__ == "__main__":
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
+
+ server_address = ("0.0.0.0", PORT)
+ try:
+ httpd = ThreadingHTTPServer(server_address, FeishuHandler)
+ logging.info(f"🚀 Server successfully started at http://localhost:{PORT}")
+ logging.info("You can now access the viewer directly through the browser.")
+ logging.info("Press Ctrl+C to stop the server.")
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ logging.info("Shutting down the server...")
+ sys.exit(0)
+ except Exception as e:
+ logging.error(f"Could not start server on port {PORT}: {e}")
diff --git a/test_batch_get.py b/test_batch_get.py
new file mode 100644
index 0000000..3c3c9ff
--- /dev/null
+++ b/test_batch_get.py
@@ -0,0 +1,44 @@
+# -*- coding: UTF-8 -*-
+import unittest
+from unittest.mock import MagicMock, patch
+import api
+
+class TestBatchGetChunking(unittest.TestCase):
+ @patch('api.request')
+ def test_batch_get_records_chunking(self, mock_request):
+ # Setup mock client
+ client = api.Client("https://open.feishu.cn")
+ access_token = "fake_token"
+ app_token = "fake_app"
+ table_id = "fake_table"
+
+ # Create 250 record IDs (should result in 3 chunks: 100, 100, 50)
+ record_ids = [f"rec_{i}" for i in range(250)]
+
+ # Mock responses
+ mock_request.side_effect = [
+ {'code': 0, 'data': {'records': [{'record_id': f'rec_{i}'} for i in range(0, 100)]}},
+ {'code': 0, 'data': {'records': [{'record_id': f'rec_{i}'} for i in range(100, 200)]}},
+ {'code': 0, 'data': {'records': [{'record_id': f'rec_{i}'} for i in range(200, 250)]}},
+ ]
+
+ # Call the method
+ results = client.batch_get_records(access_token, app_token, table_id, record_ids)
+
+ # Verify
+ self.assertEqual(len(results), 250)
+ self.assertEqual(mock_request.call_count, 3)
+
+ # Verify the chunks were passed correctly
+ first_call_payload = mock_request.call_args_list[0][0][3] # payload is the 4th positional arg
+ self.assertEqual(len(first_call_payload['record_ids']), 100)
+ self.assertEqual(first_call_payload['record_ids'][0], "rec_0")
+ self.assertEqual(first_call_payload['record_ids'][99], "rec_99")
+
+ last_call_payload = mock_request.call_args_list[2][0][3]
+ self.assertEqual(len(last_call_payload['record_ids']), 50)
+ self.assertEqual(last_call_payload['record_ids'][0], "rec_200")
+ self.assertEqual(last_call_payload['record_ids'][49], "rec_249")
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/utils.py b/utils.py
new file mode 100644
index 0000000..495e278
--- /dev/null
+++ b/utils.py
@@ -0,0 +1,34 @@
+# -*- coding: UTF-8 -*-
+import json
+import logging as logger
+import requests
+class LarkException(Exception):
+ def __init__(self, code=0, msg=None):
+ self.code = code
+ self.msg = msg
+
+ def __str__(self) -> str:
+ return "{}:{}".format(self.code, self.msg)
+
+ __repr__ = __str__
+
+def request(method, url, headers, payload={}, params=None):
+ response = requests.request(method, url, headers=headers, json=payload, params=params)
+ logger.info("URL: " + url)
+ logger.info("X-Tt-Logid: " + response.headers['X-Tt-Logid'])
+ logger.info("headers:\n"+json.dumps(headers,indent=2, ensure_ascii=False))
+ logger.info("payload:\n"+json.dumps(payload,indent=2, ensure_ascii=False))
+ resp = {}
+ if response.text[0] == '{':
+ resp = response.json()
+ logger.info("response:\n"+json.dumps(resp,indent=2, ensure_ascii=False))
+ else:
+ logger.info("response:\n"+response.text)
+ code = resp.get("code", -1)
+ if code == -1:
+ code = resp.get("StatusCode", -1)
+ if code == -1 and response.status_code != 200:
+ response.raise_for_status()
+ if code != 0:
+ raise LarkException(code=code, msg=resp.get("msg", ""))
+ return resp
\ No newline at end of file
diff --git a/uv.lock b/uv.lock
new file mode 100644
index 0000000..1b81676
--- /dev/null
+++ b/uv.lock
@@ -0,0 +1,110 @@
+version = 1
+revision = 2
+requires-python = ">=3.13"
+
+[[package]]
+name = "certifi"
+version = "2026.2.25"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
+ { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
+ { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
+ { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
+ { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
+ { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
+ { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
+ { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
+ { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
+ { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
+]
+
+[[package]]
+name = "idna"
+version = "3.11"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
+]
+
+[[package]]
+name = "python"
+version = "0.1.0"
+source = { virtual = "." }
+dependencies = [
+ { name = "python-dotenv" },
+ { name = "requests" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "python-dotenv", specifier = ">=1.2.2" },
+ { name = "requests", specifier = ">=2.32.5" },
+]
+
+[[package]]
+name = "python-dotenv"
+version = "1.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
+]
+
+[[package]]
+name = "urllib3"
+version = "2.6.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
+]
diff --git a/列出数据表.md b/列出数据表.md
new file mode 100644
index 0000000..3da2a82
--- /dev/null
+++ b/列出数据表.md
@@ -0,0 +1,117 @@
+# 列出数据表
+
+列出多维表格中的所有数据表,包括其 ID、版本号和名称。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables
+HTTP Method | GET
+接口频率限制 | [20 次/秒](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 获取数据表信息(base:table:read)
查看、评论、编辑和管理多维表格(bitable:app)
查看、评论和导出多维表格(bitable:app:readonly)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+
+### 路径参数
+
+名称 | 类型 | 描述
+---|---|---
+app_token | string | 多维表格 App 的唯一标识。不同形态的多维表格,其 `app_token` 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 `app_token` 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 `obj_type` 的值为 `bitable` 时,`obj_token` 字段的值才是多维表格的 `app_token`。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
**示例值**:"appbcbWCzen6D8dezhoCH2RpMAh"
+
+### 查询参数
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+page_token | string | 否 | 分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的 page_token,下次遍历可采用该 page_token 获取查询结果
**示例值**:tblsRc9GRRXKqhvW
+page_size | int | 否 | 分页大小
**示例值**:10
**默认值**:`20`
**数据校验规则**:
- 最大值:`100`
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+has_more | boolean | 是否还有更多项
+page_token | string | 分页标记,当 has_more 为 true 时,会同时返回新的 page_token,否则不返回 page_token
+total | int | 总数
+items | app.table\[\] | 数据表信息
+table_id | string | 数据表 ID
+revision | int | 数据表的版本号。对数据表进行修改时更新,如新增、删除记录,修改数据表名称等,初始为 1,每次更新+1
+name | string | 数据表名称
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "has_more": false,
+ "page_token": "tblKz5D60T4JlfcT",
+ "total": 1,
+ "items": [
+ {
+ "table_id": "tblKz5D60T4JlfcT",
+ "revision": 1,
+ "name": "数据表1"
+ }
+ ]
+ }
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+200 | 1254000 | WrongRequestJson | 请求体错误
+200 | 1254001 | WrongRequestBody | 请求体错误
+200 | 1254002 | Fail | 内部错误,有疑问可咨询客服
+200 | 1254003 | WrongBaseToken | app_token 错误
+200 | 1254004 | WrongTableId | table_id 错误
+200 | 1254005 | WrongViewId | view_id 错误
+200 | 1254006 | WrongRecordId | 检查 record_id
+200 | 1254007 | EmptyValue | 空值
+200 | 1254008 | EmptyView | 空视图
+200 | 1254009 | WrongFieldId | 字段 id 错误
+200 | 1254010 | ReqConvError | 请求错误
+400 | 1254011 | Page size must greater than 0. | 确认page_size参数的值符合要求。
+200 | 1254030 | TooLargeResponse | 响应体过大
+400 | 1254036 | Base is copying, please try again later. | 多维表格副本复制中,稍后重试
+200 | 1254040 | BaseTokenNotFound | app_token 不存在
+200 | 1254041 | TableIdNotFound | table_id 不存在
+200 | 1254042 | ViewIdNotFound | view_id 不存在
+200 | 1254043 | RecordIdNotFound | record_id 不存在
+200 | 1254044 | FieldIdNotFound | field_id 不存在
+200 | 1254060 | TextFieldConvFail | 多行文本字段错误
+200 | 1254061 | NumberFieldConvFail | 数字字段错误
+200 | 1254062 | SingleSelectFieldConvFail | 单选字段错误
+200 | 1254063 | MultiSelectFieldConvFail | 多选字段错误
+200 | 1254064 | DatetimeFieldConvFail | 日期字段错误
+200 | 1254065 | CheckboxFieldConvFail | 复选框字段错误
+200 | 1254066 | UserFieldConvFail | 人员字段有误。原因可能是:
- `user_id_type` 参数指定的 ID 类型与传入的 ID 类型不匹配
- 传入了不识别的类型或结构,目前只支持填写 `id` 参数,且需要传入数组
- 跨应用传入了 `open_id`。如果跨应用传入 ID,建议使用 `user_id`。不同应用获取的 `open_id` 不能交叉使用
+200 | 1254067 | LinkFieldConvFail | 关联字段错误
+200 | 1254100 | TableExceedLimit | 数据表或仪表盘数量超限。每个多维表格中,数据表加仪表盘的数量最多为 100 个
+200 | 1254101 | ViewExceedLimit | 视图数量超限, 限制200个
+200 | 1254102 | FileExceedLimit | 超限
+200 | 1254103 | RecordExceedLimit | 记录数量超限, 限制20,000条
+200 | 1254104 | RecordAddOnceExceedLimit | 单次添加记录数量超限, 限制500条
+200 | 1254130 | TooLargeCell | 格子内容过大
+200 | 1254290 | TooManyRequest | 请求过快,稍后重试
+200 | 1254291 | Write conflict | 同一个数据表(table) 不支持并发调用写接口,请检查是否存在并发调用写接口。写接口包括:新增、修改、删除记录;新增、修改、删除字段;修改表单;修改视图等。
+200 | 1254301 | OperationTypeError | 多维表格未开启高级权限或不支持开启高级权限
+403 | 1254302 | The role has no permissions. | 无访问权限, 常由表格开启了高级权限造成, 如果是用应用请求的话,目前有两种方法对应用赋予高级权限,第一种方法为在表格中添加应用为协作者并将应用设置为管理员,第二种方法为在一个用户群中将应用添加为机器人, 并在高级权限的角色中添加该用户群,从而赋予对应的权限。
+200 | 1255001 | InternalError | 内部错误,有疑问可咨询客服
+200 | 1255002 | RpcError | 内部错误,有疑问可咨询客服
+200 | 1255003 | MarshalError | 序列化错误,有疑问可咨询客服
+200 | 1255004 | UmMarshalError | 反序列化错误
+200 | 1255005 | ConvError | 内部错误,有疑问可咨询客服处
+504 | 1255040 | Request timed out, please try again later. | 请求超时,请进行重试
diff --git a/创建多维表格.md b/创建多维表格.md
new file mode 100644
index 0000000..3625362
--- /dev/null
+++ b/创建多维表格.md
@@ -0,0 +1,124 @@
+# 创建多维表格
+
+在指定文件夹中创建一个多维表格,包含一个空白的数据表。
+
+**注意事项**:要基于模板创建多维表格,可先获取模板多维表格 `app_token` 作为文件 token,再调用[复制文件](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/file/copy)接口创建多维表格。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/bitable/v1/apps
+HTTP Method | POST
+接口频率限制 | [20 次/分钟](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 创建多维表格(base:app:create)
查看、评论、编辑和管理多维表格(bitable:app)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+Content-Type | string | 是 | **固定值**:"application/json; charset=utf-8"
+
+### 请求体
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+name | string | 否 | 多维表格 App 名称。最长为 255 个字符。
**示例值**:"一篇新的多维表格"
+folder_token | string | 否 | 多维表格 App 归属文件夹。默认为空,表示多维表格将被创建在云空间根目录。了解如何获取文件夹 Token,参考[如何获取云文档资源相关 Token](https://open.feishu.cn/document/ukTMukTMukTM/uczNzUjL3czM14yN3MTN#08bb5df6)。
**注意**:
请确保调用身份拥有在该文件夹中的编辑权限。若应用使用的是 `tenant_access_token` 权限,此处仅可指定应用创建的文件夹。详情参考[如何为应用开通云文档相关资源的权限](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-add-permissions-to-app)。
**示例值**:"fldcnqquW1svRIYVT2Np6Iabcef"
+time_zone | string | 否 | 文档时区,详情参考[文档时区介绍](https://feishu.feishu.cn/docx/YKRndTM7VoyDqpxqqeEcd67MnEf)。
**示例值**:"Asia/Macau"
+
+### 请求体示例
+```json
+{
+ "name":"一篇新的多维表格",
+ "folder_token": "fldcnqquW1svRIYVT2Np6Iabcef"
+}
+```
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+app | app | 返回响应体
+app_token | string | 多维表格的唯一标识 app_token
+name | string | 多维表格的名称
+folder_token | string | 多维表格 App 归属文件夹
+url | string | 多维表格 App 的 URL 链接
+default_table_id | string | 默认创建的数据表 ID
+time_zone | string | 文档时区
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "data": {
+ "app": {
+ "app_token": "S404b*****e9PQsYDWYcNryFn0g",
+ "default_table_id": "tbl********oumSQ",
+ "folder_token": "fldcnqquW1svRIYVT2Np6Iabcef",
+ "name": "一篇新的多维表格",
+ "url": "https://example.feishu.cn/base/S404b*****e9PQsYDWYcNryFn0g"
+ }
+ },
+ "msg": "success"
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+200 | 1254000 | WrongRequestJson | 请求体错误
+200 | 1254001 | WrongRequestBody | 请求体错误
+200 | 1254002 | Fail | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1254003 | WrongBaseToken | app_token 错误
+200 | 1254004 | WrongTableId | table_id 错误
+200 | 1254005 | WrongViewId | view_id 错误
+200 | 1254006 | WrongRecordId | 检查 record_id
+200 | 1254007 | EmptyValue | 空值
+200 | 1254008 | EmptyView | 空视图
+200 | 1254009 | WrongFieldId | 字段 id 错误
+200 | 1254010 | ReqConvError | 请求错误
+200 | 1254025 | InvalidCopyTypes | Copy Type参数无效
+200 | 1254030 | TooLargeResponse | 响应体过大
+400 | 1254031 | InvalidAppName | 名称不能超过255个字符
+400 | 1254036 | Base is copying, please try again later. | 复制多维表格为异步操作,该错误码表示当前多维表格仍在复制中,在复制期间无法操作当前多维表格。需要等待复制完成后再操作
+200 | 1254040 | BaseTokenNotFound | app_token 不存在
+200 | 1254041 | TableIdNotFound | table_id 不存在
+200 | 1254042 | ViewIdNotFound | view_id 不存在
+200 | 1254043 | RecordIdNotFound | record_id 不存在
+200 | 1254044 | FieldIdNotFound | field_id 不存在
+200 | 1254060 | TextFieldConvFail | 多行文本字段错误
+200 | 1254061 | NumberFieldConvFail | 数字字段错误
+200 | 1254062 | SingleSelectFieldConvFail | 单选字段错误
+200 | 1254063 | MultiSelectFieldConvFail | 多选字段错误
+200 | 1254064 | DatetimeFieldConvFail | 日期字段错误
+200 | 1254065 | CheckboxFieldConvFail | 复选框字段错误
+200 | 1254066 | UserFieldConvFail | 人员字段有误。原因可能是:
- `user_id_type` 参数指定的 ID 类型与传入的 ID 类型不匹配
- 传入了不识别的类型或结构,目前只支持填写 `id` 参数,且需要传入数组
- 跨应用传入了 `open_id`。如果跨应用传入 ID,建议使用 `user_id`。不同应用获取的 `open_id` 不能交叉使用
+200 | 1254067 | LinkFieldConvFail | 关联字段错误
+200 | 1254100 | TableExceedLimit | 数据表或仪表盘数量超限。每个多维表格中,数据表加仪表盘的数量最多为 100 个
+200 | 1254101 | ViewExceedLimit | 视图数量超限,限制 200 个,包括公共视图、锁定视图和个人视图(即只有视图所有者才能看到的视图)
+200 | 1254102 | FileExceedLimit | 文档数量超限
+200 | 1254103 | RecordExceedLimit | 记录数量超限,限制 20,000 条
+200 | 1254104 | RecordAddOnceExceedLimit | 单次添加记录数量超限,限制 500 条
+200 | 1254130 | TooLargeCell | 格子内容过大
+200 | 1254290 | TooManyRequest | 请求过快,稍后重试
+200 | 1254291 | Write conflict | 同一个数据表 (table) 不支持并发调用写接口,请检查是否存在并发调用写接口。写接口包括:新增、修改、删除记录;新增、修改、删除字段;修改表单;修改视图等。
+200 | 1254301 | OperationTypeError | 多维表格未开启高级权限或不支持开启高级权限
+200 | 1254304 | PermNotAllow | 仅企业版和旗舰版飞书支持行列权限
+403 | 1254701 | DriveNodePermNotAllow | 对目标云空间节点没有权限
+404 | 1254702 | DriveNodeNotExist | 云空间节点不存在,检查参数正确性
+400 | 1254800 | InvalidParameter | 参数错误,请根据msg修正后重试
+200 | 1255001 | InternalError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255002 | RpcError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255003 | MarshalError | 序列化错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255004 | UmMarshalError | 反序列化错误
+200 | 1255005 | ConvError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+504 | 1255040 | 请求超时 | 进行重试
diff --git a/复制多维表格.md b/复制多维表格.md
new file mode 100644
index 0000000..a210f7b
--- /dev/null
+++ b/复制多维表格.md
@@ -0,0 +1,108 @@
+# 复制多维表格
+
+复制一个多维表格,可以指定复制到某个有权限的文件夹下。
+
+**注意事项**:当多维表格记录数超 50,000 条可复制上限时,仅可复制多维表格结构。
+
+## 前提条件
+
+调用此接口前,请确保当前调用身份(tenant_access_token 或 user_access_token)已有多维表格和目标文件夹的阅读、编辑等文档权限,否则接口将返回 HTTP 403 或 400 状态码。了解更多,参考[如何为应用或用户开通云文档权限](https://open.feishu.cn/document/ukTMukTMukTM/uczNzUjL3czM14yN3MTN#16c6475a)。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/copy
+HTTP Method | POST
+接口频率限制 | [20 次/分钟](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 复制多维表格(base:app:copy)
查看、评论、编辑和管理多维表格(bitable:app)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+Content-Type | string | 是 | **固定值**:"application/json; charset=utf-8"
+
+### 路径参数
+
+名称 | 类型 | 描述
+---|---|---
+app_token | string | 要复制的多维表格 App 的唯一标识。不同形态的多维表格,其 app_token 的获取方式不同,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview)获取。
**示例值**:"AW3Qbtr2cakCnesXzXVbbsrIcVT "
+
+### 请求体
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+name | string | 否 | 多维表格 App 的名称
**示例值**:"一篇新的多维表格"
+folder_token | string | 否 | 了解如何获取文件夹 Token,参考[如何获取云文档资源相关 Token](https://open.feishu.cn/document/ukTMukTMukTM/uczNzUjL3czM14yN3MTN#08bb5df6)。
**注意**:
请确保调用身份拥有在该文件夹中的编辑权限。若应用使用的是 `tenant_access_token` 权限,此处仅可指定应用创建的文件夹。详情参考[如何为应用开通云文档相关资源的权限](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-add-permissions-to-app)。
**示例值**:"fldcnqquW1svRIYVT2Np6Iabcef"
+without_content | boolean | 否 | 是否复制多维表格中的内容,默认 false,即复制多维表格中的内容。可取值:
* true:不复制
* false:复制
**示例值**:false
+time_zone | string | 否 | 文档时区,详情参考[文档时区介绍](https://feishu.feishu.cn/docx/YKRndTM7VoyDqpxqqeEcd67MnEf)。
**示例值**:"Asia/Shanghai"
+
+### 请求体示例
+```json
+{
+ "name": "一篇新的多维表格",
+ "folder_token": "fldcnqquW1svRIYVT2Np6Iabcef",
+ "without_content": false,
+ "time_zone": "Asia/Shanghai"
+}
+```
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+app | app | 返回响应体
+app_token | string | 多维表格的唯一标识 app_token
+name | string | 多维表格的名称
+folder_token | string | 多维表格 App 归属文件夹
+url | string | 多维表格 App 的 URL 链接
+time_zone | string | 文档时区
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "app": {
+ "app_token": "S404b*****e9PQsYDWYcNryFn0g",
+ "name": "一篇新的多维表格",
+ "folder_token": "fldbco*****CIMltVc",
+ "url": "https://example.feishu.cn/base/S404b*****e9PQsYDWYcNryFn0g",
+ "time_zone": ""
+ }
+ }
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+400 | 1254000 | WrongRequestJson | 请求体错误
+400 | 1254001 | WrongRequestBody | 请求体错误
+400 | 1254002 | Fail | 内部错误,有疑问可咨询客服
+400 | 1254003 | WrongBaseToken | app_token 错误
+400 | 1254031 | InvalidAppName | 多维表格名称格式错误,长度不超过 100 个字符,不能包含 ? / \ * : [ ]
+400 | 1254036 | Base is copying, please try again later. | 多维表格副本复制中,稍后重试
+404 | 1254040 | BaseTokenNotFound | app_token 不存在
+400 | 1254290 | TooManyRequest | 请求过快,稍后重试
+400 | 1254291 | Write conflict | 同一个数据表(table) 不支持并发调用写接口,请检查是否存在并发调用写接口。写接口包括:新增、修改、删除记录;新增、修改、删除字段;修改表单;修改视图等。
+403 | 1254304 | PermNotAllow | 仅企业版和旗舰版飞书支持行列权限
+403 | 1254701 | DriveNodePermNotAllow | 目标文件夹没有权限
+404 | 1254702 | DriveNodeNotExist | 目标文件夹不存在
+400 | 1254800 | InvalidParameter | 参数错误,请根据msg修正后重试
+500 | 1255001 | InternalError | 内部错误,有疑问可咨询客服
+500 | 1255002 | RpcError | 内部错误,有疑问可咨询客服
+500 | 1255003 | MarshalError | 序列化错误,有疑问可咨询客服
+500 | 1255004 | UmMarshalError | 反序列化错误
+500 | 1255005 | ConvError | 内部错误,有疑问可咨询客服处
+504 | 1255040 | 请求超时 | 进行重试
diff --git a/多维表格常见问题.md b/多维表格常见问题.md
new file mode 100644
index 0000000..62a77ed
--- /dev/null
+++ b/多维表格常见问题.md
@@ -0,0 +1,194 @@
+# 多维表格常见问题
+
+## 1. 如何在多维表格中上传附件?
+
+**第 1 步**:调用[上传素材](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/upload_all)或[分片上传素材](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/upload_prepare)接口将附件上传至多维表格,获取文件的 file_token。
+
+**第 2 步**:调用[新增记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/create)或[更新记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/update)接口,将 file_token 的值传入多维表格中。请求示例如下所示:
+file_token 仅支持在当前多维表格中使用,要上传附件至其他多维表格,你仍需要重新上传素材获取新的 file_token。
+```json
+{
+ "records": [
+ {
+ "fields": {
+ "附件": [
+ {
+ "file_token": "boxbcCFb2dBwMK9S8kDILk1tayh"
+ },
+ {
+ "file_token": "boxbcCFb2dBwMK9S8kDILk1tayh"
+ }
+ ]
+ }
+ },
+ {
+ "fields": {
+ "附件": [
+ {
+ "file_token": "boxbcCFb2dBwMK9S8kDILk1tayh"
+ },
+ {
+ "file_token": "boxbcCFb2dBwMK9S8kDILk1tayh"
+ }
+ ]
+ }
+ }
+ ]
+}
+```
+
+## 2. 如何下载多维表格中的附件?
+
+**第 1 步**:调用[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)获取多维表格中附件的 file_token。
+
+**第 2 步**:调用[下载素材](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/download)或者[获取素材临时下载链接](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/drive-v1/media/batch_get_tmp_download_url)接口下载附件。
+
+若多维表格开启了高级权限,你需要添加 extra 参数作为 URL 查询参数鉴权。你可通过以下方式获取 extra 参数:
+
+1. 调用[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)接口,响应示例中会返回附件的下载链接,如下所示:
+ ```json
+ {
+ "code": 0,
+ "data": {
+ "has_more": false,
+ "items": [
+ {
+ "fields": {
+ "附件": [
+ {
+ "file_token": "RSkabsaphoy6yVxK0mGcggabcef",
+ "name": "74d2c703524489dbabcef.png",
+ "size": 87123,
+ "tmp_url": "https://open.feishu.cn/open-apis/drive/v1/medias/batch_get_tmp_download_url?file_tokens=RSkabsaphoy6yVxK0mGcggabcef&extra={"bitablePerm":{"tableId":"tblz8ExGaVhuiSD1","rev":32}}", // 素材临时下载链接对应的 URL 值,需要对此字符串进行 URL 编码
+ "type": "image/png",
+ "url": "https://open.feishu.cn/open-apis/drive/v1/medias/RSkabsaphoy6yVxK0mGcggabcef/download?extra={"bitablePerm":{"tableId":"tblz8ExGaVhuiSD1","rev":32}}" // 下载素材对应的 URL 值,需要对此字符串进行 URL 编码
+ }
+ ]
+ },
+ "record_id": "recbMzD0zT"
+ }
+ ],
+ "total": 1
+ },
+ "msg": "success"
+ }
+ ```
+
+2. 构建示例如下所示:
+
+```JSON
+ {"bitablePerm":{"tableId":"tblO6OeNZxfabcef","attachments":{"fld32zZi5I":{"rec0BuOHq":["boxbcsQNT0JsmrztOnX530abcef"]}}}}
+ // 转义后
+ https://open.feishu.cn/open-apis/drive/v1/medias/boxbcsQNT0JsmrztOnX530abcef/download?extra=%7B%22bitablePerm%22%3A%7B%22tableId%22%3A%22tblO6OeNZxfabcef%22%2C%22attachments%22%3A%7B%22fld32zZi5I%22%3A%7B%22rec0BuOHq%22%3A%5B%22boxbcsQNT0JsmrztOnX530abcef%22%5D%7D%7D%7D%7D
+ ```
+
+## 3. 调用多维表格相关接口,返回 1254040、1254041、1254042、1254043、1254044 等 NotFound 错误码,该如何解决?
+
+上述这些错误码表示未找到到标识各类 ID 对应的资源。你可参考以下方法解决:
+
+1. 确认 ID 是否正确。你可参考[多维表格概述](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview)标获取各类资源的 ID
+2. 如果 ID 对应的资源刚创建完成后出现此类报错,可能是遇到了服务器的主从延迟问题。你可尝试等待几秒之后重试。
+
+## 4. 调用查询记录接口成功,数据返回为空,但实际多维表格存在数据,该如何解决?
+
+这一般是由于多维表格开通了高级权限,导致调用身份权限不足。你需给予调用身份数据表的 **可管理** 权限或多维表格的 **可管理** 等权限,再重新调用。具体步骤如下所示:
+
+- 对用户授予可管理权限,你可在 **多维表格高级权限设置** 中添加用户,为用户开通足够权限。具体可参考飞书帮助中心文档[使用多维表格高级权限](https://www.feishu.cn/hc/zh-CN/articles/588604550568-%E4%BD%BF%E7%94%A8%E5%A4%9A%E7%BB%B4%E8%A1%A8%E6%A0%BC%E9%AB%98%E7%BA%A7%E6%9D%83%E9%99%90)。
+
+- 对应用授予可管理权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。
+
+
+
+
+
+**注意**:
+ 在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [API 权限](https://open.feishu.cn/document/ukTMukTMukTM/uYTM5UjL2ETO14iNxkTN/scope-list)。否则你将无法在文档应用窗口搜索到目标应用。
+
+- 你也可以在 **多维表格高级权限设置** 中添加用户或一个包含应用的群组, 给予这个群自定义的读写等权限。
+
+## 5. 调用查询记录接口,复选框返回数据为空,如何解决?
+
+在多维表格中,如果字段为空值,则查询记录接口不返回数据。相应地,如果复选框字段为空值(即用户没有选中和取消选中过该复选框),则查询记录接口也不返回数据。在此场景下,由于空值效果与复选框为 `false` 效果相同,开发者需自行兼容该空值场景。
+## 6. 如何筛选自定义编号类型的自动编号字段?
+
+要筛选含有固定字符的自动编号字段,需将自定义的固定字符去除,再筛选,否则将返回空数据。如下图,自定义的固定字符为 2024,在调用查询记录接口时,需确保仅 value 的值为自增部分数字,不包含自定义的 2024。
+
+
+
+
+
+## 7. 调用查询记录接口,如何筛选人员字段?
+
+对于人员字段,你需在 value 中传入人员的用户 ID。用户 ID 的类型与查询记录接口中查询参数 `user_id_type` 指定的类型一致,默认为 Open ID 类型。以下为筛选人员 ID 为 `ou_00d9ea2d7bcd6b6aa7d71dd88deabcef` 和 `ou_5fdfc3d312b24d28224769baf52abcef` 的请求示例:
+
+```json
+{
+ "view_id": "vewfrk8iX4",
+ "field_names": [
+ "创建人"
+ ],
+ "filter": {
+ "conjunction": "and",
+ "conditions": [
+ {
+ "field_name": "创建人",
+ "operator": "contains",
+ "value": [
+ "ou_00d9ea2d7bcd6b6aa7d71dd88deabcef",
+ "ou_5fdfc3d312b24d28224769baf52abcef"
+ ]
+ }
+ ]
+ }
+}
+```
+## 8. 查询记录接口是否支持查询特定行,如数据表第 10 行~20 行的数据?
+
+1. 在多维表格中新增一个自动编号字段,编号类型选择自增数字。
+
+
+
+2. 筛选该自动编号。例如要查询第 10~20 行数据,则筛选小于 21、大于 9 的编号,然后调用[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)接口,请求体如下所示:
+
+```json
+ {
+ "view_id": "vewfrk8iX4",
+ "field_names": [],
+ "filter": {
+ "conjunction": "and",
+ "conditions": [
+ {
+ "field_name": "编号",
+ "operator": "isGreater",
+ "value": [
+ "9"
+ ]
+ },
+ {
+ "field_name": "编号",
+ "operator": "isLess",
+ "value": [
+ "21"
+ ]
+ }
+ ]
+ }
+ }
+ ```
+
+你也可以调用[批量获取记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/batch_get)接口,使用记录的 Record ID 来查询,获取多条记录的数据。
+
+## 9. 如何获取多维表格指定数据表的总记录数(或总行数)?
+
+你可以调用[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)接口,在请求体中将 `conditions` 字段设为空或不设置,以下为请求体示例:
+
+```json
+{
+ "view_id": "vewfrk8iX4", // 请替换为实际的 view_id
+ "filter": {
+ "conjunction": "and",
+ "conditions": []
+ }
+}
+```
+若请求成功,响应体中将返回 `total` 字段,即为记录总数。
\ No newline at end of file
diff --git a/多维表格概述.md b/多维表格概述.md
new file mode 100644
index 0000000..1125b1b
--- /dev/null
+++ b/多维表格概述.md
@@ -0,0 +1,272 @@
+# 多维表格概述
+多维表格(Base)是全新的业务管理工具,帮助用户重构工作应用和团队协同模式,高效在线协同数据,随心构建个性化应用,轻松掌控全盘业务数据,和团队一起创造效率的无限可能。多维表格可以是一个表格,也可以是无数个应用。它拥有强大的底层开放能力,你可以通过多维表格 API 轻松打通内部其他业务系统,让业务数据通畅流转,实时同步。
+## 典型案例
+
+开放平台提供了集成多维表格能力的客户实践案例:
+
+- [芝麻开门,在飞书五天打造门禁管理系统](https://open.feishu.cn/solutions/detail/wiseasy)
+- [飞书集成平台,让企业服务行业项目管理与销售体验升级](https://open.feishu.cn/solutions/detail/cloudwise)
+## 接入流程
+
+接入多维表格 OpenAPI 的流程如下图所示。了解更多,参考[云文档概述](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/docs-overview) 的 **接入流程** 一节。
+
+
+## 开发教程
+体验以下多维表格相关教程,了解如何运用多维表格 API 助力企业高效协作。
+
+- [快速调用一个服务端 API(以创建多维表格接口为例)](https://open.feishu.cn/document/uAjLw4CM/uMzNwEjLzcDMx4yM3ATM/call-a-server-api-base-example/introduction)
+- [快速接入多维表格](https://open.feishu.cn/document/home/quick-access-to-base/preparation)
+- [多维表格管理敏捷项目](https://open.feishu.cn/document/home/agile-project-cycle-management-based-on-bitable/introduction)
+
+## 鉴权说明
+使用 tenant_access_token 访问多维表格资源之前,请确保你的应用已经是多维表格的所有者或者协作者,否则会调用失败。
+你可通过添加文档应用的方式将应用添加为协作者,详情参考[开通文档、电子表格等其它云文档资源权限](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-add-permissions-to-app#223459af);或通过应用身份创建一篇多维表格,再使用 tenant_access_token 来调用接口。
+
+## 使用限制
+
+使用多维表格接口,整体有以下限制或说明:
+- 对于接口的批量操作,单次最高为 1,000 条记录,且响应状态是全部成功或者失败,不存在部分成功或失败的结果。
+- 为保证稳定性,建议对单一多维表格同时只请求**一次** API 写操作。
+单一多维表格中,各个资源的数量限制如下所示:
+
+| **资源** | **最大支持数量** |
+| --------- | ---------- |
+| 记录 | 不同租户的最大支持数量不同,开放平台没有额外限制。你可以在多维表格数据表 UI 中点击查看。  |
+| 字段 | 300,对于公式类型的字段,最多支持 100 个 | |
+| 视图 | 200 |
+| 数据表+仪表盘 | 100 |
+| 高级权限自定义角色 | 30 |
+| 高级权限协作者 | 200 |
+
+## 资源介绍
+
+多维表格开放了多维表格 App、数据表、视图、记录、字段、仪表盘、高级权限等多种资源的接口。本小节介绍这几类资源的含义。了解更多多维表格的概念和使用说明,可参考飞书帮助中心文档[快速上手多维表格](https://www.feishu.cn/hc/zh-CN/articles/697278684206-%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B%E5%A4%9A%E7%BB%B4%E8%A1%A8%E6%A0%BC)。
+
+
+
+### 多维表格 app
+
+一个多维表格可以理解成是一个应用(app,但不是在开发者后台创建的应用),标记该应用的唯一标识为 `app_token`。作为一个应用,多维表格有多种形态:可以作为一个独立应用,也可以作为一个模块(block)与文档、电子表格结合。
+
+#### 多维表格形态
+
+| **多维表格形态** | **资源定义** | **含义** |
+| ---------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 文件夹中的多维表格 | Base app | 存储在飞书云空间(云盘)文件夹中的多维表格。URL 以 **feishu.cn/base** 开头 |
+| 知识库下的多维表格 | Base app 和 wiki node | 放置在知识库中的多维表格。URL 以 **feishu.cn/wiki** 开头 |
+| 文档嵌入多维表格 | Base docx block | 即在"文档"中插入的多维表格,URL 以 **feishu.cn/docx** 开头 |
+| 电子表格嵌入多维表格 | Base sheet block | 在电子表格中嵌入的多维表格,URL 以 **feishu.cn/sheets** 开头 |
+
+#### 多维表格 app_token 获取方式
+
+不同形态的多维表格,其 `app_token` 的获取方式不同,具体如下所示。
+
+##### **文件夹中的多维表格**
+
+该类多维表格的 app_token 为 URL 下图高亮部分:
+
+
+
+##### **知识库下的多维表格**
+
+需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取该类多维表格的 app_token。如下返回示例,当 `obj_type` 的值为 `bitable` 时,`obj_token` 字段的值 `AW3Qbtr2cakCnesXzXVbbsrIcVT` 是多维表格的 `app_token`:
+```json
+{
+ "node":{
+ "space_id":"6946843325487912356",
+ "node_token":"wikcnKQ1k3p******8Vabcef",
+ "obj_token":"AW3Qbtr2cakCnesXzXVbbsrIcVT", // 多维表格的 app_token
+ "obj_type":"bitable",
+ "parent_node_token":"wikcnKQ1k3p******8Vabcef",
+ "node_type":"origin",
+ "origin_node_token":"wikcnKQ1k3p******8Vabcef",
+ "origin_space_id":"6946843325487912356",
+ "has_child":false,
+ "title":"标题",
+ "obj_create_time":"1642402428",
+ "obj_edit_time":"1642402428",
+ "node_create_time":"1642402428",
+ "creator":"ou_xxxxx",
+ "owner":"ou_xxxxx"
+ }
+}
+```
+
+##### **文档嵌入多维表格**
+
+文档中嵌入的多维表格,需要调用文档相关接口获取多维表格的 `app_token`。
+调用[获取文档所有块](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/document-docx/docx-v1/document-block/list),在返回结果中检索,其中 `bitable.token` 字段的值 `AW3Qbtr2cakCnesXzXVbbsrIcVT_tblkIYhz52o6G5nx`是用 `_` 隔开的 `app_token` 和 `table_id`;
+```json
+{
+ "bitable": {
+ "token": "AW3Qbtr2cakCnesXzXVbbsrIcVT_tblkIYhz52o6G5nx"
+ },
+ "block_id": "Mgeadqo4CoeoOGxI7Lgb4GNicEd",
+ "block_type": 18,
+ "parent_id": "YUqpdO2eLo7xJdxy5RQbuQBdctf"
+}
+```
+
+##### **电子表格嵌入多维表格**
+
+电子表格中嵌入的多维表格,需要调用电子表格相关接口获取多维表格的 `app_token`。
+若电子表格中嵌有多维表格,需调用[获取表格元数据](https://open.feishu.cn/document/ukTMukTMukTM/uETMzUjLxEzM14SMxMTN),在返回结果中将返回 `blockInfo`。其中,当 `blockType` 的值为 `BITABLE_BLOCK` 时,blockToken 字段的值`AW3Qbtr2cakCnesXzXVbbsrIcVT_tblkIYhz52o6G5nx` 是用 `_` 隔开的 `app_token` 和 `table_id`。
+```json
+{
+ "blockInfo": {
+ "blockToken": "AW3Qbtr2cakCnesXzXVbbsrIcVT_tblkIYhz52o6G5nx",
+ "blockType": "BITABLE_BLOCK"
+ },
+ "columnCount": 0,
+ "frozenColCount": 0,
+ "frozenRowCount": 0,
+ "index": 0,
+ "rowCount": 0,
+ "sheetId": "***",
+ "title": "*** "
+}
+```
+
+### 数据表 table
+
+多维表格的数据容器,一个多维表格中至少有一个数据表(table),也可能有多个数据表。每个数据表都有唯一标识 `table_id`。`table_id` 在一个多维表格 App 中唯一,在全局不一定唯一。
+你可通过多维表格 URL 获取 `table_id`,下图高亮部分即为当前数据表的唯一标识。你也可通过[列出数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list)接口获取 `table_id`。
+
+
+
+### 视图 view
+
+指数据的汇总和展现形式。视图有多种类型,包括表格视图、看板视图、画册视图、甘特视图和表单视图等,可参考飞书帮助中心文档[视图类型](https://www.feishu.cn/hc/zh-CN/articles/360049067931-%E4%BD%BF%E7%94%A8%E5%A4%9A%E7%BB%B4%E8%A1%A8%E6%A0%BC%E8%A7%86%E5%9B%BE#tabs0|lineguid-6kjqG)。一个数据表至少有一个视图,可能有多个视图。每个视图都有唯一标识 `view_id`,`view_id` 在一个多维表格中唯一,在全局不一定唯一。
+你可通过多维表格 URL 获取 `view_id`,下图高亮部分即为当前视图的唯一标识。你也可通过[列出视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/list)接口获取 `view_id`。暂时无法获取到嵌入到文档中的多维表格的 `view_id`。
+
+
+
+#### **表单视图 form**
+
+表单视图是多维表格的一种视图类型,形式类似于问卷,可以用来收集信息和数据。每个表单都有唯一标识 `form_id`,即当前视图的 `view_id`。`form_id` 的获取方式和 `view_id` 的获取方式相同。
+
+
+
+### 记录 record
+
+数据表中的每一行数据都是一条记录(record)。每条记录都有唯一标识 `record_id`,`record_id` 在一个多维表格中唯一,在全局不一定唯一。`record_id` 需要通过[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)接口获取。
+
+
+
+### 字段 field
+
+即多维表格的“列”,多维表格提供丰富的字段类型。每个字段都有唯一标识 `field_id`,`field_id` 在一个多维表格内唯一,在全局不一定唯一。`field_id` 需要通过[列出字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/list)接口获取。了解更多字段说明,参考[字段编辑指南](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/guide)。
+
+
+
+### 仪表盘 block
+
+仪表盘与数据看板类似,可以从不同的维度统计对多维表格中的数据进行统计。了解更多,参考飞书帮助中心文档[使用多维表格仪表盘](https://www.feishu.cn/hc/zh-CN/articles/161059314076-%E4%BD%BF%E7%94%A8%E5%A4%9A%E7%BB%B4%E8%A1%A8%E6%A0%BC%E4%BB%AA%E8%A1%A8%E7%9B%98)。
+
+
+
+仪表盘的唯一标识为 `block_id`,以 `blk` 开头,你可通过多维表格 URL 获取 `block_id`,下图高亮部分即为当前仪表盘的唯一标识,也可通过[列出仪表盘](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-dashboard/list)接口获取。
+
+
+
+### 高级权限
+
+高级权限允许用户针对单一数据表设置哪些用户可以查看、编辑指定的行,或是设置针对某用户可以编辑的列。高级权限接口分为 **自定义角色** 和 **协作者** 两部分,多维表格的 **所有者** 或者 **有可管理权限** 的用户可通过接口设置高级权限,管理高级权限的协作者。了解更多,参考[高级权限概述](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role/advanced-permission-guide)。
+
+
+
+#### **自定义角色 role**
+
+在高级权限中添加角色并设置权限,该角色即为自定义角色。每个自定义角色都有唯一标识 `role_id`。`role_id` 需要通过[列出自定义角色](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role/list)接口获取。
+
+
+
+#### **协作者 member**
+
+高级权限设置中,一个自定义角色(role)中的成员,即为协作者(member)。每个协作者都有唯一标识 `member_id`。`member_id` 需要通过[列出协作者](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role-member/list)接口获取。
+
+### 自动化流程 workflows
+
+自动化流程是用户给多维表格设定的自动运行规则。设定“触发条件”和“执行操作”以后,多维表格会根据数据变更,自动执行下一步操作。你可通过[列出自动化流程](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-workflow/list)获取自动化流程的 ID,即 `workflow_id`。
+
+## 方法列表
+以下为多维表格的方法列表。其中,“商店”代表[商店应用](https://open.feishu.cn/document/home/app-types-introduction/overview);“自建”代表[企业自建应用](https://open.feishu.cn/document/home/app-types-introduction/overview),了解更多应用相关信息,参考[应用类型简介](https://open.feishu.cn/document/home/app-types-introduction/overview)。了解调用服务端 API 的流程,参考[流程概述](https://open.feishu.cn/document/uMzNwEjLzcDMx4yM3ATM/ugzNwEjL4cDMx4CO3ATM)。
+
+### 多维表格 app
+
+**[方法 (API)](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)** | 权限要求(满足任一) | **[访问凭证](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)(选择其一)** | 商店 | 自建
+---|---|---|---|---
+[创建多维表格](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app/create)
`POST` /open-apis/bitable/v1/apps | 创建多维表格(base:app:create)
查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[复制多维表格](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app/copy)
`POST` /open-apis/bitable/v1/apps/:app_token/copy | 复制多维表格(base:app:copy)
查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[获取多维表格元数据](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app/get)
`GET` /open-apis/bitable/v1/apps/:app_token | 查看、评论和导出多维表格(bitable:app:readonly)
查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[更新多维表格元数据](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app/update)
`PUT` /open-apis/bitable/v1/apps/:app_token | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+
+### 数据表 table
+
+**[方法 (API)](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)** | 权限要求(满足任一) | **[访问凭证](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)(选择其一)** | 商店 | 自建
+---|---|---|---|---
+[列出数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list)
`GET` /open-apis/bitable/v1/apps/:app_token/tables | 查看、评论和导出多维表格(bitable:app:readonly)
查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[新增数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/create)
`POST` /open-apis/bitable/v1/apps/:app_token/tables | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[新增多个数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/batch_create)
`POST` /open-apis/bitable/v1/apps/:app_token/tables/batch_create | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[删除数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/delete)
`DELETE` /open-apis/bitable/v1/apps/:app_token/tables/:table_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[删除多个数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/batch_delete)
`POST` /open-apis/bitable/v1/apps/:app_token/tables/batch_delete | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+
+### 视图 view
+
+**[方法 (API)](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)** | 权限要求(满足任一) | **[访问凭证](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)(选择其一)** | 商店 | 自建
+---|---|---|---|---
+[列出视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/list)
`GET` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/views | 查看、评论和导出多维表格(bitable:app:readonly)
查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[新增视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/create)
`POST` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/views | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[更新视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/patch)
`PATCH` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/views/:view_id | 查看、评论和导出多维表格(bitable:app:readonly)
查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[检索视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/get)
`GET` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/views/:view_id | 检索视图(base:view:read)
查看、评论、编辑和管理多维表格(bitable:app)
查看、评论和导出多维表格(bitable:app:readonly) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[删除视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/delete)
`DELETE` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/views/:view_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+
+### 记录 record
+
+**[方法 (API)](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)** | 权限要求(满足任一) | **[访问凭证](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)(选择其一)** | 商店 | 自建
+---|---|---|---|---
+[列出记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/list)
`GET` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/records | 查看、评论和导出多维表格(bitable:app:readonly)
查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[检索记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/get)
`GET` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/:record_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[新增记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/create)
`POST` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/records | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[新增多条记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/batch_create)
`POST` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_create | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[更新记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/update)
`PUT` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/:record_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[更新多条记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/batch_update)
`POST` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_update | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[删除记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/delete)
`DELETE` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/:record_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[删除多条记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/batch_delete)
`POST` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_delete | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+
+### 字段 field
+
+**[方法 (API)](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)** | 权限要求(满足任一) | **[访问凭证](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)(选择其一)** | 商店 | 自建
+---|---|---|---|---
+[列出字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/list)
`GET` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/fields | 查看、评论和导出多维表格(bitable:app:readonly)
查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[新增字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/create)
`POST` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/fields | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[更新字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/update)
`PUT` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/fields/:field_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[删除字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/delete)
`DELETE` /open-apis/bitable/v1/apps/:app_token/tables/:table_id/fields/:field_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+
+### 仪表盘 dashboard
+
+**[方法 (API)](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)** | 权限要求(满足任一) | **[访问凭证](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)(选择其一)** | 商店 | 自建
+---|---|---|---|---
+[复制仪表盘](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-dashboard/copy)
`POST` /open-apis/bitable/v1/apps/:app_token/dashboards/:block_id/copy | 复制仪表盘(base:dashboard:copy)
查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[列出仪表盘](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-dashboard/list)
`GET` /open-apis/bitable/v1/apps/:app_token/dashboards | 获取仪表盘信息(base:dashboard:read)
查看、评论、编辑和管理多维表格(bitable:app)
查看、评论和导出多维表格(bitable:app:readonly) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+
+### 自定义角色 role
+
+**[方法 (API)](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)** | 权限要求(满足任一) | **[访问凭证](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)(选择其一)** | 商店 | 自建
+---|---|---|---|---
+[列出自定义权限](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role/list)
`GET` /open-apis/bitable/v1/apps/:app_token/roles | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[新增自定义权限](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role/create)
`POST` /open-apis/bitable/v1/apps/:app_token/roles/:role_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[更新自定义权限](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role/update)
`PUT` /open-apis/bitable/v1/apps/:app_token/roles/:role_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[删除自定义权限](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role/delete)
`DELETE` /open-apis/bitable/v1/apps/:app_token/roles/:role_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+
+### 协作者 member
+
+高级权限下的协作者。
+
+**[方法 (API)](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)** | 权限要求(满足任一) | **[访问凭证](https://open.feishu.cn/document/ukTMukTMukTM/uMTNz4yM1MjLzUzM)(选择其一)** | 商店 | 自建
+---|---|---|---|---
+[列出协作者](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role-member/list)
`GET` /open-apis/bitable/v1/apps/:app_token/roles/:role_id/members | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[新增协作者](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role-member/create)
`POST` /open-apis/bitable/v1/apps/:app_token/roles/:role_id/members | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[删除协作者](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role-member/delete)
`DELETE` /open-apis/bitable/v1/apps/:app_token/roles/:role_id/members/:member_id | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[批量新增协作者](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role-member/batch_create)
`POST` /open-apis/bitable/v1/apps/:app_token/roles/:role_id/members/batch_create | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
+[批量删除协作者](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role-member/batch_delete)
`DELETE` /open-apis/bitable/v1/apps/:app_token/roles/:role_id/members/batch_delete | 查看、评论、编辑和管理多维表格(bitable:app) | `tenant_access_token`
`user_access_token` | **✓** | **✓**
diff --git a/批量获取记录.md b/批量获取记录.md
new file mode 100644
index 0000000..829efbf
--- /dev/null
+++ b/批量获取记录.md
@@ -0,0 +1,296 @@
+# 批量获取记录
+
+通过多个记录 ID 查询记录信息。该接口最多支持查询 100 条记录。
+
+## 注意事项
+
+若多维表格开启了高级权限,你需确保调用身份拥有多维表格的可管理权限,否则可能出现调用成功但返回数据为空的情况。了解具体步骤,参考[如何为应用或用户开通文档权限](https://open.feishu.cn/document/ukTMukTMukTM/uczNzUjL3czM14yN3MTN#16c6475a)。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_get
+HTTP Method | POST
+接口频率限制 | [20 次/秒](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 检索特定记录(base:record:read)
查看、评论、编辑和管理多维表格(bitable:app)
查看、评论和导出多维表格(bitable:app:readonly)
+字段权限要求 | **注意事项**:该接口返回体中存在下列敏感字段,仅当开启对应的权限后才会返回;如果无需获取这些字段,则不建议申请
获取用户基本信息(contact:user.base:readonly)
以应用身份访问通讯录(contact:contact:access_as_app)
读取通讯录(contact:contact:readonly)
以应用身份读取通讯录(contact:contact:readonly_as_app)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+Content-Type | string | 是 | **固定值**:"application/json; charset=utf-8"
+
+### 路径参数
+
+名称 | 类型 | 描述
+---|---|---
+app_token | string | 多维表格 App 的唯一标识。不同形态的多维表格,其 `app_token` 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 `app_token` 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 `obj_type` 的值为 `bitable` 时,`obj_token` 字段的值才是多维表格的 `app_token`。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
**示例值**:"NQRxbRkBMa6OnZsjtERcxhabcef"
**数据校验规则**:
- 长度范围:`0` ~ `100` 字符
+table_id | string | 多维表格数据表的唯一标识。获取方式:
- 你可通过多维表格 URL 获取 `table_id`,下图高亮部分即为当前数据表的 `table_id`
- 也可通过[列出数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list)接口获取 `table_id`

**示例值**:"tbl0xe5g8PPabcef"
**数据校验规则**:
- 长度范围:`0` ~ `50` 字符
+
+### 请求体
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+record_ids | string\[\] | 是 | 记录 ID 列表。调用[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)获取。
**示例值**:["recyO3299N"]
**数据校验规则**:
- 长度范围:`1` ~ `100`
+user_id_type | string | 否 | 此次调用中使用的用户 id 的类型
**示例值**:"open_id"
**可选值有**:
- user_id:以user_id来识别用户
- union_id:以union_id来识别用户
- open_id:以open_id来识别用户
+with_shared_url | boolean | 否 | 是否返回记录的分享链接。可选值:
- true:返回分享链接
- false:不返回分享链接
**默认值**:false
**示例值**:true
+automatic_fields | boolean | 否 | 是否返回自动计算的字段。可选值:
- true:返回自动计算的字段
- false:不返回自动计算的字段
**默认值**:false
**示例值**:true
+
+### 请求体示例
+```json
+{
+ "record_ids": [
+ "recyOaMB2F",
+ "rec111111",
+ "recyOaMB2F"
+ ],
+ "user_id_type": "open_id",
+ "with_shared_url": true,
+ "automatic_fields": true
+}
+```
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+records | app.table.record\[\] | 记录列表
+fields | map<string, union> | 记录字段
+record_id | string | 记录 ID
+created_by | person | 创建人信息
+id | string | 创建人 ID。ID 类型与请求体中的 `user_id_type` 指定的类型一致。
+name | string | 创建人中文姓名
+en_name | string | 创建人英文姓名
+email | string | 创建人邮箱
+avatar_url | string | 创建人头像链接
**字段权限要求(满足任一)**:
获取用户基本信息(contact:user.base:readonly)
以应用身份访问通讯录(contact:contact:access_as_app)
读取通讯录(contact:contact:readonly)
以应用身份读取通讯录(contact:contact:readonly_as_app)
+created_time | int | 创建时间。毫秒级时间戳。
+last_modified_by | person | 修改人信息
+id | string | 修改人 ID。ID 类型与请求体中的 `user_id_type` 指定的类型一致。
+name | string | 修改人中文姓名
+en_name | string | 修改人英文姓名
+email | string | 修改人邮箱
+avatar_url | string | 修改人头像链接
**字段权限要求(满足任一)**:
获取用户基本信息(contact:user.base:readonly)
以应用身份访问通讯录(contact:contact:access_as_app)
读取通讯录(contact:contact:readonly)
以应用身份读取通讯录(contact:contact:readonly_as_app)
+last_modified_time | int | 最近更新时间。毫秒级时间戳。
+shared_url | string | 记录分享链接
+record_url | string | 记录链接(检索记录接口将返回该字段,本接口不返回)
+forbidden_record_ids | string\[\] | 禁止访问的记录列表(针对开启了高级权限的多维表格)
+absent_record_ids | string\[\] | 不存在的记录列表
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "forbidden_record_ids": [
+ "recyOaMB2F"
+ ],
+ "absent_record_ids": [
+ "rec111111"
+ ],
+ "records": [
+ {
+ "created_by": {
+ "avatar_url": "https://internal-api-lark-file.feishu.cn/static-resource/v1/06d568cb-f464-4c2e-bd03-76512c545c5j~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp",
+ "email": "",
+ "en_name": "Min Zhang",
+ "id": "ou_92945f86a98bba075174776959c90eda",
+ "name": "张敏"
+ },
+ "created_time": 1691049973000,
+ "fields": {
+ "人员": [
+ {
+ "avatar_url": "https://internal-api-lark-file.feishu.cn/static-resource/v1/b2-7619-4b8a-b27b-c72d90b06a2j~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp",
+ "email": "minzhang.leben@bytedance.com",
+ "en_name": "Min Zhang",
+ "id": "ou_2910013f1e6456f16a0ce75ede950a0a",
+ "name": "张敏"
+ },
+ {
+ "avatar_url": "https://internal-api-lark-file.feishu.cn/static-resource/v1/v2_q86-fcb6-4f18-85c7-87ca8881e50j~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp",
+ "email": "minzhang.00@bytedance.com",
+ "en_name": "Min Zhang",
+ "id": "ou_e04138c9633dd0d2ea166d79f548ab5d",
+ "name": "张敏"
+ }
+ ],
+ "修改人": [
+ {
+ "avatar_url": "https://internal-api-lark-file.feishu.cn/static-resource/v1/06d568cb-f464-4c2e-bd03-76512c545c5j~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp",
+ "email": "",
+ "en_name": "Min Zhang",
+ "id": "ou_92945f86a98bba075174776959c90eda",
+ "name": "张敏"
+ }
+ ],
+ "创建人": [
+ {
+ "avatar_url": "https://internal-api-lark-file.feishu.cn/static-resource/v1/06d568cb-f464-4c2e-bd03-76512c545c5j~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp",
+ "email": "",
+ "en_name": "Min Zhang",
+ "id": "ou_92945f86a98bba075174776959c90eda",
+ "name": "张敏"
+ }
+ ],
+ "创建时间": 1691049973000,
+ "单向关联": {
+ "link_record_ids": [
+ "recnVYsuqV"
+ ]
+ },
+ "单选": "选项1",
+ "双向关联": {
+ "link_record_ids": [
+ "recqLvMaXT",
+ "recrdld32q"
+ ]
+ },
+ "地理位置": {
+ "address": "东长安街",
+ "adname": "东城区",
+ "cityname": "北京市",
+ "full_address": "天安门广场,北京市东城区东长安街",
+ "location": "116.397755,39.903179",
+ "name": "天安门广场",
+ "pname": "北京市"
+ },
+ "复选框": true,
+ "多行文本": [
+ {
+ "text": "多行文本内容1",
+ "type": "text"
+ },
+ {
+ "mentionNotify": false,
+ "mentionType": "User",
+ "name": "张敏",
+ "text": "@张敏",
+ "token": "ou_2910013f1e6456f16a0ce75ede950a0a",
+ "type": "mention"
+ }
+ ],
+ "多选": [
+ "选项1",
+ "选项2"
+ ],
+ "数字": 2323.2323,
+ "日期": 1690992000000,
+ "最后更新时间": 1702455191000,
+ "条码": [
+ {
+ "text": "123",
+ "type": "text"
+ }
+ ],
+ "电话号码": "131xxxx6666",
+ "自动编号": "17",
+ "群组": [
+ {
+ "avatar_url": "https://internal-api-lark-file.feishu-boe.cn/static-resource/v1/v2_c8d2cd50-ba29-476f-b7f1-5b5917cb18ej~?image_size=72x72&cut_type=&quality=&format=jpeg&sticker_format=.webp",
+ "id": "oc_cd07f55f14d6f4a4f1b51504e7e97f48",
+ "name": "武侠聊天组"
+ }
+ ],
+ "评分": 3,
+ "货币": 1,
+ "超链接": {
+ "link": "https://bitable.feishu.cn",
+ "text": "飞书多维表格官网"
+ },
+ "进度": 0.66,
+ "附件": [
+ {
+ "file_token": "Vl3FbVkvnowlgpxpqsAbBrtFcrd",
+ "name": "飞书.jpeg",
+ "size": 32975,
+ "tmp_url": "https://open.feishu.cn/open-apis/drive/v1/medias/batch_get_tmp_download_url?file_tokens=Vl3FbVk11owlgpxpqsAbBrtFcrd&extra=%7B%22bitablePerm%22%3A%7B%22tableId%22%3A%22tblBJyX6jZteblYv%22%2C%22rev%22%3A90%7D%7D",
+ "type": "image/jpeg",
+ "url": "https://open.feishu.cn/open-apis/drive/v1/medias/Vl3FbVk11owlgpxpqsAbBrtFcrd/download?extra=%7B%22bitablePerm%22%3A%7B%22tableId%22%3A%22tblBJyX6jZteblYv%22%2C%22rev%22%3A90%7D%7D"
+ }
+ ]
+ },
+ "last_modified_by": {
+ "avatar_url": "https://internal-api-lark-file.feishu.cn/static-resource/v1/06d568cb-f464-4c2e-bd03-76512c545c5j~?image_size=72x72&cut_type=default-face&quality=&format=jpeg&sticker_format=.webp",
+ "email": "",
+ "en_name": "Min Zhang",
+ "id": "ou_92945f86a98bba075174776959c90eda",
+ "name": "张敏"
+ },
+ "last_modified_time": 1702455191000,
+ "record_id": "recyOaMB2F",
+ "shared_url": "https://example.feishu.cn/record/KBcNrNtpWePAlscCvdmb6ZcSc5b"
+ }
+ ]
+ }
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+400 | 1254000 | WrongRequestJson | 请求体错误。请检查请求参数
+400 | 1254001 | WrongRequestBody | 请求体错误。请检查请求参数
+500 | 1254002 | Fail | 导致报 1254002 错误码的场景较多,请参考以下建议排查:
- 如果单次操作的内容变更较大,请尝试在单次操作中减少数据量
- 如果你并发调用了接口,请尝试控制请求间隔,稍后重试
- 如果在知识库(wiki)中创建多维表格,请检查你是否使用了知识库[创建知识空间节点](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space-node/create)接口创建多维表格。在此场景下不能使用[创建多维表格](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app/create)接口
- 请检查接口参数是否有误。例如,在分页查询多维表格时,传递了无效的 page_token,或传递了错误的数据表的 table_id
- 如果该报错偶尔发生,可能是服务器超时或不稳定,请重试解决
+400 | 1254003 | WrongBaseToken | app_token 错误。app_token 是多维表格 App 的唯一标识。不同形态的多维表格,其 `app_token` 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 `app_token` 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 `obj_type` 的值为 `bitable` 时,`obj_token` 字段的值才是多维表格的 `app_token`。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
+400 | 1254004 | WrongTableId | table_id 错误。table_id 是多维表格数据表的唯一标识。获取方式:
- 你可通过多维表格 URL 获取 `table_id`,下图高亮部分即为当前数据表的 `table_id`
- 也可通过[列出数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list)接口获取 `table_id`

+400 | 1254005 | WrongViewId | view_id 错误。view_id 是多维表格中视图的唯一标识。获取方式:
- 在多维表格的 URL 地址栏中,`view_id` 是下图中高亮部分:

- 通过[列出视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/list)接口获取。暂时无法获取到嵌入到云文档中的多维表格的 `view_id`。
**注意**:
当 `filter` 参数 或 `sort` 参数不为空时,请求视为对数据表中的全部数据做条件过滤,指定的 `view_id` 会被忽略。
+400 | 1254006 | WrongRecordId | record_id 错误。record_id 是数据表中一条记录的唯一标识。通过[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)接口获取
+400 | 1254007 | EmptyValue | 空值
+400 | 1254008 | EmptyView | 空视图
+400 | 1254009 | WrongFieldId | field_id 错误。field_id 是数据表中一个字段的唯一标识。通过[列出字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/list)接口获取
+400 | 1254010 | ReqConvError | 请求错误
+400 | 1254011 | Page size must greater than 0. | page_size参数非法
+400 | 1254016 | InvalidSort | Sort参数错误
+400 | 1254018 | InvalidFilter | filter 参数错误。请参考[记录过滤参数填写指南](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/record-filter-guide)了解如何填写 filter 参数。
+400 | 1254024 | InvalidFieldNames | field_names 参数错误。请检查接口中字段名称和多维表格中的字段名称是否完全匹配。如果难以排查,建议你调用[列出字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/list)接口获取字段名称,因为根据表格页面的 UI 名称可能会忽略空格、换行或特殊符号等差异
+400 | 1254030 | InvalidPageToken | 分页游标错误
+400 | 1254036 | Bitable is copying, please try again later. | 复制多维表格为异步操作,该错误码表示当前多维表格仍在复制中,在复制期间无法操作当前多维表格。需要等待复制完成后再操作
+404 | 1254040 | BaseTokenNotFound | app_token 不存在。不同形态的多维表格,其 `app_token` 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 `app_token` 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 `obj_type` 的值为 `bitable` 时,`obj_token` 字段的值才是多维表格的 `app_token`。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
+404 | 1254041 | TableIdNotFound | table_id 不存在。获取方式:
- 你可通过多维表格 URL 获取 `table_id`,下图高亮部分即为当前数据表的 `table_id`
- 也可通过[列出数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list)接口获取 `table_id`

+404 | 1254042 | ViewIdNotFound | view_id 不存在。获取方式:
- 在多维表格的 URL 地址栏中,`view_id` 是下图中高亮部分:

- 通过[列出视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/list)接口获取。暂时无法获取到嵌入到云文档中的多维表格的 `view_id`。
**注意**:
当 `filter` 参数 或 `sort` 参数不为空时,请求视为对数据表中的全部数据做条件过滤,指定的 `view_id` 会被忽略。
+404 | 1254043 | RecordIdNotFound | record_id 不存在。record_id 是数据表中一条记录的唯一标识。请通过[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)接口获取。
+404 | 1254044 | FieldIdNotFound | field_id 不存在。field_id 是数据表中一个字段的唯一标识。通过[列出字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/list)接口获取。
+404 | 1254045 | FieldNameNotFound | 字段名称不存在。请检查:
- 接口中字段名称和多维表格中的字段名称是否完全匹配。如果难以排查,建议你调用[列出字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/list)接口获取字段名称,因为根据表格页面的 UI 名称可能会忽略空格、换行或特殊符号等差异。
- 表格是否开启了高级权限但调用身份缺少对应字段的权限。你需要为调用身份授予高级权限:
- 对用户授予高级权限,你需要在多维表格页面右上方 **分享** 入口为当前用户添加可管理权限。
- 对应用授予高级权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。


**注意**:
在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [API 权限](https://open.feishu.cn/document/ukTMukTMukTM/uYTM5UjL2ETO14iNxkTN/scope-list)。否则你将无法在文档应用窗口搜索到目标应用。
- 你也可以在 **多维表格高级权限设置** 中添加用户或一个包含应用的群组, 给予这个群自定义的读写等权限。
+500 | 1254060 | TextFieldConvFail | 多行文本字段错误
+500 | 1254061 | NumberFieldConvFail | 数字字段错误
+500 | 1254062 | SingleSelectFieldConvFail | 单选字段错误
+500 | 1254063 | MultiSelectFieldConvFail | 多选字段错误
+500 | 1254064 | DatetimeFieldConvFail | 日期字段错误
+500 | 1254065 | CheckboxFieldConvFail | 复选框字段错误
+500 | 1254066 | UserFieldConvFail | 人员字段有误。原因可能是:
- `user_id_type` 参数指定的 ID 类型与传入的 ID 类型不匹配
- 传入了不识别的类型或结构,目前只支持填写 `id` 参数,且需要传入数组
- 跨应用传入了 `open_id`。如果跨应用传入 ID,建议使用 `user_id`。不同应用获取的 `open_id` 不能交叉使用
- 若想对人员字段传空,可传 null
+500 | 1254067 | LinkFieldConvFail | 关联字段错误
+500 | 1254068 | URLFieldConvFail | 超链接字段错误
+500 | 1254069 | AttachFieldConvFail | 附件字段错误
+400 | 1254072 | Failed to convert phone field, please make sure it is correct. | 电话字段错误
+400 | 1254100 | TableExceedLimit | 数据表或仪表盘数量超限。每个多维表格中,数据表加仪表盘的数量最多为 100 个
+400 | 1254101 | ViewExceedLimit | 视图数量超限, 限制 200 个
+400 | 1254102 | FileExceedLimit | 文件数量超限
+400 | 1254103 | RecordExceedLimit | 记录数量超限, 限制 20,000 条
+400 | 1254104 | RecordAddOnceExceedLimit | 单次添加记录数量超限, 单次调用最多更新 1,000 条记录
+400 | 1254107 | FilterLengthExceedLimit | Filter长度超限, 限制 2,000 个字符
+400 | 1254108 | SortLengthExceedLimit | Sort 长度超限, 限制 1,000 个字符
+400 | 1254109 | FormulaTableSizeExceedLimit | 公式表大小超限
+400 | 1254130 | TooLargeCell | 格子内容过大
+400 | 1254290 | TooManyRequest | 请求过快,稍后重试
+400 | 1254291 | LockNotObtainedError | 在同一个数据表中,并发调用了读写接口或请求过快,出现冲突。请参考以下建议解决:
- 确保没有并发调用多维表格读写相关接口
- 若操作量较大,建议在接口与接口之间增加 0.5 或 1 秒的延迟,也可在报错中增加重试逻辑,确保业务的稳定性
- 对于写接口,可以将接口中的查询参数 `ignore_consistency_check` 设置为 true,表示在读写操作时,暂时忽略一致性检查,以提高性能
+400 | 1254301 | OperationTypeError | 多维表格未开启高级权限或不支持开启高级权限
+400 | 1254302 | RolePermNotAllow | 无访问权限,常由表格开启了高级权限造成。请确保当前调用身份具有高级权限或多维表格的管理权限:
- 对于应用身份,你需要通过云文档网页页面右上方 「**...**」->「**...更多**」-> 「**添加文档应用**」入口添加并授予应用可管理权限,或在高级权限设置中添加一个包含应用的群组,给予这个群读写权限
- 对于用户身份,你需要通过云文档网页的「**分享**」入口授予用户管理权限
了解更多,参考[如何为应用或用户开通云文档权限](https://open.feishu.cn/document/ukTMukTMukTM/uczNzUjL3czM14yN3MTN#16c6475a)。
+400 | 1254303 | AttachPermNotAllow | 附件无权限
+500 | 1255001 | InternalError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+500 | 1255002 | RpcError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+500 | 1255003 | MarshalError | 序列化错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+500 | 1255004 | UmMarshalError | 反序列化错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+500 | 1255005 | ConvError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+504 | 1255040 | Request timed out, please try again later | 请求超时
+500 | 1254607 | Data not ready, please try again later | 该报错一般是由于前置操作未执行完成,或本次操作数据太大,服务器计算超时导致。遇到该错误码时,建议等待一段时间后重试。通常有以下几种原因:
- **编辑操作频繁**:开发者对多维表格的编辑操作非常频繁。可能会导致由于等待前置操作处理完成耗时过长而超时的情况。多维表格底层对数据表的处理基于版本维度的串行方式,不支持并发。因此,并发请求时容易出现此类错误,不建议开发者对单个数据表进行并发请求。
- **批量操作负载重**:开发者在多维表格中进行批量新增、删除等操作时,如果数据表的数据量非常大,可能会导致单次请求耗时过长,最终导致请求超时。建议开发者适当降低批量请求的 page_size 以减少请求耗时。
- **资源分配与计算开销**:资源分配是基于单文档维度的,如果读接口涉及公式计算、排序等计算逻辑,会占用较多资源。例如,并发读取一个文档下的多个数据表也可能导致该文档阻塞。
diff --git a/批量获取评论.md b/批量获取评论.md
new file mode 100644
index 0000000..f9b816e
--- /dev/null
+++ b/批量获取评论.md
@@ -0,0 +1,159 @@
+# 批量获取评论
+
+该接口用于根据评论 ID 列表批量获取云文档评论信息,包括评论和回复 ID、回复的内容、评论人和回复人的用户 ID 等。支持返回全局评论以及局部评论(可通过 is_whole 字段区分)。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/drive/v1/files/:file_token/comments/batch_query
+HTTP Method | POST
+接口频率限制 | [100 次/分钟](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 获取云文档中的评论(docs:document.comment:read)
查看、评论、编辑和管理云空间中所有文件(drive:drive)
查看、评论和下载云空间中所有文件(drive:drive:readonly)
+字段权限要求 | **注意事项**:该接口返回体中存在下列敏感字段,仅当开启对应的权限后才会返回;如果无需获取这些字段,则不建议申请
获取用户 user ID(contact:user.employee_id:readonly)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+Content-Type | string | 是 | **固定值**:"application/json; charset=utf-8"
+
+### 路径参数
+
+名称 | 类型 | 描述
+---|---|---
+file_token | string | 文档 Token
**示例值**:"doxbcdl03Vsxhm7Qmnj110abcef"
+
+### 查询参数
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+file_type | string | 是 | 云文档类型
**示例值**:docx
**可选值有**:
- doc:旧版文档类型,已不推荐使用
- docx:新版文档类型
- sheet:电子表格类型
- file:文件类型
- slides:幻灯片
+user_id_type | string | 否 | 用户 ID 类型
**示例值**:open_id
**可选值有**:
- open_id:标识一个用户在某个应用中的身份。同一个用户在不同应用中的 Open ID 不同。[了解更多:如何获取 Open ID](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-obtain-openid)
- union_id:标识一个用户在某个应用开发商下的身份。同一用户在同一开发商下的应用中的 Union ID 是相同的,在不同开发商下的应用中的 Union ID 是不同的。通过 Union ID,应用开发商可以把同个用户在多个应用中的身份关联起来。[了解更多:如何获取 Union ID?](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-obtain-union-id)
- user_id:标识一个用户在某个租户内的身份。同一个用户在租户 A 和租户 B 内的 User ID 是不同的。在同一个租户内,一个用户的 User ID 在所有应用(包括商店应用)中都保持一致。User ID 主要用于在不同的应用间打通用户数据。[了解更多:如何获取 User ID?](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-obtain-user-id)
**默认值**:`open_id`
**当值为 `user_id`,字段权限要求**:
获取用户 user ID(contact:user.employee_id:readonly)
+
+### 请求体
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+comment_ids | string\[\] | 是 | 需要获取数据的评论 ID ,可通过调用获取云文档所有评论接口获取 comment_id
**示例值**:["1654857036541812356"]
+
+### 请求体示例
+```json
+{
+ "comment_ids": [
+ "1654857036541812356"
+ ]
+}
+```
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+items | file.comment\[\] | 评论的相关信息、回复的信息、回复分页的信息
+comment_id | string | 评论 ID
+user_id | string | 用户 ID
+create_time | int | 创建时间
+update_time | int | 更新时间
+is_solved | boolean | 是否已解决
+solved_time | int | 解决评论时间
+solver_user_id | string | 解决评论者的用户 ID
+has_more | boolean | 是否有更多回复
+page_token | string | 回复分页标记
+is_whole | boolean | 是否是全文评论
+quote | string | 局部评论的引用字段
+reply_list | reply_list | 评论里的回复列表
+replies | file.comment.reply\[\] | 回复列表
+content | reply_content | 回复内容
+elements | reply_element\[\] | 回复的内容
+type | string | 回复的内容元素
**可选值有**:
- text_run:普通文本
- docs_link:at 云文档链接
- person:at 联系人
+text_run | text_run | 文本内容
+text | string | 回复 普通文本
+docs_link | docs_link | 添加云文档链接
+url | string | 回复 at 云文档
+person | person | 添加用户的 user_id
+user_id | string | 添加用户的 user_id 以@用户
+reply_id | string | 回复 ID
+user_id | string | 用户 ID
+create_time | int | 创建时间
+update_time | int | 更新时间
+extra | reply_extra | 回复的其他内容,图片 Token 等
+image_list | string\[\] | 评论中的图片 Token list
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "items": [
+ {
+ "comment_id": "6916106822734512356",
+ "user_id": "ou_cc19b2bfb93f8a44db4b4d6eababcef",
+ "create_time": 1610281603,
+ "update_time": 1610281603,
+ "is_solved": false,
+ "solved_time": 1610281603,
+ "solver_user_id": "null",
+ "has_more": false,
+ "page_token": "6916106822734512356",
+ "is_whole": true,
+ "quote": "划词评论引用内容",
+ "reply_list": {
+ "replies": [
+ {
+ "content": {
+ "elements": [
+ {
+ "type": "text_run",
+ "text_run": {
+ "text": "comment text"
+ },
+ "docs_link": {
+ "url": "https://example.feishu.cn/docs/doccnHh7U87HOFpii5u5Gabcef"
+ },
+ "person": {
+ "user_id": "ou_cc19b2bfb93f8a44db4b4d6eababcef"
+ }
+ }
+ ]
+ },
+ "reply_id": "6916106822734512356",
+ "user_id": "ou_cc19b2bfb93f8a44db4b4d6eab2abcef",
+ "create_time": 1610281603,
+ "update_time": 1610281603,
+ "extra": {
+ "image_list": [
+ "xfsfseewewabcef"
+ ]
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+400 | 1069301 | fail | 重试,若稳定失败请联系相关业务方oncall人员
+400 | 1069302 | param error | 检查参数有效性
+403 | 1069303 | forbidden | 检查是否有待评论云文档的评论权限
+400 | 1069304 | docs had been deleted | 检查待评论云文档是否已被删除
+400 | 1069305 | docs not exist | 检查待评论云文档是否能正常访问
+400 | 1069306 | content review not pass | 排查评论内容是否存在不合法内容
+404 | 1069307 | not exist | 检查待评论云文档是否能正常访问、检查评论内容at人或云文档是否存在
+400 | 1069308 | exceeded limit | 评论数据超过上限限制,详情请咨询客服
+400 | 1069399 | internal error | 重试,若稳定失败请联系相关业务方oncall人员
+400 | 1064230 | locked for data migration | 数据迁移中,暂时无法上传。
diff --git a/数据结构概述.md b/数据结构概述.md
new file mode 100644
index 0000000..9b297b2
--- /dev/null
+++ b/数据结构概述.md
@@ -0,0 +1,80 @@
+# 数据结构概述
+
+本文档介绍多维表格数据表中记录、字段和视图等的数据结构。多维表格中的数据表由记录(record)和字段(field)组成, 同时可以拥有多个视图(view)。
+
+## 记录
+记录由 record 和 fields 两个结构组成。
+
+### record 结构
+
+record 是一个 object 结构类型。
+| 参数 | 数据类型 | 描述 |
+| --------- | ------- | --------- |
+|`record_id`| string | 记录的 ID |
+|`fields`| map | 记录的字段 |
+
+### fields 结构
+
+fields 字段为 map 型,由字段名称和其具体内容的键值对组成。了解 fields 详细结构和参数描述,参考[多维表格记录数据结构](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/bitable-record-data-structure-overview)。
+```json
+{
+ "任务情况总结": [
+ {
+ "text": "网站更新任务由黄泡泡负责,正在进行中",
+ "type": "text"
+ }
+ ]
+}
+```
+
+参数 | 数据类型 | 描述 | 示例值
+---|---|---|---
+key | string | 多维表格数据表中的字段名称。 | "任务情况总结"
+value | union | 某个字段的具体内容,其结构可以是数字、字符串、布尔型、字符串列表或对象列表。详情参考下文。 | 该示例值为对象列表,更多示例,参考[多维表格记录数据结构](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/bitable-record-data-structure-overview)。
```json
[
{
"text": "网站更新任务正在进行中",
"type": "text"
}
]
```
+
+## 字段
+
+字段即多维表格数据表中的“列”,是一个`object`结构类型。字段的基本结构如下所示。了解字段详细结构和参数描述,参考[字段编辑指南](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/guide)。
+
+```json
+{
+ "field_id": "fldYWaldeW", // 字段的 ID
+ "field_name": "文本", // 字段名称
+ "type": 1, // 字段的类型,详情参考下文
+ "description": "字段的描述", // 对字段的更多说明
+ "is_primary": true, // 该字段是否是初始的索引字段
+ "property": null, // 字段的属性,详情参考下文
+ "ui_type": "Text", // 字段在界面上的展示类型,例如进度字段是数字的一种展示形态
+ "is_hidden": false // 字段是否是隐藏字段
+}
+```
+参数描述如下所示:
+
+名称 | 类型 | 描述
+---|---|---
+field_id | string | 字段 ID
+field_name | string | 字段名称
+type | int | 字段类型:相同类型用ui_type区分:
- 1:文本(默认值)、条码(需声明 "ui_type": "Barcode")、邮箱(需声明"ui_type": "Email")
- 2:数字(默认值)、进度(需声明 "ui_type": "Progress")、货币(需声明 "ui_type": "Currency")、评分(需声明 "ui_type": "Rating")
- 3:单选
- 4:多选
- 5:日期
- 7:复选框
- 11:人员
- 13:电话号码
- 15:超链接
- 17:附件
- 18:单向关联
- 19:查找引用
- 20:公式
- 21:双向关联
- 22:地理位置
- 23:群组
- 24:流程(不支持通过写接口新增或编辑,仅支持读接口)
- 1001:创建时间
- 1002:最后更新时间
- 1003:创建人
- 1004:修改人
- 1005:自动编号
- 3001:按钮(不支持通过写接口新增或编辑,仅支持读接口)
+description | 字段的描述 | 对字段的更多说明。
+is_primary | true/false | 该字段是否是初始的索引字段。
+property | object | 字段属性,因字段类型而异。详情参考[字段编辑指南](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/guide)。
+ui_type | string | 字段的 UI 类型:
- "Text":文本
- "Email":邮箱
- "Barcode":条码
- "Number":数字
- "Progress":进度
- "Currency":货币
- "Rating":评分
- "SingleSelect":单选
- "MultiSelect":多选
- "DateTime":日期
- "Checkbox":复选框
- "User":人员
- "GroupChat":群组
- "Phone":电话号码
- "Url":超链接
- "Attachment":附件
- "SingleLink":单向关联
- "Formula":公式
- "Lookup": 查找引用
- "DuplexLink":双向关联
- "Location":地理位置
- "CreatedTime":创建时间
- "ModifiedTime":最后更新时间
- "CreatedUser":创建人
- "ModifiedUser":修改人
- "AutoNumber":自动编号
- "Button":按钮
+is_hidden | true/false | 字段是否是隐藏字段。
+
+## 视图
+
+视图是一个 object 结构类型。
+
+参数 | 类型 | 描述
+---|---|---
+view_id | string | 视图 ID。`view_id` 在一个多维表格中唯一,在全局不一定唯一。获取方式:
- 你可通过多维表格 URL 获取 `view_id`,下图高亮部分即为当前视图的唯一标识。

- 你也可通过[列出视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/list)接口获取 `view_id`。暂时无法获取到嵌入到文档中的多维表格的 `view_id`。
+view_name | string | 视图名称
+view_type | string | 视图类型,支持以下类型,默认为 grid 类型。
- grid:表格视图
- kanban:看板视图
- gallery:画册视图
- gantt:甘特视图
- form:表单视图
+
+## 自定义数据结构
+
+### delete_record
+| 参数 | 数据类型 | 描述 |
+| --------- | --------------- | ----------- |
+|`deleted` | `boolean` | 是否删除成功 |
+|`record_id` | `string` | 单条记录的 ID |
\ No newline at end of file
diff --git a/新增一个数据表.md b/新增一个数据表.md
new file mode 100644
index 0000000..7f183e7
--- /dev/null
+++ b/新增一个数据表.md
@@ -0,0 +1,189 @@
+# 新增一个数据表
+
+新增一个数据表,支持传入数据表名称、视图名称和字段。
+
+## 前提条件
+
+调用此接口前,请确保当前调用身份(tenant_access_token 或 user_access_token)已有多维表格的编辑等文档权限,否则接口将返回 HTTP 403 或 400 状态码。了解更多,参考[如何为应用或用户开通文档权限](https://open.feishu.cn/document/ukTMukTMukTM/uczNzUjL3czM14yN3MTN#16c6475a)。
+
+## 使用限制
+
+每个多维表格中,数据表与仪表盘的总数量上限为 100。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables
+HTTP Method | POST
+接口频率限制 | [10 次/秒](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 新增数据表(base:table:create)
查看、评论、编辑和管理多维表格(bitable:app)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+Content-Type | string | 是 | **固定值**:"application/json; charset=utf-8"
+
+### 路径参数
+
+名称 | 类型 | 描述
+---|---|---
+app_token | string | 多维表格 App 的唯一标识。不同形态的多维表格,其 app_token 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 app_token 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 obj_type 的值为 bitable 时,obj_token 字段的值才是多维表格的 app_token。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
**示例值**:"appbcbWCzen6D8dezhoCH2RpMAh"
**数据校验规则**:
- 最小长度:`1` 字符
+
+### 请求体
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+table | req_table | 否 | 数据表
+name | string | 否 | 数据表名称。该字段必填。
**注意**:
- 名称中的首尾空格将会被默认去除
- 数据表名称不可以包含 `/ \ ? * : [ ]` 等特殊字符
**示例值**:"一个新的数据表"
**数据校验规则**:
- 长度范围:`1` ~ `100` 字符
+default_view_name | string | 否 | 默认表格视图的名称。
注意:
- 名称中的首尾空格将会被去除
- 名称中不允许包含 [ ] 两个字符
**示例值**:"表格视图"
+fields | app.table.create_header\[\] | 否 | 数据表的初始字段。了解如何填写字段,参考[字段编辑指南](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/guide)。
**注意**:
- 如果传入了 `default_view_name` 字段,则必须传入 `fields` 字段
- 如果不传 `default_view_name` 字段,则 `fields` 字段为可选字段
- 若 `default_view_name` 字段和 `fields` 字段都不传,将会创建一个仅包含索引字段的空数据表。
- 数据表的第一个字段为索引字段。索引字段仅支持以下类型:
- 1:多行文本
- 2:数字
- 5:日期
- 13:电话号码
- 15:超链接
- 20:公式
- 22:地理位置
**数据校验规则**:
- 长度范围:`1` ~ `300`
+field_name | string | 是 | 字段名称
**示例值**:"问题描述"
+type | int | 是 | 字段类型。不支持新增 19 查找引用字段类型。
**示例值**:1
**可选值有**:
- 1:文本
- 2:数字
- 3:单选
- 4:多选
- 5:日期
- 7:复选框
- 11:人员
- 13:电话号码
- 15:超链接
- 17:附件
- 18:单向关联
- 20:公式
- 21:双向关联
- 22:地理位置
- 23:群组
- 1001:创建时间
- 1002:最后更新时间
- 1003:创建人
- 1004:修改人
- 1005:自动编号
+ui_type | string | 否 | 字段在界面上的展示类型,例如 Progress 进度字段是数字的一种展示形态
**示例值**:"Progress"
**可选值有**:
- Text:文本
- Barcode:条码
- Number:数字
- Progress:进度
- Currency:货币
- Rating:评分
- SingleSelect:单选
- MultiSelect:多选
- DateTime:日期
- Checkbox:复选框
- User:人员
- GroupChat:群组
- Phone:电话号码
- Url:超链接
- Attachment:附件
- SingleLink:单向关联
- Formula:公式
- DuplexLink:双向关联
- Location:地理位置
- CreatedTime:创建时间
- ModifiedTime:最后更新时间
- CreatedUser:创建人
- ModifiedUser:修改人
- AutoNumber:自动编号
+property | app.table.field.property | 否 | 字段属性
+options | app.table.field.property.option\[\] | 否 | 单选、多选字段的选项信息
+name | string | 否 | 选项名
**示例值**:"红色"
+id | string | 否 | 选项 ID,创建时不可指定 ID
**示例值**:"optKl35lnG"
+color | int | 否 | 选项颜色,详情参考[字段编辑指南](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/guide)。
**示例值**:0
**数据校验规则**:
- 取值范围:`0` ~ `54`
+formatter | string | 否 | 数字、公式字段的显示格式。详情参考[字段编辑指南](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/guide)。
**示例值**:"0"
+date_formatter | string | 否 | 日期、创建时间、最后更新时间字段的显示格式。默认为 "yyyy/MM/dd",详情参考[字段编辑指南](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/guide)。
**示例值**:"2021/01/30"
+auto_fill | boolean | 否 | 日期字段中新纪录自动填写创建时间。默认为 false
**示例值**:false
+multiple | boolean | 否 | 人员字段中允许添加多个成员,单向关联、双向关联中允许添加多个记录
**示例值**:false
+table_id | string | 否 | 单向关联、双向关联字段中关联的数据表的id
**示例值**:"tblsRc9GRRXKqhvW"
+table_name | string | 否 | 单向关联、双向关联字段中关联的数据表的名字
**示例值**:"table2"
+back_field_name | string | 否 | 双向关联字段中关联的数据表中对应的双向关联字段的名字
**示例值**:"table1-双向关联"
+auto_serial | app.field.property.auto_serial | 否 | 自动编号类型
+type | string | 是 | 自动编号类型
**示例值**:"auto_increment_number"
**可选值有**:
- custom:自定义编号
- auto_increment_number:自增数字
+options | app.field.property.auto_serial.options\[\] | 否 | 自动编号规则列表
+type | string | 是 | 自动编号的可选规则项类型
**示例值**:"created_time"
**可选值有**:
- system_number:自增数字位,value范围1-9
- fixed_text:固定字符,最大长度:20
- created_time:创建时间,支持格式 "yyyyMMdd"、"yyyyMM"、"yyyy"、"MMdd"、"MM"、"dd"
+value | string | 是 | 与自动编号的可选规则项类型相对应的取值
**示例值**:"yyyyMMdd"
+location | app.field.property.location | 否 | 地理位置输入方式
+input_type | string | 是 | 地理位置输入限制
**示例值**:"not_limit"
**可选值有**:
- only_mobile:只允许移动端上传
- not_limit:无限制
+formula_expression | string | 否 | 公式字段的表达式
**示例值**:"bitable::$table[tblNj92WQBAasdEf].$field[fldMV60rYs]*2"
+allowed_edit_modes | allowed_edit_modes | 否 | 字段支持的编辑模式
+manual | boolean | 否 | 是否允许手动录入
**示例值**:true
+scan | boolean | 否 | 是否允许移动端录入
**示例值**:true
+min | number(float) | 否 | 进度、评分等字段的数据范围最小值
**示例值**:0
+max | number(float) | 否 | 进度、评分等字段的数据范围最大值
**示例值**:10
+range_customize | boolean | 否 | 进度等字段是否支持自定义范围
**示例值**:true
+currency_code | string | 否 | 货币币种
**示例值**:"CNY"
+rating | rating | 否 | 评分字段的相关设置
+symbol | string | 否 | 评分字段的符号展示
**示例值**:"star"
+description | app.table.field.description | 否 | 字段的描述
+disable_sync | boolean | 否 | 是否禁止同步,如果为true,表示禁止同步该描述内容到表单的问题描述
**示例值**:true
**默认值**:`true`
+text | string | 否 | 字段描述内容,支持换行\n
**示例值**:"请按 name_id 格式填写\n例如:“Alice_20202020”"
+
+### 请求体示例
+```json
+{
+ "table": {
+ "name": "数据表名称",
+ "default_view_name": "默认的表格视图",
+ "fields": [
+ {
+ "field_name": "索引字段",
+ "type": 1
+ },
+ {
+ "field_name": "单选",
+ "type": 3,
+ "ui_type": "SingleSelect",
+ "property": {
+ "options": [
+ {
+ "name": "Enabled",
+ "color": 0
+ },
+ {
+ "name": "Disabled",
+ "color": 1
+ },
+ {
+ "name": "Draft",
+ "color": 2
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
+```
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+table_id | string | 多维表格数据表的 ID
+default_view_id | string | 默认表格视图的 ID。该字段仅在请求参数中填写了`default_view_name` 或 `fields` 字段才会返回
+field_id_list | string\[\] | 数据表初始字段的 ID 列表,该字段仅在请求参数中填写了 `fields` 才会返回
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "table_id": "tblDBTWm6Es84d8c",
+ "default_view_id": "vewUuKOz2R",
+ "field_id_list": [
+ "fldhr2hBEA"
+ ]
+ }
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+200 | 1254000 | WrongRequestJson | 请求体错误
+200 | 1254001 | WrongRequestBody | 请求体错误。请检查请求体中是否已传入所有必填参数
+200 | 1254002 | Fail | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1254003 | WrongBaseToken | app_token 错误
+200 | 1254004 | WrongTableId | table_id 错误
+200 | 1254007 | EmptyValue | 空值
+200 | 1254008 | EmptyView | 空视图
+200 | 1254009 | WrongFieldId | 字段 id 错误
+200 | 1254010 | ReqConvError | 请求错误
+400 | 1254012 | NotSupportFieldOrView | 不支持的字段或视图。注意数据表的初始索引字段仅支持以下类型:
- 1:多行文本
- 2:数字
- 5:日期
- 13:电话号码
- 15:超链接
- 20:公式
- 22:地理位置
+200 | 1254013 | TableNameDuplicated | 表名重复
+400 | 1254014 | FieldNameDuplicated | 字段名重复
+400 | 1254021 | EmptyViewName | 视图名为空
+400 | 1254022 | InvalidViewName | 视图名无效
+400 | 1254029 | InvalidFieldName | 字段名无效
+200 | 1254030 | TooLargeResponse | 响应体过大
+200 | 1254036 | Base is copying, please try again later. | 复制多维表格为异步操作,该错误码表示当前多维表格仍在复制中,在复制期间无法操作当前多维表格。需要等待复制完成后再操作
+200 | 1254040 | BaseTokenNotFound | app_token 不存在
+200 | 1254041 | TableIdNotFound | table_id 不存在
+200 | 1254044 | FieldIdNotFound | field_id 不存在
+200 | 1254060 | TextFieldConvFail | 多行文本字段错误
+200 | 1254061 | NumberFieldConvFail | 数字字段错误
+200 | 1254062 | SingleSelectFieldConvFail | 单选字段错误
+200 | 1254063 | MultiSelectFieldConvFail | 多选字段错误
+200 | 1254064 | DatetimeFieldConvFail | 日期字段错误
+200 | 1254065 | CheckboxFieldConvFail | 复选框字段错误
+200 | 1254066 | UserFieldConvFail | 人员字段有误。原因可能是:
- `user_id_type` 参数指定的 ID 类型与传入的 ID 类型不匹配
- 传入了不识别的类型或结构,目前只支持填写 `id` 参数,且需要传入数组
- 跨应用传入了 `open_id`。如果跨应用传入 ID,建议使用 `user_id`。不同应用获取的 `open_id` 不能交叉使用
- 若想对人员字段传空,可传 null
+200 | 1254067 | LinkFieldConvFail | 关联字段错误
+200 | 1254100 | TableExceedLimit | 数据表或仪表盘数量超限。每个多维表格中,数据表加仪表盘的数量最多为 100 个
+200 | 1254101 | ViewExceedLimit | 视图数量超限, 限制200个
+200 | 1254130 | TooLargeCell | 格子内容过大
+200 | 1254290 | TooManyRequest | 请求过快,稍后重试
+200 | 1254291 | Write conflict | 同一个数据表(table) 不支持并发调用写接口,请检查是否存在并发调用写接口。写接口包括:新增、修改、删除记录;新增、修改、删除字段;修改表单;修改视图等。
+200 | 1254301 | OperationTypeError | 多维表格未开启高级权限或不支持开启高级权限
+403 | 1254302 | Permission denied. | 调用身份缺少多维表格的高级权限。你需要为调用身份授予高级权限:
- 对用户授予高级权限,你需要在多维表格页面右上方 **分享** 入口为当前用户添加可管理权限。
- 对应用授予高级权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。


**注意**:
在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [API 权限](https://open.feishu.cn/document/ukTMukTMukTM/uYTM5UjL2ETO14iNxkTN/scope-list)。否则你将无法在文档应用窗口搜索到目标应用。
- 你也可以在 **多维表格高级权限设置** 中添加用户或一个包含应用的群组, 给予这个群自定义的读写等权限。
+400 | 1254607 | Data not ready, please try again later | 该报错一般是由于前置操作未执行完成,或本次操作数据太大,服务器计算超时导致。遇到该错误码时,建议等待一段时间后重试。通常有以下几种原因:
- **编辑操作频繁**:开发者对多维表格的编辑操作非常频繁。可能会导致由于等待前置操作处理完成耗时过长而超时的情况。多维表格底层对数据表的处理基于版本维度的串行方式,不支持并发。因此,并发请求时容易出现此类错误,不建议开发者对单个数据表进行并发请求。
- **批量操作负载重**:开发者在多维表格中进行批量新增、删除等操作时,如果数据表的数据量非常大,可能会导致单次请求耗时过长,最终导致请求超时。建议开发者适当降低批量请求的 page_size 以减少请求耗时。
- **资源分配与计算开销**:资源分配是基于单文档维度的,如果读接口涉及公式计算、排序等计算逻辑,会占用较多资源。例如,并发读取一个文档下的多个数据表也可能导致该文档阻塞。
+200 | 1255001 | InternalError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255002 | RpcError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255003 | MarshalError | 序列化错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255004 | UmMarshalError | 反序列化错误
+200 | 1255005 | ConvError | 服务内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
diff --git a/新增多个数据表.md b/新增多个数据表.md
new file mode 100644
index 0000000..c6e349e
--- /dev/null
+++ b/新增多个数据表.md
@@ -0,0 +1,131 @@
+# 新增多个数据表
+
+新增多个数据表,仅可指定数据表名称。
+
+## 前提条件
+
+调用此接口前,请确保当前调用身份(tenant_access_token 或 user_access_token)已有多维表格的编辑等文档权限,否则接口将返回 HTTP 403 或 400 状态码。了解更多,参考[如何为应用或用户开通文档权限](https://open.feishu.cn/document/ukTMukTMukTM/uczNzUjL3czM14yN3MTN#16c6475a)。
+
+## 使用限制
+
+每个多维表格中,数据表与仪表盘的总数量上限为 100。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/batch_create
+HTTP Method | POST
+接口频率限制 | [10 次/秒](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 新增数据表(base:table:create)
查看、评论、编辑和管理多维表格(bitable:app)
+字段权限要求 | **注意事项**:该接口返回体中存在下列敏感字段,仅当开启对应的权限后才会返回;如果无需获取这些字段,则不建议申请
获取用户 user ID(contact:user.employee_id:readonly)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+Content-Type | string | 是 | **固定值**:"application/json; charset=utf-8"
+
+### 路径参数
+
+名称 | 类型 | 描述
+---|---|---
+app_token | string | 多维表格 App 的唯一标识。不同形态的多维表格,其 app_token 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 app_token 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 obj_type 的值为 bitable 时,obj_token 字段的值才是多维表格的 app_token。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
**示例值**:"appbcbWCzen6D8dezhoCH2RpMAh"
+
+### 查询参数
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+user_id_type | string | 否 | 用户 ID 类型
**示例值**:open_id
**可选值有**:
- open_id:标识一个用户在某个应用中的身份。同一个用户在不同应用中的 Open ID 不同。[了解更多:如何获取 Open ID](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-obtain-openid)
- union_id:标识一个用户在某个应用开发商下的身份。同一用户在同一开发商下的应用中的 Union ID 是相同的,在不同开发商下的应用中的 Union ID 是不同的。通过 Union ID,应用开发商可以把同个用户在多个应用中的身份关联起来。[了解更多:如何获取 Union ID?](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-obtain-union-id)
- user_id:标识一个用户在某个租户内的身份。同一个用户在租户 A 和租户 B 内的 User ID 是不同的。在同一个租户内,一个用户的 User ID 在所有应用(包括商店应用)中都保持一致。User ID 主要用于在不同的应用间打通用户数据。[了解更多:如何获取 User ID?](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-obtain-user-id)
**默认值**:`open_id`
**当值为 `user_id`,字段权限要求**:
获取用户 user ID(contact:user.employee_id:readonly)
+
+### 请求体
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+tables | req_table\[\] | 否 | tables
+name | string | 否 | 数据表名称。该字段必填。
**注意**:
- 名称中的首尾空格将会被默认去除。
- 数据表名称不可以包含 `/ \ ? * : [ ]` 等特殊字符。
**示例值**:"一个新的数据表"
**数据校验规则**:
- 长度范围:`1` ~ `100` 字符
+
+### 请求体示例
+```json
+{
+ "tables": [
+ {
+ "name": "一个新的数据表"
+ }
+ ]
+}
+```
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+table_ids | string\[\] | table ids
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "table_ids": [
+ "tblIovTTN2eIW2hn"
+ ]
+ }
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+200 | 1254000 | WrongRequestJson | 请求体错误
+200 | 1254001 | WrongRequestBody | 请求体错误
+200 | 1254002 | Fail | 内部错误,有疑问可咨询客服
+200 | 1254003 | WrongBaseToken | app_token 错误
+200 | 1254004 | WrongTableId | table_id 错误
+200 | 1254005 | WrongViewId | view_id 错误
+200 | 1254006 | WrongRecordId | 检查 record_id
+200 | 1254007 | EmptyValue | 空值
+200 | 1254008 | EmptyView | 空视图
+200 | 1254009 | WrongFieldId | 字段 id 错误
+200 | 1254010 | ReqConvError | 请求错误
+400 | 1254013 | TableNameDuplicated | 表名重复
+200 | 1254030 | TooLargeResponse | 响应体过大
+400 | 1254036 | Base is copying, please try again later. | 复制多维表格为异步操作,该错误码表示当前多维表格仍在复制中,在复制期间无法操作当前多维表格。需要等待复制完成后再操作
+200 | 1254040 | BaseTokenNotFound | app_token 不存在
+200 | 1254041 | TableIdNotFound | table_id 不存在
+200 | 1254042 | ViewIdNotFound | view_id 不存在
+200 | 1254043 | RecordIdNotFound | record_id 不存在
+200 | 1254044 | FieldIdNotFound | field_id 不存在
+200 | 1254060 | TextFieldConvFail | 多行文本字段错误
+200 | 1254061 | NumberFieldConvFail | 数字字段错误
+200 | 1254062 | SingleSelectFieldConvFail | 单选字段错误
+200 | 1254063 | MultiSelectFieldConvFail | 多选字段错误
+200 | 1254064 | DatetimeFieldConvFail | 日期字段错误
+200 | 1254065 | CheckboxFieldConvFail | 复选框字段错误
+200 | 1254066 | UserFieldConvFail | 人员字段有误。原因可能是:
- `user_id_type` 参数指定的 ID 类型与传入的 ID 类型不匹配
- 传入了不识别的类型或结构,目前只支持填写 `id` 参数,且需要传入数组
- 跨应用传入了 `open_id`。如果跨应用传入 ID,建议使用 `user_id`。不同应用获取的 `open_id` 不能交叉使用
+200 | 1254067 | LinkFieldConvFail | 关联字段错误
+200 | 1254100 | TableExceedLimit | 数据表或仪表盘数量超限。每个多维表格中,数据表加仪表盘的数量最多为 100 个
+200 | 1254101 | ViewExceedLimit | 视图数量超限, 限制200个
+200 | 1254102 | FileExceedLimit | 超限
+200 | 1254103 | RecordExceedLimit | 记录数量超限, 限制20,000条
+200 | 1254104 | RecordAddOnceExceedLimit | 单次添加记录数量超限, 限制500条
+200 | 1254130 | TooLargeCell | 格子内容过大
+200 | 1254290 | TooManyRequest | 请求过快,稍后重试
+200 | 1254291 | Write conflict | 同一个数据表(table) 不支持并发调用写接口,请检查是否存在并发调用写接口。写接口包括:新增、修改、删除记录;新增、修改、删除字段;修改表单;修改视图等。
+200 | 1254301 | OperationTypeError | 多维表格未开启高级权限或不支持开启高级权限
+403 | 1254302 | The role has no permissions. | 调用身份缺少多维表格的高级权限。你需要为调用身份授予高级权限:
- 对用户授予高级权限,你需要在多维表格页面右上方 **分享** 入口为当前用户添加可管理权限。
- 对应用授予高级权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。


**注意**:
在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [API 权限](https://open.feishu.cn/document/ukTMukTMukTM/uYTM5UjL2ETO14iNxkTN/scope-list)。否则你将无法在文档应用窗口搜索到目标应用。
- 你也可以在 **多维表格高级权限设置** 中添加用户或一个包含应用的群组, 给予这个群自定义的读写等权限。
+200 | 1255001 | InternalError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255002 | RpcError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255003 | MarshalError | 序列化错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255004 | UmMarshalError | 反序列化错误
+200 | 1255005 | ConvError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+504 | 1255040 | 请求超时 | 进行重试
diff --git a/更新多维表格元数据.md b/更新多维表格元数据.md
new file mode 100644
index 0000000..cc86c02
--- /dev/null
+++ b/更新多维表格元数据.md
@@ -0,0 +1,107 @@
+# 更新多维表格元数据
+
+更新多维表格元数据,包括多维表格的名称、是否开启高级权限。
+
+## 注意事项
+
+- 在线文档和电子表格中嵌入的多维表格、知识库中的多维表格不支持开启高级权限。
+- 此接口非原子操作,先修改多维表格名称,后开关高级权限,可能存在部分成功的情况。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token
+HTTP Method | PUT
+接口频率限制 | [10 次/秒](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 更新多维表格(base:app:update)
查看、评论、编辑和管理多维表格(bitable:app)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+Content-Type | string | 是 | **固定值**:"application/json; charset=utf-8"
+
+### 路径参数
+
+名称 | 类型 | 描述
+---|---|---
+app_token | string | 目标多维表格的 App token。该接口仅支持存储在云空间文件夹中的多维表格,即 URL 以 **feishu.cn/base** 开头的多维表格形态。该类多维表格的 app_token 为 URL 下图高亮部分:

**示例值**:"appbcbWCzen6D8dezhoCH2RpMAh"
+
+### 请求体
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+name | string | 否 | 新的多维表格名称,不传则不更新名称。
**示例值**:"新的多维表格名称"
+is_advanced | boolean | 否 | 多维表格是否开启高级权限。不传则不更新设置。可选值:
- true:开启高级权限
- false:关闭高级权限
**示例值**:true
+
+### 请求体示例
+```json
+{
+ "name": "新的多维表格名称",
+ "is_advanced": true
+}
+```
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+app | display_app_v2 | 多维表格元数据
+app_token | string | 多维表格的唯一标识 app_token
+name | string | 多维表格的名称
+is_advanced | boolean | 多维表格是否已开启高级权限
+time_zone | string | 文档时区
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "app": {
+ "app_token": "appbcbWCzen6D8dezhoCH2RpMAh",
+ "name": "新的多维表格名字",
+ "is_advanced": true
+ }
+ }
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+200 | 1254000 | WrongRequestJson | 请求体错误
+200 | 1254001 | WrongRequestBody | 请求体错误
+200 | 1254002 | Fail | 内部错误,有疑问可咨询客服
+200 | 1254003 | WrongBaseToken | app_token 错误
+200 | 1254010 | ReqConvError | 请求错误
+200 | 1254031 | InvalidAppName | 多维表格名称格式错误,长度不超过 100 个字符,不能包含 ? / \ * : [ ]
+400 | 1254036 | Base is copying, please try again later. | 多维表格副本复制中,稍后重试
+200 | 1254040 | BaseTokenNotFound | app_token 不存在
+200 | 1254043 | RecordIdNotFound | record_id 不存在
+200 | 1254200 | internal error | 内部错误
+200 | 1254290 | TooManyRequest | 请求过快,稍后重试
+200 | 1254291 | Write conflict | 同一个数据表(table) 不支持并发调用写接口,请检查是否存在并发调用写接口。写接口包括:新增、修改、删除记录;新增、修改、删除字段;修改表单;修改视图等。
+400 | 1254301 | OperationTypeError | 多维表格未开启高级权限或不支持开启高级权限
+403 | 1254302 | Permission denied. | 无访问权限, 常由表格开启了高级权限造成, 请在高级权限设置中添加一个包含应用的群, 给予这个群读写权限
+403 | 1254304 | The role has no permissions. | 无权限
+200 | 1255001 | InternalError | 内部错误,有疑问可咨询客服
+200 | 1255002 | RpcError | 内部错误,有疑问可咨询客服
+200 | 1255003 | MarshalError | 序列化错误,有疑问可咨询客服
+200 | 1255004 | UmMarshalError | 反序列化错误
+504 | 1255040 | 请求超时 | 进行重试
+
+## 补充错误码
+
+**错误码** | **原因** | **排查建议** |
+| ------- | ------- | ----------------- |
+| 1254061 | 字段格式错误。 | 确认对应字段类型参数格式是否正确。
diff --git a/更新数据表.md b/更新数据表.md
new file mode 100644
index 0000000..f5b045d
--- /dev/null
+++ b/更新数据表.md
@@ -0,0 +1,78 @@
+# 更新数据表
+
+更新数据表的名称。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id
+HTTP Method | PATCH
+接口频率限制 | [10 次/秒](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 更新数据表(base:table:update)
查看、评论、编辑和管理多维表格(bitable:app)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+Content-Type | string | 是 | **固定值**:"application/json; charset=utf-8"
+
+### 路径参数
+
+名称 | 类型 | 描述
+---|---|---
+app_token | string | 多维表格 App 的唯一标识。不同形态的多维表格,其 `app_token` 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 `app_token` 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 `obj_type` 的值为 `bitable` 时,`obj_token` 字段的值才是多维表格的 `app_token`。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
**示例值**:"XrgTb4y1haKYnasu0xXb1g7lcSg"
**数据校验规则**:
- 最小长度:`1` 字符
+table_id | string | 多维表格数据表的唯一标识。获取方式:
- 你可通过多维表格 URL 获取 `table_id`,下图高亮部分即为当前数据表的 `table_id`
- 也可通过[列出数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list)接口获取 `table_id`

**示例值**:"tbl1TkhyTWDkSoZ3"
+
+### 请求体
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+name | string | 否 | 数据表的新名称。
**注意**:
- 名称中的首尾空格将会被去除。
- 数据表名称不可以包含 `/ \ ? * : [ ]` 等特殊字符。
- 如果名称为空或和旧名称相同,接口仍然会返回成功,但是名称不会被更改。
**示例值**:"新的数据表名称"
**数据校验规则**:
- 长度范围:`1` ~ `100` 字符
- 正则校验:`^[^\[\]\:\\\/\?\*]+$`
+
+### 请求体示例
+```json
+{
+ "name": "新的数据表名称"
+}
+```
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+name | string | 新的数据表名称
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "name": "新的数据表名称"
+ }
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+400 | 1254001 | WrongRequestBody | 请求体错误
+400 | 1254002 | Fail | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+400 | 1254003 | WrongBaseToken | app_token 错误
+400 | 1254004 | WrongTableId | table_id 错误
+400 | 1254013 | TableNameDuplicated | 表名重复
+403 | 1254036 | Base is copying, please try again later. | 复制多维表格为异步操作,该错误码表示当前多维表格仍在复制中,在复制期间无法操作当前多维表格。需要等待复制完成后再操作
+404 | 1254040 | BaseTokenNotFound | app_token 不存在
+404 | 1254041 | TableIdNotFound | table_id 不存在
+429 | 1254290 | TooManyRequest | 请求过快,稍后重试
+403 | 1254302 | Permission denied. | 调用身份缺少多维表格的高级权限。你需要为调用身份授予高级权限:
- 对用户授予高级权限,你需要在多维表格页面右上方 **分享** 入口为当前用户添加可管理权限。
- 对应用授予高级权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。


**注意**:
在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [API 权限](https://open.feishu.cn/document/ukTMukTMukTM/uYTM5UjL2ETO14iNxkTN/scope-list)。否则你将无法在文档应用窗口搜索到目标应用。
- 你也可以在 **多维表格高级权限设置** 中添加用户或一个包含应用的群组, 给予这个群自定义的读写等权限。
+500 | 1255001 | InternalError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
diff --git a/获取多维表格元数据.md b/获取多维表格元数据.md
new file mode 100644
index 0000000..ca456a9
--- /dev/null
+++ b/获取多维表格元数据.md
@@ -0,0 +1,108 @@
+# 获取多维表格元数据
+
+获取指定多维表格的元数据信息,包括多维表格名称、多维表格版本号、多维表格是否开启高级权限等。
+
+## 请求
+
+基本 |
+---|---
+HTTP URL | https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token
+HTTP Method | GET
+接口频率限制 | [20 次/秒](https://open.feishu.cn/document/ukTMukTMukTM/uUzN04SN3QjL1cDN)
+支持的应用类型 | Custom App、Store App
+权限要求
**调用该 API 所需的权限。开启其中任意一项权限即可调用**
开启任一权限即可 | 获取多维表格信息(base:app:read)
查看、评论、编辑和管理多维表格(bitable:app)
查看、评论和导出多维表格(bitable:app:readonly)
+
+### 请求头
+
+名称 | 类型 | 必填 | 描述
+---|---|---|---
+Authorization | string | 是 | `tenant_access_token`
或
`user_access_token`
**值格式**:"Bearer `access_token`"
**示例值**:"Bearer u-7f1bcd13fc57d46bac21793a18e560"
[了解更多:如何选择与获取 access token](https://open.feishu.cn/document/uAjLw4CM/ugTN1YjL4UTN24CO1UjN/trouble-shooting/how-to-choose-which-type-of-token-to-use)
+
+### 路径参数
+
+名称 | 类型 | 描述
+---|---|---
+app_token | string | 多维表格 App 的唯一标识。不同形态的多维表格,其 `app_token` 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 `app_token` 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 `obj_type` 的值为 `bitable` 时,`obj_token` 字段的值才是多维表格的 `app_token`。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
**示例值**:"appbcbWCzen6D8dezhoCH2RpMAh"
+
+## 响应
+
+### 响应体
+
+名称 | 类型 | 描述
+---|---|---
+code | int | 错误码,非 0 表示失败
+msg | string | 错误描述
+data | \- | \-
+app | display_app | 多维表格元数据
+app_token | string | 多维表格的唯一标识 app_token
+name | string | 多维表格的名称
+revision | int | 多维表格的版本号。对多维表格进行修改时更新,如新增、删除数据表,修改数据表名等,初始为 1,每次更新+1
+is_advanced | boolean | 多维表格是否开启了高级权限。取值包括:
- true:开启了高级权限
- false:关闭了高级权限
了解更多参考飞书帮助中心文档[使用多维表格高级权限](https://www.feishu.cn/hc/zh-CN/articles/588604550568)。
+time_zone | string | 多维表格的时区
+formula_type | int | 多维表格的公式字段类型。可结合[字段相关 API](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/create)使用。
**可选值有**:
- 1:不支持指定公式字段类型
- 2:支持指定公式字段类型
+advance_version | string | 文档高级权限版本。可结合[自定义角色 API](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role/create)使用。
**可选值有**:
- v1:v1版本
- v2:v2版本
+
+### 响应体示例
+```json
+{
+ "code": 0,
+ "msg": "success",
+ "data": {
+ "app": {
+ "app_token": "appbcbWCzen6D8dezhoCH2RpMAh",
+ "name": "mybase",
+ "revision": 1,
+ "is_advanced": false,
+ "time_zone": "Asia/Beijing",
+ "formula_type": 1,
+ "advance_version": "v1"
+ }
+ }
+}
+```
+
+### 错误码
+
+HTTP状态码 | 错误码 | 描述 | 排查建议
+---|---|---|---
+200 | 1254000 | WrongRequestJson | 请求体错误
+200 | 1254001 | WrongRequestBody | 请求体错误
+200 | 1254002 | Fail | 导致报 1254002 错误码的场景较多,请参考以下建议排查:
- 如果单次操作的内容变更较大,请尝试在单次操作中减少数据量
- 如果你并发调用了接口,请尝试控制请求间隔,稍后重试
- 如果在知识库(wiki)中创建多维表格,请检查你是否使用了知识库[创建知识空间节点](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space-node/create)接口创建多维表格。在此场景下不能使用[创建多维表格](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app/create)接口
- 请检查接口参数是否有误。例如,在分页查询多维表格时,传递了无效的 page_token,或传递了错误的数据表的 table_id
- 如果该报错偶尔发生,可能是服务器超时或不稳定,请重试解决
+200 | 1254003 | WrongBaseToken | app_token 错误。app_token 是多维表格 App 的唯一标识。不同形态的多维表格,其 `app_token` 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 `app_token` 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 `obj_type` 的值为 `bitable` 时,`obj_token` 字段的值才是多维表格的 `app_token`。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
+200 | 1254004 | WrongTableId | table_id 错误。table_id 是多维表格数据表的唯一标识。获取方式:
- 你可通过多维表格 URL 获取 `table_id`,下图高亮部分即为当前数据表的 `table_id`
- 也可通过[列出数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list)接口获取 `table_id`

+200 | 1254005 | WrongViewId | view_id 错误。view_id 是多维表格中视图的唯一标识。获取方式:
- 在多维表格的 URL 地址栏中,`view_id` 是下图中高亮部分:

- 通过[列出视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/list)接口获取。暂时无法获取到嵌入到云文档中的多维表格的 `view_id`。
**注意**:
当 `filter` 参数 或 `sort` 参数不为空时,请求视为对数据表中的全部数据做条件过滤,指定的 `view_id` 会被忽略。
+200 | 1254006 | WrongRecordId | record_id 错误。record_id 是数据表中一条记录的唯一标识。通过[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)接口获取
+200 | 1254007 | EmptyValue | 空值
+200 | 1254008 | EmptyView | 空视图
+200 | 1254009 | WrongFieldId | field_id 错误。field_id 是数据表中一个字段的唯一标识。通过[列出字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/list)接口获取
+200 | 1254010 | ReqConvError | 请求错误
+200 | 1254030 | TooLargeResponse | 响应体过大
+400 | 1254036 | Base is copying, please try again later. | 复制多维表格为异步操作,该错误码表示当前多维表格仍在复制中,在复制期间无法操作当前多维表格。需要等待复制完成后再操作
+200 | 1254040 | BaseTokenNotFound | app_token 不存在。不同形态的多维表格,其 `app_token` 的获取方式不同:
- 如果多维表格的 URL 以 ==**feishu.cn/base**== 开头,该多维表格的 `app_token` 是下图高亮部分:

- 如果多维表格的 URL 以 ==**feishu.cn/wiki**== 开头,你需调用知识库相关[获取知识空间节点信息](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/wiki-v2/space/get_node)接口获取多维表格的 app_token。当 `obj_type` 的值为 `bitable` 时,`obj_token` 字段的值才是多维表格的 `app_token`。
了解更多,参考[多维表格 app_token 获取方式](https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview#-752212c)。
+200 | 1254041 | TableIdNotFound | table_id 不存在。获取方式:
- 你可通过多维表格 URL 获取 `table_id`,下图高亮部分即为当前数据表的 `table_id`
- 也可通过[列出数据表](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list)接口获取 `table_id`

+200 | 1254042 | ViewIdNotFound | view_id 不存在。获取方式:
- 在多维表格的 URL 地址栏中,`view_id` 是下图中高亮部分:

- 通过[列出视图](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/list)接口获取。暂时无法获取到嵌入到云文档中的多维表格的 `view_id`。
**注意**:
当 `filter` 参数 或 `sort` 参数不为空时,请求视为对数据表中的全部数据做条件过滤,指定的 `view_id` 会被忽略。
+200 | 1254043 | RecordIdNotFound | record_id 不存在。record_id 是数据表中一条记录的唯一标识。请通过[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)接口获取。
+200 | 1254044 | FieldIdNotFound | field_id 不存在。field_id 是数据表中一个字段的唯一标识。通过[列出字段](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-field/list)接口获取。
+200 | 1254060 | TextFieldConvFail | 文本字段错误
+200 | 1254061 | NumberFieldConvFail | 数字字段错误
+200 | 1254062 | SingleSelectFieldConvFail | 单选字段错误
+200 | 1254063 | MultiSelectFieldConvFail | 多选字段错误
+200 | 1254064 | DatetimeFieldConvFail | 日期字段错误
+200 | 1254065 | CheckboxFieldConvFail | 复选框字段错误
+200 | 1254066 | UserFieldConvFail | 人员字段有误。原因可能是:
- `user_id_type` 参数指定的 ID 类型与传入的 ID 类型不匹配
- 传入了不识别的类型或结构,目前只支持填写 `id` 参数,且需要传入数组
- 跨应用传入了 `open_id`。如果跨应用传入 ID,建议使用 `user_id`。不同应用获取的 `open_id` 不能交叉使用
- 若想对人员字段传空,可传 null
+200 | 1254067 | LinkFieldConvFail | 关联字段错误
+200 | 1254100 | TableExceedLimit | 数据表或仪表盘数量超限。每个多维表格中,数据表加仪表盘的数量最多为 100 个
+200 | 1254101 | ViewExceedLimit | 视图数量超限, 限制200个
+200 | 1254102 | FileExceedLimit | 文件数量超限
+200 | 1254103 | RecordExceedLimit | 记录数量超限, 限制20,000条
+200 | 1254104 | RecordAddOnceExceedLimit | 单次添加记录数量超限, 单次调用最多更新 1,000 条记录
+200 | 1254130 | TooLargeCell | 格子内容过大
+200 | 1254290 | TooManyRequest | 请求过快,稍后重试
+200 | 1254291 | Write conflict | 在同一个数据表中,并发调用了读写接口或请求过快,出现冲突。请参考以下建议解决:
- 确保没有并发调用多维表格读写相关接口
- 若操作量较大,建议在接口与接口之间增加 0.5 或 1 秒的延迟,也可在报错中增加重试逻辑,确保业务的稳定性
- 对于写接口,可以将接口中的查询参数 `ignore_consistency_check` 设置为 true,表示在读写操作时,暂时忽略一致性检查,以提高性能
+200 | 1254301 | OperationTypeError | 多维表格未开启高级权限或不支持开启高级权限
+200 | 1255001 | InternalError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255002 | RpcError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255003 | MarshalError | 序列化错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255004 | UmMarshalError | 反序列化错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+200 | 1255005 | ConvError | 内部错误,请联系[技术支持](https://applink.feishu.cn/TLJpeNdW)
+504 | 1255040 | Request timed out, please try again later | 请求超时,进行重试