import pandas as pd import pandas as pd import numpy as np import requests import json import time import re from datetime import datetime from dateutil.relativedelta import relativedelta from pathlib import Path from urllib.parse import quote from io import BytesIO ROOT = Path('.').absolute() # 当前工作目录 def generateToken() -> str: """ 生成 token """ token_api = 'https://api.dingtalk.com/v1.0/oauth2/accessToken' # 该信息在钉钉开放应用中 data = { "appKey": "ding5kqocon5s9oph5uq", "appSecret": 'HL1jgsIIfLAC0eTH0A1m4mwxUDqbgsiPeCCGGE3ocM6qJBTIW7Ivt9drxF_Z4Kb_' } res = requests.post(token_api, json=data) token = res.json()['accessToken'] return token def read_processes_instances(token, formUuid, createFromTimeGMT, createToTimeGMT, page, n): """ 函数功能:读取流程表单的所有数据 """ api = f'https://api.dingtalk.com/v1.0/yida/processes/instances?pageNumber={page}&pageSize={n}' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": token } formData = { "appType": "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken": "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "userId": "yida_pub_account", # 超级管理员账号 "language": "zh_CN", "formUuid": formUuid, "createFromTimeGMT": createFromTimeGMT, "createToTimeGMT": createToTimeGMT, # "searchFieldJson": json.dumps(searchField), # 如果增加上这一项会要求升级宜搭存储 "instanceStatus": "RUNNING" } res = requests.post(api, headers=headers, json=formData) return res.json() def read_instances(token, formUuid, page, n): """ 函数功能:读取流程表单的所有数据 """ api = f'https://api.dingtalk.com//v1.0/yida/forms/instances/search?pageNumber={page}&pageSize={n}' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": token } formData = { "appType": "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken": "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "userId": "yida_pub_account", # 超级管理员账号 "language": "zh_CN", "formUuid": formUuid, # "searchFieldJson": json.dumps(searchField), # 如果增加上这一项会要求升级宜搭存储 "instanceStatus": "RUNNING" } res = requests.post(api, headers=headers, json=formData) return res.json() def timeStamp(timeNum): """ 函数功能:将时间戳(毫秒) 转化为时间日期格式""" timeStamp = float(timeNum/1000) timeArray = time.localtime(timeStamp) otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray) return otherStyleTime def get_time_range_minute(n): """ 获取近n分钟的时间戳(单位是毫秒)""" def delay_time(time_str, years=0, months=0, days=0, hours=0, minutes=0, seconds=0): if type(time_str) == str: time_str = datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S') ret = time_str + relativedelta(years=years, months=months, days=days, hours=hours, minutes=minutes, seconds=seconds) return ret # 获得当前时间 now_time = datetime.now() endTime = int(time.mktime(time.strptime(now_time.strftime('%Y/%m/%d %H:%M:%S'), '%Y/%m/%d %H:%M:%S'))) * 1000 - 1000 # n小时前的时间 ret2 = delay_time(now_time, minutes=-n) startTime = int(time.mktime(time.strptime(ret2.strftime('%Y/%m/%d %H:%M:%S'), '%Y/%m/%d %H:%M:%S'))) * 1000 # print(f'时间区间:[{startTime}-{endTime}]') return startTime, endTime def get_approval_records(token: str, processInstanceId: str): """ 函数功能:获取流程表单的审批记录 --F6客户服务 应用 """ appType = "APP_UYZ0KG6L0CCNV80GZ66O" systemToken = "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2" userId = "yida_pub_account" api = f'https://api.dingtalk.com/v1.0/yida/processes/operationRecords?appType={appType}&systemToken={systemToken}&userId={userId}&language=zh_CN&processInstanceId={processInstanceId}' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": token } res = requests.get(api, headers=headers) print('获取流程表单的审批记录') return res.json() def forms_isDone(TOKEN: str, FORMID: str, CREATE_FROM, CREATE_TO,PAGES,processInstanceId,isDone_id): ''' 返回isDone_id 对应的控件内容 ''' for i in range(1, PAGES+1): form_data = read_processes_instances(token=TOKEN, formUuid=FORMID, page=i, n=100) for data in form_data.get('data'): processInstanceId_1 = data.get('processInstanceId') if processInstanceId_1 == processInstanceId: isDone = data.get('data').get(isDone_id) return isDone def aggree_approval(token: str, taskId: str, processInstanceId: str, formData: dict): """ 函数功能:同意审批节点 --F6客户服务 应用 """ api = f'https://api.dingtalk.com/v1.0/yida/tasks/execute' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": token } payload = { "outResult": "AGREE", "appType": "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken": "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "remark": "同意(接口自动)", "formDataJson": json.dumps(formData, cls=NpEncoder), "processInstanceId": processInstanceId, # "userId": "yida_pub_account", "userId": "2268275546837446", "language": "zh_CN", "taskId": int(taskId) } res = requests.post(api, headers=headers, json=payload) print('同意审批节点') return res def switch(token: str, processInstanceId: str, formData: dict): """ 函数说明: 开关 程序调用时避免 重复进行(执行间隔 小于 单次执行时间时 发送 重复处理) """ api = f'https://api.dingtalk.com//v1.0/yida/forms/instances' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": token } payload = { "appType" : "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken" : "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "userId" : "yida_pub_account", # 管理员 "language" : "zh_CN", "useLatestVersion" : "false", "formInstanceId" : processInstanceId, "updateFormDataJson" : json.dumps(formData, cls=NpEncoder), } res = requests.put(api, headers=headers, json=payload) print('开关 程序调用时避免 重复进行(执行间隔 小于 单次执行时间时 发送 重复处理)') def update_form_staff(TOKEN: str,processInstanceId,staff_id,ModifiedField_value): """ 函数功能:员工更新表单内容 """ api = f'https://api.dingtalk.com//v1.0/yida/forms/instances' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": TOKEN } data_new = { ModifiedField_value:[staff_id] } payload = { "appType" : "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken" : "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "userId" : "yida_pub_account", # 曹伟 id "language" : "zh_CN", "useLatestVersion" : "false", "formInstanceId" : processInstanceId, "updateFormDataJson" : json.dumps(data_new, cls=NpEncoder), } res = requests.put(api, headers=headers, json=payload) print('员工更新表单内容') return res def get_staffID(TOKEN: str,staff_name): """ 函数功能:通过员工名称获取员工id""" # 读取员工对应关系:宜搭员工-ID对应表 TOKEN = generateToken() FORMID = "FORM-EA866E715PF9YA7ECCAGSABX91Q72PVA3WRFL6" # 宜搭员工-ID对应表 FORM-EA866E715PF9YA7ECCAGSABX91Q72PVA3WRFL6 # 读取流程表单数据 form_data = read_instances(token=TOKEN, formUuid=FORMID, page=1, n=100) PAGES = form_data.get('totalCount')//100 + 1 ALL_DATA_staff = [] """ 获取全量数据 """ for i in range(1, PAGES+1): # form_data = read_processes_instances(token=TOKEN, formUuid=FORMID, createFromTimeGMT=CREATE_FROM, createToTimeGMT=CREATE_TO, page=i, n=100, searchField={'textField_l7if5ff9': '否'}) form_data = read_instances(token=TOKEN, formUuid=FORMID, page=i, n=100) for data in form_data.get('data'): ALL_DATA_staff.append(data) res_new = [v['formData']['textField_lfrw3u59'] for v in ALL_DATA_staff if v['formData']['textField_lfrw3u58']== staff_name] print('通过员工名称获取员工id') return res_new def get_type(TOKEN: str,ModifiedField_value,Form_id): """ 函数功能:通过唯一标识获取控件类型""" api = f'https://api.dingtalk.com/v1.0/yida/forms/formFields?appType=APP_UYZ0KG6L0CCNV80GZ66O&systemToken=XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2&formUuid='+Form_id+'&userId=yida_pub_account' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": TOKEN } res = requests.get(api, headers=headers) cnew = res.json().get('result') componentName = [i.get('componentName') for i in cnew if i.get('fieldId')== ModifiedField_value ] print('通过唯一标识获取控件类型') return componentName def update_text(TOKEN: str, processInstanceId, formData): """ 函数说明:更新单行文本控件内容 """ api = f'https://api.dingtalk.com//v1.0/yida/forms/instances' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": TOKEN } payload = { "appType" : "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken" : "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "userId" : "yida_pub_account", # 管理员 "language" : "zh_CN", "useLatestVersion" : "false", "formInstanceId" : processInstanceId, "updateFormDataJson" : json.dumps(formData, cls=NpEncoder), } res = requests.put(api, headers=headers, json=payload) print('更新单行文本控件内容') return res def read_instances_V2(token, formUuid, createFromTimeGMT, createToTimeGMT, page, n): """ 函数功能:读取流程表单的所有数据 """ api = f'https://api.dingtalk.com//v1.0/yida/forms/instances/search' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": token } formData = { "appType": "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken": "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "userId": "yida_pub_account", # 超级管理员账号 "language": "zh_CN", "formUuid": formUuid, 'pageNumber':page, 'pageSize':n, # "searchFieldJson": json.dumps(searchField), # 如果增加上这一项会要求升级宜搭存储 "instanceStatus": "RUNNING" } res = requests.post(api, headers=headers, json=formData) return res.json() def pass_on(TOKEN: str,taskId, processInstanceId, staffid): """ 函数功能:转交审批节点 """ api = f'https://api.dingtalk.com//v1.0/yida/tasks/redirect' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": TOKEN } payload = { "processInstanceId" : processInstanceId, "byManager" : "y", "appType" : "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken" : "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "language": "zh_CN", "remark" : "转交(接口自动)", "nowActionExecutorId" : staffid, "userId" : "2268275546837446", "taskId" : int(taskId) } res = requests.post(api, headers=headers, json=payload) print('转交审批节点') return res def read_instances_new(token, formUuid, createFromTimeGMT, createToTimeGMT, page, n): """ 函数功能:读取流程表单的所有数据 """ api = f'https://api.dingtalk.com/v1.0/yida/forms/instances/search' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": token } formData = { "appType": "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken": "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "userId": "yida_pub_account", # 超级管理员账号 "language": "zh_CN", "formUuid": formUuid, # "searchFieldJson": json.dumps(searchField), # 如果增加上这一项会要求升级宜搭存储 'currentPage':page, 'pageSize':n } res = requests.post(api, headers=headers, json=formData) return res.json() def read_instances11111(token, formUuid, page, n): """ 函数功能:读取普通表单的所有数据 """ api = f'https://api.dingtalk.com//v1.0/yida/forms/instances/search' headers = { "Content-Type": "application/json", "x-acs-dingtalk-access-token": token } formData = { "appType" : "APP_UYZ0KG6L0CCNV80GZ66O", "systemToken" : "XA966F81JAJOFCVVVKO64E9MIIZV1EWE5SFMKJ2", "userId" : "yida_pub_account", "language" : "zh_CN", "formUuid" : formUuid, "currentPage" : page, "pageSize" : n } res = requests.post(api, headers=headers, json=formData) return res.json() class NpEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, np.integer): return int(obj) elif isinstance(obj, np.floating): return float(obj) elif isinstance(obj, np.ndarray): return obj.tolist() else: return super(NpEncoder, self).default(obj) """ 处理流程开始 """ TOKEN = generateToken() FORMID = "FORM-RK966E71EQS9FTL1FLA7VBG5Y5RY2A32X2DGLK1" # 发起批量修改需求 FORMID2 = "FORM-J7966ZA1RTU9UG2Y7SCQRB00OXJA3JX0U2DGL0" # 修改明细表 # 10分钟时间间隔 CREATE_FROM, CREATE_TO = [timeStamp(t) for t in get_time_range_minute(100000)] # 读取流程表单数据 form_data = read_processes_instances(token=TOKEN, formUuid=FORMID, createFromTimeGMT=CREATE_FROM, createToTimeGMT=CREATE_TO, page=1, n=100) PAGES = form_data.get('totalCount')//100 + 1 ALL_DATA = [] """ 获取全量数据 """ for i in range(1, PAGES+1): # form_data = read_processes_instances(token=TOKEN, formUuid=FORMID, createFromTimeGMT=CREATE_FROM, createToTimeGMT=CREATE_TO, page=i, n=100, searchField={'textField_l7if5ff9': '否'}) form_data = read_processes_instances(token=TOKEN, formUuid=FORMID, createFromTimeGMT=CREATE_FROM, createToTimeGMT=CREATE_TO, page=i, n=100) for data in form_data.get('data'): ALL_DATA.append(data) print(f'读取到流程表单中 {len(ALL_DATA)} 条数据!') for data in ALL_DATA: processInstanceId = data.get('processInstanceId') USEWRID = data.get('originator').get('userId') print('USEWRID:',USEWRID) switch_value = data.get('data').get('textField_lexoznc4') # 状态 if switch_value == "待处理": TOKEN = generateToken() '''读取员工对应关系:宜搭员工-ID对应表 ''' FORMID = "FORM-EA866E715PF9YA7ECCAGSABX91Q72PVA3WRFL6" # 宜搭员工-ID对应表 FORM-EA866E715PF9YA7ECCAGSABX91Q72PVA3WRFL6 # 读取流程表单数据 form_data = read_instances11111(token=TOKEN, formUuid=FORMID, page=1, n=100) PAGES = form_data.get('totalCount')//10 + 1 ALL_DATA_staff = {} """ 获取全量数据 """ for i in range(1, PAGES+1): # form_data = read_processes_instances(token=TOKEN, formUuid=FORMID, createFromTimeGMT=CREATE_FROM, createToTimeGMT=CREATE_TO, page=i, n=100, searchField={'textField_l7if5ff9': '否'}) form_data = read_instances11111(token=TOKEN, formUuid=FORMID, page=i, n=100) for data11 in form_data.get('data'): ALL_DATA_staff[data11['formData']['textField_lfrw3u58']]=data11['formData']['textField_lfrw3u59'] print(f'读取到宜搭员工-ID对应表表单中 {len(ALL_DATA_staff)} 条数据!') # 状态——修改为:处理中 formData = {'textField_lexoznc4':'处理中'} switch(TOKEN,processInstanceId,formData) # 获取表单名称、表单id、功能选择、修改字段名称、关联编码 Form_value = data.get('data').get('selectField_lexoznbw') # 表单名称 Form_id = data.get('data').get('selectField_lexoznbw_id') # 表单id Function_value = data.get('data').get('selectField_lexoznbx') # 功能选择 ModifiedField_value = data.get('data').get('textField_lexoznc5') # 修改字段唯一标识符 Relevancy_value = data.get('data').get('textField_lexoznby') # 关联编码 print(Form_value,Form_id,Function_value,ModifiedField_value,Relevancy_value) # 读取修改明细表全量数据 form_data = read_instances_V2(token=TOKEN, formUuid=FORMID2, createFromTimeGMT=CREATE_FROM, createToTimeGMT=CREATE_TO, page=1, n=100) PAGES = form_data.get('totalCount')//100 + 1 LIST_DATA = [] """ 获取全量数据 """ for i in range(1, PAGES+1): # form_data = read_processes_instances(token=TOKEN, formUuid=FORMID, createFromTimeGMT=CREATE_FROM, createToTimeGMT=CREATE_TO, page=i, n=100, searchField={'textField_l7if5ff9': '否'}) form_data = read_instances_new(token=TOKEN, formUuid=FORMID2, createFromTimeGMT=CREATE_FROM, createToTimeGMT=CREATE_TO, page=i, n=100) for v in range(0,len(form_data.get('data'))): LIST_DATA.append(form_data.get('data')[v]) if len(LIST_DATA)>0: for data_amend in LIST_DATA: try: # 遍历:筛选出关联编码 = Relevancy_value 状态为未处理 if Relevancy_value == data_amend.get('formData').get('textField_lez31l31') and data_amend.get('formData').get('textField_lexootfr') == "未处理": # 获取明细表单数据 revise_name = [i.get('fieldData') for i in json.loads(data_amend.get('instanceValue')) if i.get('fieldId')== 'textField_lexootfo' ][0]['value'] # 修改内容 textField_lexootfo example_id = [i.get('fieldData') for i in json.loads(data_amend.get('instanceValue')) if i.get('fieldId')== 'textField_lexootfn' ][0]['value'] # 实例id textField_lexootfn state_name = [i.get('fieldData') for i in json.loads(data_amend.get('instanceValue')) if i.get('fieldId')== 'textField_lexootfr' ][0]['value'] # 状态(默认:未处理) textField_lexootfr formInstanceId = data_amend.get('formInstanceId') # 修改明细表 实例id # 判断 功能选择———————————————————————————————————————————————————————————————————— if Function_value == "修改内容": # 判断需要修改的类型 componentName_new = get_type(TOKEN,ModifiedField_value,Form_id) if componentName_new ==['EmployeeField']: # 成员字段 staffid = ALL_DATA_staff[revise_name] # 获取员工id update_form_staff(TOKEN,example_id,staffid,ModifiedField_value) # 修改 实例id textField_lexootfn 对应的内容 # formData = {'textField_lexoznc4':'已处理'} # update_text(TOKEN, processInstanceId, formData) # 发起批量修改需求_状态为_已处理 formData = {'textField_lexootfr':'已处理'} update_text(TOKEN, formInstanceId, formData) # 修改明细_状态为_已处理 else: formData = {ModifiedField_value:revise_name} update_text(TOKEN, example_id, formData) # 修改 实例id textField_lexootfn 对应的内容 # formData = {'textField_lexoznc4':'已处理'} # update_text(TOKEN, processInstanceId, formData) # 发起批量修改需求_状态为_已处理 formData = {'textField_lexootfr':'已处理'} update_text(TOKEN, formInstanceId, formData) # 修改明细_状态为_已处理 elif Function_value == "转交": # 判断需要修改的类型 componentName_new = get_type(TOKEN,ModifiedField_value,Form_id) if componentName_new ==['EmployeeField']: # 成员字段 staffid = ALL_DATA_staff[revise_name] # 获取员工id # 获取审批记录 res_new = get_approval_records(token=TOKEN, processInstanceId=example_id) records_new = res_new.get('result') # 获取审批节点的 taskId records_new = [item for item in records_new if item.get('type') == "TODO"] taskId_new = records_new[0].get('taskId') pass_on(TOKEN=TOKEN, taskId=taskId_new, processInstanceId=example_id, staffid=staffid) # 修改明细表和需求表 formData = {'textField_lexootfr':'已处理'} update_text(TOKEN, formInstanceId, formData) # 修改明细_状态为_已处理 elif Function_value == "修改内容并转交": # 判断需要修改的类型 componentName_new = get_type(TOKEN,ModifiedField_value,Form_id) if componentName_new ==['EmployeeField']: # 成员字段 staffid = ALL_DATA_staff[revise_name] # 获取员工id update_form_staff(TOKEN,example_id,staffid,ModifiedField_value) # 修改 实例id textField_lexootfn 对应的内容 下面再进行转交 # 获取审批记录 res_new = get_approval_records(token=TOKEN, processInstanceId=example_id) records_new = res_new.get('result') # 获取审批节点的 taskId records_new = [item for item in records_new if item.get('type') == "TODO"] taskId_new = records_new[0].get('taskId') pass_on(TOKEN=TOKEN, taskId=taskId_new, processInstanceId=example_id, staffid=staffid) # 修改明细表和需求表 formData = {'textField_lexootfr':'已处理'} update_text(TOKEN, formInstanceId, formData) # 修改明细_状态为_已处理 except: example_id = [i.get('fieldData') for i in json.loads(data_amend.get('instanceValue')) if i.get('fieldId')== 'textField_lexootfn' ][0]['value'] # 实例id textField_lexootfn print('错误:',example_id) # 同意流程 + 发起批量修改需求_状态为_已处理 res_new = get_approval_records(token=TOKEN, processInstanceId=processInstanceId) records_new = res_new.get('result') # 获取审批节点的 taskId records_new = [item for item in records_new if item.get('type') == "TODO"] taskId_new = records_new[0].get('taskId') data_new = {'textField_lexoznc4':'已处理'} aggree_approval(token=TOKEN, taskId=taskId_new, processInstanceId=processInstanceId, formData=data_new)