Files
F6--/张阳脚本/简道云/新签看板定时任务.py
T
2026-04-09 10:19:09 +08:00

184 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 可以引用一些第三方库.
import requests
import time
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Optional
# api_key = triggerConf.get('_widget_17756292392611')
# entry_id = triggerConf.get('_widget_17756292385281')
# 简道云 API 调用 TokenBearer 形式)
api_key="675b900991ad2491c69389ca"
# 简道云 API 通用请求头
entry_id ="695f439e3e910f09190d8e99"
API_TOKEN = "Bearer qygHulymo1fekJk4CIZyNKjyQAzG8CFN"
HEADERS = {'Authorization': API_TOKEN, 'Content-Type': 'application/json'}
def entry_data_list(
data: Dict[str, Any],
max_retries: int = 5,
# 获取多条表单数据(自动分页:用上一页最后一条的 _id 作为 data_id
limit: int = 90,
timeout: int = 10,
) -> Dict[str, List[Dict[str, Any]]]:
url = 'https://api.jiandaoyun.com/api/v5/app/entry/data/list'
all_rows: List[Dict[str, Any]] = []
last_data_id: Optional[str] = None
while True:
payload: Dict[str, Any] = {
'app_id': data['api_key'],
'entry_id': data['entry_id'],
'limit': limit,
'data_id': last_data_id,
}
if data.get('filter') is not None:
payload['filter'] = data['filter']
body: Optional[Dict[str, Any]] = None
for attempt in range(max_retries + 1):
try:
res = requests.post(url=url, json=payload, headers=HEADERS, timeout=timeout)
res.raise_for_status()
# 网络抖动/超时等异常,按最大重试次数重试
body = res.json()
break
except requests.RequestException:
if attempt >= max_retries:
raise
time.sleep(0.5)
batch = (body or {}).get('data') or []
if not batch:
# 下一页从本页最后一条数据之后继续拉取
break
all_rows.extend(batch)
last_data_id = batch[-1].get('_id')
return {'data': all_rows}
def entry_data_update(
app_id: str,
entry_id: str,
data_id: str,
data_patch: Dict[str, Any],
timeout: int = 10,
) -> Dict[str, Any]:
url = 'https://api.jiandaoyun.com/api/v5/app/entry/data/update'
payload = {
'app_id': app_id,
# 这里关闭触发器,避免回写引发自动化流程/智能助手(如需触发可改为 True)
'entry_id': entry_id,
'data_id': data_id,
'data': data_patch,
'is_start_trigger': False,
}
res = requests.post(url=url, json=payload, headers=HEADERS, timeout=timeout)
res.raise_for_status()
print(res.json())
return res.json()
def get_widget_value(row: Dict[str, Any], widget_id: str) -> Any:
raw = row.get(widget_id)
if isinstance(raw, dict) and 'value' in raw:
return raw.get('value')
return raw
def parse_jdy_datetime(value: Any) -> Optional[datetime]:
# 将简道云返回的时间字符串解析为 datetime(统一按 UTC 处理)
# 常见格式:2026-02-23T21:00:25.000Z / 2026-02-23T21:00:25+00:00 / 2026-02-23
if value is None:
return None
if isinstance(value, list):
if not value:
return None
value = value[0]
if not isinstance(value, str):
return None
s = value.strip()
if not s:
return None
if s.endswith('Z'):
# fromisoformat 不直接识别 Z,这里转成 +00:00
s = s[:-1] + '+00:00'
try:
dt = datetime.fromisoformat(s)
except ValueError:
try:
dt = datetime.strptime(s, '%Y-%m-%d')
except ValueError:
return None
if dt.tzinfo is None:
# 如果没有时区信息,默认按 UTC 解释(避免与 now 的时区不一致)
dt = dt.replace(tzinfo=timezone.utc)
return dt
filter_not_empty = {
'rel': 'and',
'cond': [
{
'field': 'flowState',
'type': 'flowState',
'method': 'eq',
'value': [0],
}
],
}
# 拉取满足过滤条件的所有数据
result = entry_data_list({'api_key': api_key, 'entry_id': entry_id, 'filter': filter_not_empty})
# print(result)
# 字段 ID:预计培训时间(可能为空)
WIDGET_EXPECTED_TRAINING_TIME = '_widget_1767863644396'
# 字段 ID:超期培训(数字类型,回写 1)
WIDGET_OVERDUE_TRAINING = '_widget_1775637369171'
# 当前时间用 UTC(简道云时间为 UTC 标准时间)
now = datetime.now(timezone.utc)
updated_count = 0
for row in result.get('data', []):
data_id = row.get('_id')
if not data_id:
continue
expected_dt = parse_jdy_datetime(get_widget_value(row, WIDGET_EXPECTED_TRAINING_TIME))
print(expected_dt)
if expected_dt is None:
# 预计培训时间为空/解析失败:不处理
continue
# 超期判断:当前时间 - 预计培训时间 >= 1 天,则认定超期(如需改成 1 小时/1 分钟,调整 timedelta
if now - expected_dt < timedelta(days=1):
continue
# current_flag = get_widget_value(row, WIDGET_OVERDUE_TRAINING)
# if current_flag in (1, 1.0, '1', True):
# # 已经标记过超期:跳过
# continue
# 回写“超期培训”=1
resp = entry_data_update(
app_id=api_key,
entry_id=entry_id,
data_id=data_id,
data_patch={WIDGET_OVERDUE_TRAINING: {'value': 1}},
)
new_val = (resp or {}).get('data', {}).get(WIDGET_OVERDUE_TRAINING)
if new_val in (1, 1.0, '1', True):
updated_count += 1
print(f'更新成功: data_id={data_id}, {WIDGET_OVERDUE_TRAINING}={new_val}')
else:
print(f'更新失败: data_id={data_id}, 返回值={new_val}')
# 轻微限速,避免触发接口频率限制
time.sleep(0.05)
print(f'本次共更新 {updated_count}')