# 可以引用一些第三方库. 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 调用 Token(Bearer 形式) 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} 条')