为了实现工单系统,又不想自己去结合钉钉的组织架构实现一套审批流,所以采用钉钉的审批结合自己的系统去实现工单。
由钉钉实现审批流,软件实现查询,记录工单等。
在上一篇中已经实现了审批流程以及回调函数的实现和整个工单的逻辑,下面描述一下数据库的设计。
主要分为两个表去实现:
1. 工单的种类表,主要用于存储钉钉的工单种类或者想要用于操作的工单种类,即不是所有的工单都需要做记录操作。
表名:ticket_type
字段 | 字段类型 | 描述 |
id | int(11) | 自增id,主键 |
process_code | varchar(64) | 钉钉工单code码 |
name | varchar(64) | 工单名称 |
url | varchar(256) | 工单钉钉的申请页面地址 |
icon_url | tinyint(1) | 图标地址 |
create_time | datetime | 创建时间 |
update_time | datetime | 修改时间 |
上述这些字段是钉钉的工单类型api会提供的,下面方法获取数据:
def getTicketType(): Client = SecretClient(CORPID, SECRET) #dingtalk连接 #用一个userid去获取这个userid的所有工单,如果工单很多,可以遍历执行,这里只有获取100个 req = Client.bpms.process_listbyuserid(USERID, 0, 100) process_top_vo = req['process_list']['process_top_vo'] for p in process_top_vo: icon_url = p.get('icon_url', '') name = p.get('name', '') process_code = p.get('process_code', '') url = p.get('url', '') ticketType = TicketType.objects.filter(process_code=process_code) if ticketType: ticketType.update(icon_url=icon_url, name=name, url=url, update_time=datetime.datetime.now()) else: TicketType.objects.create(process_code=process_code, icon_url=icon_url, name=name, url=url, type='', update_time=datetime.datetime.now())
2. 工单记录表,主要用于存储工单记录,通过钉钉回调过来的数据,然后记录在ticket_type里面的工单。
表名:ticket_record
字段 | 字段类型 | 描述 |
id | int(11) | 自增id,主键 |
process_code | varchar(64) | 钉钉工单code码(ticket_type) |
process_instance_id | varchar(64) | 工单的唯一编号 |
business_id | varchar(32) | 审批实例业务编号 |
title | varchar(64) | 审批实例标题 |
operation_records | longtext | 操作记录列表 |
originator_dept_id | varchar(32) | 发起的部门(钉钉部门) |
originator_userid | varchar(64) | 发起人(userid) |
form_component_values | longtext | 表单详情列表 |
status | varchar(32) | 审批状态 |
result | varchar(32) | 审批结果,分为 agree 和 refuse |
cc_userids | varchar(1000) | 抄送人 |
participant | varchar(64) | 当前处理人(userid) |
relation | varchar(1000) | 关联人(记录哪些人操作过) |
tasks | longtext | 已审批任务列表,可以通过此列表获取已审批人 |
is_operation | tinyint | 是否操作(用于后续自动化操作的记录) |
create_time | datetime | 创建时间 |
update_time | datetime | 修改时间 |
这个通过钉钉回调过来的信息,然后通过钉钉的api去获取钉钉的工单实例详情:
请求方式:POST(HTTPS)
请求地址:https://oapi.dingtalk.com/topapi/processinstance/get?access_token=ACCESS_TOKEN
参数说明
名称 | 类型 | 是否必须 | 示例值 | 描述 |
---|---|---|---|---|
process_instance_id | String | 必须 | 1a2b-3e4d | 审批实例id |
def getTicketRecord(process_instance_id):#此参数可以用钉钉的回调中获取 ''' 获取工单详情 :param process_instance_id: 工单的id :return: ''' url = 'https://oapi.dingtalk.com/topapi/processinstance/get?access_token=' + getToken() data = { 'process_instance_id': process_instance_id } #获取工单的详情 req = requests.post(url, data=json.dumps(data)) content = json.loads(req.text)['process_instance'] originator_userid = content.get('originator_userid') business_id = content.get('business_id') title = content.get('title') operation_records = content.get('operation_records') originator_dept_id = content.get('originator_dept_id') form_component_values = content.get('form_component_values') tasks = content.get('tasks') status = content.get('status') result = content.get('result') cc_userids = content.get('cc_userids') relation = [] participant = '' for task in tasks: userid = str(task.get('userid', '')) if task.get('task_result') == 'NONE' and task.get('task_status') == 'RUNNING': #如果是这个状态的值,那么就是当前需要操作的 if not participant: participant += userid else: participant = participant + ',' + userid if userid: if userid not in relation: relation.append(userid) #所有做过操作的人,用于展示界面上相关的工单列表 if originator_userid not in relation: relation.append(originator_userid) relation = ','.join(relation) participant = participant
将上述的值存到数据库中即可。
综合,完成一个所有的钉钉流程:
1. 钉钉填写工单:回调到工单系统,工单系统记录下操作。
2. 工单状态的改变:回调到工单系统,工单系统记录下操作。
3. 判断工单是否结束,结束了之后,是否要进行自动化处理,自动化处理要根据具体的项目来实现,达到一个完整的工单,人工只需要进行审批,所有的操作都是自动化。
目前只做了简单的几个自动化,在回调的时候判断函数:
#满足条件进行自动的操作 if status == 'COMPLETED' and result == 'agree':#判断是否结束,且是同意的 t = TicketRecord.objects.filter(process_instance_id=processInstanceId) 获取工单详情 typeTicket = '' try: ticket = TicketType.objects.get(process_code=process_code) process_code = ticket.process_code #此处加了 except Exception as e: print(e) if t and not t[0].is_operation:#判断没有进行操作过则进行自动化操作 if process_code == '12345698765432876543': #判断要进行哪种自动化 res, msg = attach(processInstanceId)#自动化函数,此处可以用celery异步操作。
回调的时候说一下一个容易出错的地方:
'bpms_task_change', 'bpms_instance_change'这两个回调在创建等操作的时候会同时回调回来,这个时候就要注意数据库的操作,避免写入相同的数,避免进行多次操作,需要自己去把控。