addproject

This commit is contained in:
qiaoxinjiu
2026-01-22 19:10:37 +08:00
commit 6994b185a3
184 changed files with 21039 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
# -*- coding:utf-8 -*-
import json
import requests
import logging
from base_framework.public_tools.utils import Tools
obj_tool = Tools()
class FeiShuMultidimensionalTableOperations:
def __init__(self, app_token, table_id, view_id):
"""
初始化飞书多维表格操作类
Args:
app_token: 多维表格token登录飞书多维表格按F12右侧接口列表中找带token的接口复制token
table_id: 多维表格id登录飞书多维表格在浏览器地址栏中找到table=后面的值
view_id: 多维表格视图id登录飞书多维表格在浏览器地址栏中找到view=后面的值
Note
如果返回显示禁止访问的话,说明你的飞书表格没有给应用开通权限,需要在更多-添加应用中添加此应用,并开通权限
这里我们使用的应用名是QualityAssurance-Customer-CD-QA
如果你搜多不到这个应用,说明你没有此应用的使用权限,找陈江或刚哥开通权限
"""
self.app_token = app_token # 表格token
self.table_id = table_id # 表格id
self.view_id = view_id # 表格视图id
self.app_id = 'cli_a53b456397bdd00c' # 飞书应用的app_id,使用前,多维表格需要在更多-添加应用中添加此应用,并开通权限
self.app_secret = 's3U4tXp9lTz7imMDQsHDNcoO4AgzXWk7' # 飞书应用的app_secret
self.fs_login_url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
self.http_session = requests.session()
self.tenant_access_token = ( # 以应用身份请求api的鉴权凭证每次获取后有效期为2小时
self.__get_tenant_access_token_by_feishu().get("tenant_access_token"))
self.http_session.headers.update({"Content-Type": "application/json; charset=utf-8",
"Authorization": f"Bearer {self.tenant_access_token}",
"User-Agent": "lark-api-explorer/v1"})
def __get_tenant_access_token_by_feishu(self):
"""功能获取飞书应用的tenant_access_token以应用身份请求api的鉴权凭证每次获取后有效期为2小时"""
headers = {'Content-Type': 'application/json; charset=utf-8'}
data = {'app_id': self.app_id, 'app_secret': self.app_secret}
r = requests.post(self.fs_login_url, data=json.dumps(data), headers=headers, verify=False)
r_json = r.json()
if r_json.get('code') == 0:
return {"expire": r_json.get('expire'), "tenant_access_token": r_json.get('tenant_access_token')}
else:
raise Exception(f"获取原始tenant_access_token失败{r_json}")
def fsmt_search_from_m_table(self, filter_dict=None, sort=None, automatic_fields=False, field_names=None):
"""
功能从多维表格中查询数据详细说明见https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search
Args:
filter_dict: 过滤条件类型list格式[{"conjunction": "string", "conditions": "list"]
conjunction: 过滤条件之间的关系取值范围and、or当仅有一个条件时也需要填写此字段
conditions: 过滤条件,格式:[{"field_name": "string", "operator": "string", "value": "list"}]
field_name: 筛选条件的左值值为字段的名称类型string
operator: 筛选条件的运算符,
取值范围is等于
isNot不等于
contains包含
doesNotContain不包含
isEmpty为空
isNotEmpty不为空
isGreater大于
isGreaterEqual大于等于
isLess小于
isLessEqual小于等于
likeLIKE 运算符。暂未支持
inIN 运算符。暂未支持
value: 筛选条件的右值值为字段的值类型list
若查询条件是时间格式,且为具体时间或日期时,格式为:["ExactDate", "13位的时间戳"]
同时也支持时间段查询,格式为:["具体的时间段描述字符"],如:
["Today"]:今天
["Tomorrow"]: 明天
["Yesterday"]:昨天
["CurrentWeek"]:本周
["LastWeek"]:上周
["CurrentMonth"]:本月
["LastMonth"]:上月
["TheLastWeek"]:过去七天内
["TheNextWeek"]:未来七天内
["TheLastMonth"]:过去三十天内
["TheNextMonth"]:未来三十天内
sort: 排序条件类型list格式[{"field_name": "string", "desc": "bool"}]
field_name: 排序字段的名称类型string
desc: 是否倒序排序类型bool, 默认值False
automatic_fields: 控制是否返回自动计算的字段, true 表示返回
field_names: 指定本次查询返回记录中包含哪些字段
Returns: {"code": 0, "msg": "",
"data": {"items": "入参中自定义的返回字段"
"has_more": "bool,是否还有更多项",
"page_token": "boolean, 分页标记,当 has_more 为 true 时,会同时返回新的 page_token否则不返回 page_token",
"total": "int, 总数"}}
"""
url = ("https://open.feishu.cn/open-apis/bitable/v1/apps/{}/tables/{}/records/search"
.format(self.app_token, self.table_id))
request_data = {"view_id": self.view_id,
"filter": filter_dict,
"automatic_fields": automatic_fields,
"sort": sort,
"field_names": field_names}
try:
rs = self.http_session.post(url, data=json.dumps(request_data), verify=False)
except Exception as e:
raise Exception(f"调用飞书接口{url}失败:{e}")
rj_json = rs.json()
if rj_json.get('code') == 0:
return rj_json
else:
raise Exception(f"调用飞书接口{url}失败:\n{rs.status_code}, {rs.text}")
def fsmt_insert_data_to_m_table(self, m_table_column_dict):
"""
https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_create
api文档地址https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/batch_create
"""
url = ("https://open.feishu.cn/open-apis/bitable/v1/apps/{}/tables/{}/records/batch_create"
.format(self.app_token, self.table_id))
request_data = {"records": []}
for column_dict in m_table_column_dict:
request_data["records"].append({"fields": column_dict})
try:
rs = self.http_session.post(url, data=json.dumps(request_data), verify=False)
except Exception as e:
raise Exception(f"调用飞书接口{url}失败:{e}")
rj_json = rs.json()
if rj_json.get('code') == 0:
return rj_json
else:
raise Exception(f"调用飞书接口{url}失败:\n{rs.status_code}, {rs.text}")
def fsmt_update_one_data_to_m_table(self, record_id, m_table_column_dict):
"""
https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/:record_id/update
api文档https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/update
"""
url = f"""https://open.feishu.cn/open-apis/bitable/v1/apps/{self.app_token}/tables/{self.table_id}/records/{record_id}"""
data = {"fields": m_table_column_dict}
logging.info(f"更新数据请求参数为:{data}")
rs = self.http_session.put(url, data=json.dumps(data), verify=False)
rj_json = rs.json()
if rj_json.get('code') == 0:
return rj_json
else:
logging.info(f"更新异常,异常返回结果为:{rs}")
def batch_update_data(self, table_id, update_records_data_list):
"""
https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_update
api文档https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/batch_update
"""
url = f"""https://open.feishu.cn/open-apis/bitable/v1/apps/{self.app_token}/tables/{table_id}/records/batch_update"""
data = {"records": update_records_data_list}
logging.info(f"批量更新数据请求参数为:{data}")
rs = self.http_session.post(url, data=json.dumps(data), verify=False)
rj_json = rs.json()
if rj_json.get('code') == 0:
return rj_json
else:
logging.info(f"批量更新异常,异常返回结果为:{rs}")
raise Exception(f"调用飞书接口{url}失败:{rs}")
def delete_data(self):
pass
if __name__ == '__main__':
fs = FeiShuMultidimensionalTableOperations(app_token="HssibyRf4anbpxsA3G2c1IPwnic",
table_id="tblVbNoZE1RI3Hdo",
view_id="vewx9lK9hI")
filter_dict = {"conjunction": "and",
"conditions": [{"field_name": "时间", "operator": "is", "value": ["Today"]}]}
sort = [{"field_name": "时间", "desc": True}]
field_names = ["时间", "入班", "换班", "补课", "月份"]
rsp = fs.fsmt_search_from_m_table(filter_dict=filter_dict, sort=sort, field_names=field_names)
if rsp.get("code") == 0:
if int(rsp.get("data").get("total")) == 0:
ci_data = [['2024-07-24', 7022, 2040, 105]]
request_data = []
for item in ci_data:
ci_date = obj_tool.get_format_date(r_time="{} 00:00:00".format(item[0]), r_type=13)
request_data.append({"时间": ci_date, "入班": item[1], "换班": item[2], "补课": item[3],
"月份": "{}".format(int(item[0].split("-")[1]))})
for item in request_data:
print(item)
# request_data = [{"时间": 1721664000000, "入班": 123, "换班": 124, "补课": 125, "月份": "7月"},
# {"时间": 1721750400000, "入班": 223, "换班": 224, "补课": 225, "月份": "7月"}]
rsp = fs.fsmt_insert_data_to_m_table(m_table_column_dict=request_data)
print(rsp)
else:
print("数据已存在,本次不写入...")
else:
print("查询失败:{}".format(rsp))