From e6e4cd8119568eb49f1b9b2273d9f5b59cbfda06 Mon Sep 17 00:00:00 2001 From: 0Xiao0 <511201264@qq.com> Date: Mon, 23 Mar 2026 10:45:02 +0800 Subject: [PATCH] first commit --- .dockerignore | 4 + .env.example | 24 ++ .gitignore | 11 + .python-version | 1 + Dockerfile | 15 + README.md | 70 +++ api.py | 237 +++++++++++ append_bitables.py | 130 ++++++ batch_get_records.py | 58 +++ bitable_calendar.py | 108 +++++ config.py | 54 +++ exec.ps1 | 2 + exec.sh | 2 + export_web_view.py | 971 ++++++++++++++++++++++++++++++++++++++++++ get_records.py | 59 +++ import_bitables.py | 158 +++++++ list_test.py | 39 ++ main.py | 6 + merge_bitables.py | 221 ++++++++++ mock.py | 40 ++ outline_view.html | 915 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 10 + requirements.txt | 2 + server.py | 76 ++++ test_batch_get.py | 44 ++ utils.py | 34 ++ uv.lock | 110 +++++ 列出数据表.md | 117 +++++ 创建多维表格.md | 124 ++++++ 复制多维表格.md | 108 +++++ 多维表格常见问题.md | 194 +++++++++ 多维表格概述.md | 272 ++++++++++++ 批量获取记录.md | 296 +++++++++++++ 批量获取评论.md | 159 +++++++ 数据结构概述.md | 80 ++++ 新增一个数据表.md | 189 ++++++++ 新增多个数据表.md | 131 ++++++ 更新多维表格元数据.md | 107 +++++ 更新数据表.md | 78 ++++ 获取多维表格元数据.md | 108 +++++ 40 files changed, 5364 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 api.py create mode 100644 append_bitables.py create mode 100644 batch_get_records.py create mode 100644 bitable_calendar.py create mode 100644 config.py create mode 100644 exec.ps1 create mode 100644 exec.sh create mode 100644 export_web_view.py create mode 100644 get_records.py create mode 100644 import_bitables.py create mode 100644 list_test.py create mode 100644 main.py create mode 100644 merge_bitables.py create mode 100755 mock.py create mode 100644 outline_view.html create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 server.py create mode 100644 test_batch_get.py create mode 100644 utils.py create mode 100644 uv.lock create mode 100644 列出数据表.md create mode 100644 创建多维表格.md create mode 100644 复制多维表格.md create mode 100644 多维表格常见问题.md create mode 100644 多维表格概述.md create mode 100644 批量获取记录.md create mode 100644 批量获取评论.md create mode 100644 数据结构概述.md create mode 100644 新增一个数据表.md create mode 100644 新增多个数据表.md create mode 100644 更新多维表格元数据.md create mode 100644 更新数据表.md create mode 100644 获取多维表格元数据.md 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

+ + +
+ +
+ +
+ Date: + + - + +
+ + + + +
+ +
+
+ +
+
+ + + + +""" + +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

+ + +
+ +
+ +
+ Date: + + - + +
+ + + + +
+ +
+
+ +
+
+ + + + 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` 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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)。 + +- 对应用授予可管理权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/22c027f63c540592d3ca8f41d48bb107_CSas7OYJBR.png?height=1994&lazyload=true&maxWidth=550&width=3278) + +![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/9f3353931fafeea16a39f0eb887db175_0tjzC9P3zU.png?height=728&lazyload=true&maxWidth=550&width=890) + +**注意**: + 在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [API 权限](https://open.feishu.cn/document/ukTMukTMukTM/uYTM5UjL2ETO14iNxkTN/scope-list)。否则你将无法在文档应用窗口搜索到目标应用。 + +- 你也可以在 **多维表格高级权限设置** 中添加用户或一个包含应用的群组, 给予这个群自定义的读写等权限。 + +## 5. 调用查询记录接口,复选框返回数据为空,如何解决? + +在多维表格中,如果字段为空值,则查询记录接口不返回数据。相应地,如果复选框字段为空值(即用户没有选中和取消选中过该复选框),则查询记录接口也不返回数据。在此场景下,由于空值效果与复选框为 `false` 效果相同,开发者需自行兼容该空值场景。 +## 6. 如何筛选自定义编号类型的自动编号字段? + +要筛选含有固定字符的自动编号字段,需将自定义的固定字符去除,再筛选,否则将返回空数据。如下图,自定义的固定字符为 2024,在调用查询记录接口时,需确保仅 value 的值为自增部分数字,不包含自定义的 2024。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/a8fc72c1c50b1621a18886aba56c0008_PMPNNaXEXe.png?height=488&lazyload=true&maxWidth=350&width=495) + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/0dd311da6db2fa91ad4e3ab2425ad3d7_myNxGtTNGr.png?height=428&lazyload=true&maxWidth=650&width=1594) + +## 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. 在多维表格中新增一个自动编号字段,编号类型选择自增数字。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/036152cbc21520eb106eae1e3329bdec_haAdeKitl7.png?height=561&lazyload=true&maxWidth=350&width=518) + +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) 的 **接入流程** 一节。 +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/2bf59a67945d4fa8ec04de95d7e60fc9_hpiU9OyoGD.png?height=546&lazyload=true&width=6382) + +## 开发教程 +体验以下多维表格相关教程,了解如何运用多维表格 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 中点击查看。 ![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/5bafd33990c42fcc8ef5d8c4c5760375_FzucbOdzz5.png?height=583&lazyload=true&width=1028) | +| 字段 | 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)。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/c0e8e232f2057b25462b2f7704b2626c_MfUbOGXKne.png?height=7223&lazyload=true&maxWidth=700&width=12841) + +### 多维表格 app + +一个多维表格可以理解成是一个应用(app,但不是在开发者后台创建的应用),标记该应用的唯一标识为 `app_token`。作为一个应用,多维表格有多种形态:可以作为一个独立应用,也可以作为一个模块(block)与文档、电子表格结合。 + +#### 多维表格形态 + +| **多维表格形态** | **资源定义** | **含义** | +| ---------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 文件夹中的多维表格 | Base app | 存储在飞书云空间(云盘)文件夹中的多维表格。URL 以 **feishu.cn/base** 开头![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_Xc87lb1mIE.png?height=766&lazyload=true&width=3004) | +| 知识库下的多维表格 | Base app 和 wiki node | 放置在知识库中的多维表格。URL 以 **feishu.cn/wiki** 开头![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/bb2e4afcd9a67d6fac88dd959efbf8f5_W8ma47dqcz.png?height=408&lazyload=true&width=1410) | +| 文档嵌入多维表格 | Base docx block | 即在"文档"中插入的多维表格,URL 以 **feishu.cn/docx** 开头![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/d6a6091dbd3db056d1e4126651116f00_rLR5ryVX5P.png?height=808&lazyload=true&width=1620) | +| 电子表格嵌入多维表格 | Base sheet block | 在电子表格中嵌入的多维表格,URL 以 **feishu.cn/sheets** 开头![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/3859f7bdf1f00878d2077175e34a09c1_O5wDuyRIFB.png?height=496&lazyload=true&width=1689) | + +#### 多维表格 app_token 获取方式 + +不同形态的多维表格,其 `app_token` 的获取方式不同,具体如下所示。 + +##### **文件夹中的多维表格** + +该类多维表格的 app_token 为 URL 下图高亮部分: + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_sTn7sVvhOB.png?height=766&lazyload=true&maxWidth=700&width=3004) + +##### **知识库下的多维表格** + +需调用知识库相关[获取知识空间节点信息](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`。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/18741fe2a0d3cafafaf9949b263bb57d_yD1wkOrSju.png?height=746&lazyload=true&maxWidth=700&width=2976) + +### 视图 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`。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/140668632c97e0095832219001d17c54_c76RMwZAgW.png?height=748&lazyload=true&maxWidth=700&width=2998) + +#### **表单视图 form** + +表单视图是多维表格的一种视图类型,形式类似于问卷,可以用来收集信息和数据。每个表单都有唯一标识 `form_id`,即当前视图的 `view_id`。`form_id` 的获取方式和 `view_id` 的获取方式相同。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/b8d9bd2d7e1ca7d0cc955ef7f1f4fe16_zDd5TqOh3Q.png?height=942&lazyload=true&maxWidth=600&width=1327) + +### 记录 record + +数据表中的每一行数据都是一条记录(record)。每条记录都有唯一标识 `record_id`,`record_id` 在一个多维表格中唯一,在全局不一定唯一。`record_id` 需要通过[查询记录](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search)接口获取。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/abc84b39be159ccdcafa707ee141144d_3fcTz7mMP5.png?height=503&lazyload=true&maxWidth=700&width=1536) + +### 字段 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)。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6fb2359552ed15433289fbd0d9fc53c1_mGnTo91OJa.png?height=656&lazyload=true&maxWidth=700&width=1170) + +### 仪表盘 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)。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/fae3c8a886a0075fdeeefe5b74f274e5_WepjCfS7Hx.png?height=1490&lazyload=true&maxWidth=600&width=2794) + +仪表盘的唯一标识为 `block_id`,以 `blk` 开头,你可通过多维表格 URL 获取 `block_id`,下图高亮部分即为当前仪表盘的唯一标识,也可通过[列出仪表盘](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-dashboard/list)接口获取。 + +![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/a966d15323ee73c66b1e9a31d34ae6c7_x3ctncH2nO.png?height=575&lazyload=true&maxWidth=600&width=1397) + +### 高级权限 + +高级权限允许用户针对单一数据表设置哪些用户可以查看、编辑指定的行,或是设置针对某用户可以编辑的列。高级权限接口分为 **自定义角色** 和 **协作者** 两部分,多维表格的 **所有者** 或者 **有可管理权限** 的用户可通过接口设置高级权限,管理高级权限的协作者。了解更多,参考[高级权限概述](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role/advanced-permission-guide)。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/9a6f4194ee269de14c8ee1f077e5f782_mp94yLqGBE.png?height=760&lazyload=true&maxWidth=500&width=871) + +#### **自定义角色 role** + +在高级权限中添加角色并设置权限,该角色即为自定义角色。每个自定义角色都有唯一标识 `role_id`。`role_id` 需要通过[列出自定义角色](https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-role/list)接口获取。 + +![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/3be8cd2e40f0f981370bc87251dafebd_i1xBoWPh2Z.png?height=761&lazyload=true&maxWidth=500&width=880) + +#### **协作者 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` 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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`
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/18741fe2a0d3cafafaf9949b263bb57d_yD1wkOrSju.png?height=746&lazyload=true&maxWidth=700&width=2976)
**示例值**:"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` 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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`
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/18741fe2a0d3cafafaf9949b263bb57d_yD1wkOrSju.png?height=746&lazyload=true&maxWidth=700&width=2976) +400 | 1254005 | WrongViewId | view_id 错误。view_id 是多维表格中视图的唯一标识。获取方式:
- 在多维表格的 URL 地址栏中,`view_id` 是下图中高亮部分:
![view_id.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/140668632c97e0095832219001d17c54_DJMgVH9x2S.png?height=748&lazyload=true&width=2998)
- 通过[列出视图](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` 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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`
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/18741fe2a0d3cafafaf9949b263bb57d_yD1wkOrSju.png?height=746&lazyload=true&maxWidth=700&width=2976) +404 | 1254042 | ViewIdNotFound | view_id 不存在。获取方式:
- 在多维表格的 URL 地址栏中,`view_id` 是下图中高亮部分:
![view_id.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/140668632c97e0095832219001d17c54_DJMgVH9x2S.png?height=748&lazyload=true&width=2998)
- 通过[列出视图](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 名称可能会忽略空格、换行或特殊符号等差异。
- 表格是否开启了高级权限但调用身份缺少对应字段的权限。你需要为调用身份授予高级权限:
- 对用户授予高级权限,你需要在多维表格页面右上方 **分享** 入口为当前用户添加可管理权限。![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/df3911b4f747d75914f35a46962d667d_dAsfLjv3QC.png?height=546&lazyload=true&maxWidth=550)
- 对应用授予高级权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/22c027f63c540592d3ca8f41d48bb107_CSas7OYJBR.png?height=1994&maxWidth=550&width=3278)
![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/9f3353931fafeea16a39f0eb887db175_0tjzC9P3zU.png?maxWidth=550)
**注意**:
在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [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`,下图高亮部分即为当前视图的唯一标识。
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/140668632c97e0095832219001d17c54_c76RMwZAgW.png?height=748&lazyload=true&maxWidth=700&width=2998)
- 你也可通过[列出视图](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 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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. | 调用身份缺少多维表格的高级权限。你需要为调用身份授予高级权限:
- 对用户授予高级权限,你需要在多维表格页面右上方 **分享** 入口为当前用户添加可管理权限。![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/df3911b4f747d75914f35a46962d667d_dAsfLjv3QC.png?height=546&lazyload=true&maxWidth=550)
- 对应用授予高级权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/22c027f63c540592d3ca8f41d48bb107_CSas7OYJBR.png?height=1994&maxWidth=550&width=3278)
![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/9f3353931fafeea16a39f0eb887db175_0tjzC9P3zU.png?maxWidth=550)
**注意**:
在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [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 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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. | 调用身份缺少多维表格的高级权限。你需要为调用身份授予高级权限:
- 对用户授予高级权限,你需要在多维表格页面右上方 **分享** 入口为当前用户添加可管理权限。![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/df3911b4f747d75914f35a46962d667d_dAsfLjv3QC.png?height=546&lazyload=true&maxWidth=550)
- 对应用授予高级权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/22c027f63c540592d3ca8f41d48bb107_CSas7OYJBR.png?height=1994&maxWidth=550&width=3278)
![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/9f3353931fafeea16a39f0eb887db175_0tjzC9P3zU.png?maxWidth=550)
**注意**:
在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [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 下图高亮部分:
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_sTn7sVvhOB.png?height=766&lazyload=true&maxWidth=700&width=3004)
**示例值**:"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` 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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`
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/18741fe2a0d3cafafaf9949b263bb57d_yD1wkOrSju.png?height=746&lazyload=true&maxWidth=700&width=2976)
**示例值**:"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. | 调用身份缺少多维表格的高级权限。你需要为调用身份授予高级权限:
- 对用户授予高级权限,你需要在多维表格页面右上方 **分享** 入口为当前用户添加可管理权限。![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/df3911b4f747d75914f35a46962d667d_dAsfLjv3QC.png?height=546&lazyload=true&maxWidth=550)
- 对应用授予高级权限,你需通过多维表格页面右上方 **「...」** -> **「...更多」** ->**「添加文档应用」** 入口为应用添加可管理权限。
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/22c027f63c540592d3ca8f41d48bb107_CSas7OYJBR.png?height=1994&maxWidth=550&width=3278)
![image.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/9f3353931fafeea16a39f0eb887db175_0tjzC9P3zU.png?maxWidth=550)
**注意**:
在 **添加文档应用** 前,你需确保目标应用至少开通了一个多维表格的 [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` 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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` 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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`
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/18741fe2a0d3cafafaf9949b263bb57d_yD1wkOrSju.png?height=746&lazyload=true&maxWidth=700&width=2976) +200 | 1254005 | WrongViewId | view_id 错误。view_id 是多维表格中视图的唯一标识。获取方式:
- 在多维表格的 URL 地址栏中,`view_id` 是下图中高亮部分:
![view_id.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/140668632c97e0095832219001d17c54_DJMgVH9x2S.png?height=748&lazyload=true&width=2998)
- 通过[列出视图](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` 是下图高亮部分:
![app_token.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/6916f8cfac4045ba6585b90e3afdfb0a_GxbfkJHZBa.png?height=766&lazyload=true&width=3004)
- 如果多维表格的 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`
![](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/18741fe2a0d3cafafaf9949b263bb57d_yD1wkOrSju.png?height=746&lazyload=true&maxWidth=700&width=2976) +200 | 1254042 | ViewIdNotFound | view_id 不存在。获取方式:
- 在多维表格的 URL 地址栏中,`view_id` 是下图中高亮部分:
![view_id.png](//sf3-cn.feishucdn.com/obj/open-platform-opendoc/140668632c97e0095832219001d17c54_DJMgVH9x2S.png?height=748&lazyload=true&width=2998)
- 通过[列出视图](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 | 请求超时,进行重试