def workflowsdetail(request, audit_id): # 按照不同的workflow_type返回不同的详情 audit_detail = Audit.detail(audit_id) if audit_detail.workflow_type == WorkflowDict.workflow_type['query']: return HttpResponseRedirect(reverse('sql:queryapplydetail', args=(audit_detail.workflow_id,))) elif audit_detail.workflow_type == WorkflowDict.workflow_type['sqlreview']: return HttpResponseRedirect(reverse('sql:detail', args=(audit_detail.workflow_id,)))
def queryprivaudit(request): # 获取用户信息 user = request.user apply_id = int(request.POST['apply_id']) audit_status = int(request.POST['audit_status']) audit_remark = request.POST.get('audit_remark') if audit_remark is None: audit_remark = '' if Audit.can_review(request.user, apply_id, 1) is False: context = {'errMsg': '你无权操作当前工单!'} return render(request, 'error.html', context) # 使用事务保持数据一致性 try: with transaction.atomic(): audit_id = Audit.detail_by_workflow_id( workflow_id=apply_id, workflow_type=WorkflowDict.workflow_type['query']).audit_id # 调用工作流接口审核 audit_result = Audit.audit(audit_id, audit_status, user.username, audit_remark) # 按照审核结果更新业务表审核状态 audit_detail = Audit.detail(audit_id) if audit_detail.workflow_type == WorkflowDict.workflow_type[ 'query']: # 更新业务表审核状态,插入权限信息 query_audit_call_back(audit_detail.workflow_id, audit_result['data']['workflow_status']) except Exception as msg: logger.error(traceback.format_exc()) context = {'errMsg': msg} return render(request, 'error.html', context) else: # 消息通知 sys_config = SysConfig() if sys_config.get('mail') or sys_config.get('ding'): # 再次获取审核信息 audit_detail = Audit.detail_by_workflow_id( workflow_id=apply_id, workflow_type=WorkflowDict.workflow_type['query']) base_url = sys_config.get('archery_base_url', 'http://127.0.0.1:8000').rstrip('/') workflow_url = "{base_url}/workflow/{audit_id}".format( base_url=base_url, audit_id=audit_detail.audit_id) async_task(notify, audit_info=audit_detail, workflow_url=workflow_url, audit_remark=audit_remark, timeout=60) return HttpResponseRedirect( reverse('sql:queryapplydetail', args=(apply_id, )))
def query_priv_audit(request): """ 查询权限审核 :param request: :return: """ # 获取用户信息 user = request.user apply_id = int(request.POST['apply_id']) audit_status = int(request.POST['audit_status']) audit_remark = request.POST.get('audit_remark') if audit_remark is None: audit_remark = '' if Audit.can_review(request.user, apply_id, 1) is False: context = {'errMsg': '你无权操作当前工单!'} return render(request, 'error.html', context) # 使用事务保持数据一致性 try: with transaction.atomic(): audit_id = Audit.detail_by_workflow_id( workflow_id=apply_id, workflow_type=WorkflowDict.workflow_type['query']).audit_id # 调用工作流接口审核 audit_result = Audit.audit(audit_id, audit_status, user.username, audit_remark) # 按照审核结果更新业务表审核状态 audit_detail = Audit.detail(audit_id) if audit_detail.workflow_type == WorkflowDict.workflow_type[ 'query']: # 更新业务表审核状态,插入权限信息 _query_apply_audit_call_back( audit_detail.workflow_id, audit_result['data']['workflow_status']) except Exception as msg: logger.error(traceback.format_exc()) context = {'errMsg': msg} return render(request, 'error.html', context) else: # 消息通知 async_task(notify_for_audit, audit_id=audit_id, audit_remark=audit_remark, timeout=60, task_name=f'query-priv-audit-{apply_id}') return HttpResponseRedirect( reverse('sql:queryapplydetail', args=(apply_id, )))
def workflowsdetail(request, audit_id): """待办详情""" # 按照不同的workflow_type返回不同的详情 audit_detail = Audit.detail(audit_id) if audit_detail.workflow_type == WorkflowDict.workflow_type['query']: return HttpResponseRedirect( reverse('sql:queryapplydetail', args=(audit_detail.workflow_id, ))) elif audit_detail.workflow_type == WorkflowDict.workflow_type['sqlreview']: workflow = SqlWorkflow.objects.get(id=audit_detail.workflow_id) if workflow.order_type == 'sqlcron_order': return HttpResponseRedirect( reverse('sql:sqlcrondetail', args=(audit_detail.workflow_id, ))) return HttpResponseRedirect( reverse('sql:detail', args=(audit_detail.workflow_id, )))
def notify_for_audit(audit_id, **kwargs): """ 工作流消息通知,不包含工单执行结束的通知 :param audit_id: :param kwargs: :return: """ # 判断是否开启消息通知,未开启直接返回 if not __notify_cnf_status(): return None sys_config = SysConfig() # 获取审核信息 audit_detail = Audit.detail(audit_id=audit_id) audit_id = audit_detail.audit_id workflow_audit_remark = kwargs.get('audit_remark', '') base_url = sys_config.get('archery_base_url', 'http://127.0.0.1:8000').rstrip('/') workflow_url = "{base_url}/workflow/{audit_id}".format(base_url=base_url, audit_id=audit_detail.audit_id) workflow_id = audit_detail.workflow_id workflow_type = audit_detail.workflow_type status = audit_detail.current_status workflow_title = audit_detail.workflow_title workflow_from = audit_detail.create_user_display group_name = audit_detail.group_name dingding_webhook = ResourceGroup.objects.get(group_id=audit_detail.group_id).ding_webhook feishu_webhook = ResourceGroup.objects.get(group_id=audit_detail.group_id).feishu_webhook qywx_webhook = ResourceGroup.objects.get(group_id=audit_detail.group_id).qywx_webhook # 获取当前审批和审批流程 workflow_auditors, current_workflow_auditors = Audit.review_info(audit_detail.workflow_id, audit_detail.workflow_type) # 准备消息内容 if workflow_type == WorkflowDict.workflow_type['query']: workflow_type_display = WorkflowDict.workflow_type['query_display'] workflow_detail = QueryPrivilegesApply.objects.get(apply_id=workflow_id) instance = workflow_detail.instance.instance_name db_name = ' ' if workflow_detail.priv_type == 1: workflow_content = '''数据库清单:{}\n授权截止时间:{}\n结果集:{}\n'''.format( workflow_detail.db_list, datetime.datetime.strftime(workflow_detail.valid_date, '%Y-%m-%d %H:%M:%S'), workflow_detail.limit_num) elif workflow_detail.priv_type == 2: db_name = workflow_detail.db_list workflow_content = '''数据库:{}\n表清单:{}\n授权截止时间:{}\n结果集:{}\n'''.format( workflow_detail.db_list, workflow_detail.table_list, datetime.datetime.strftime(workflow_detail.valid_date, '%Y-%m-%d %H:%M:%S'), workflow_detail.limit_num) else: workflow_content = '' elif workflow_type == WorkflowDict.workflow_type['sqlreview']: workflow_type_display = WorkflowDict.workflow_type['sqlreview_display'] workflow_detail = SqlWorkflow.objects.get(pk=workflow_id) instance = workflow_detail.instance.instance_name db_name = workflow_detail.db_name workflow_content = re.sub('[\r\n\f]{2,}', '\n', workflow_detail.sqlworkflowcontent.sql_content[0:500].replace('\r', '')) elif workflow_type == WorkflowDict.workflow_type['archive']: workflow_type_display = WorkflowDict.workflow_type['archive_display'] workflow_detail = ArchiveConfig.objects.get(pk=workflow_id) instance = workflow_detail.src_instance.instance_name db_name = workflow_detail.src_db_name workflow_content = '''归档表:{}\n归档模式:{}\n归档条件:{}\n'''.format( workflow_detail.src_table_name, workflow_detail.mode, workflow_detail.condition) else: raise Exception('工单类型不正确') # 准备消息格式 if status == WorkflowDict.workflow_status['audit_wait']: # 申请阶段 msg_title = "[{}]新的工单申请#{}".format(workflow_type_display, audit_id) # 接收人,发送给该资源组内对应权限组所有的用户 auth_group_names = Group.objects.get(id=audit_detail.current_audit).name msg_to = auth_group_users([auth_group_names], audit_detail.group_id) msg_cc = Users.objects.filter(username__in=kwargs.get('cc_users', [])) # 消息内容 msg_content = '''发起时间:{}\n发起人:{}\n组:{}\n目标实例:{}\n数据库:{}\n审批流程:{}\n当前审批:{}\n工单名称:{}\n工单地址:{}\n工单详情预览:{}\n'''.format( workflow_detail.create_time.strftime('%Y-%m-%d %H:%M:%S'), workflow_from, group_name, instance, db_name, workflow_auditors, current_workflow_auditors, workflow_title, workflow_url, workflow_content) elif status == WorkflowDict.workflow_status['audit_success']: # 审核通过 msg_title = "[{}]工单审核通过#{}".format(workflow_type_display, audit_id) # 接收人,仅发送给申请人 msg_to = [Users.objects.get(username=audit_detail.create_user)] msg_cc = Users.objects.filter(username__in=kwargs.get('cc_users', [])) # 消息内容 msg_content = '''发起时间:{}\n发起人:{}\n组:{}\n目标实例:{}\n数据库:{}\n审批流程:{}\n工单名称:{}\n工单地址:{}\n工单详情预览:{}\n'''.format( workflow_detail.create_time.strftime('%Y-%m-%d %H:%M:%S'), workflow_from, group_name, instance, db_name, workflow_auditors, workflow_title, workflow_url, workflow_content) elif status == WorkflowDict.workflow_status['audit_reject']: # 审核驳回 msg_title = "[{}]工单被驳回#{}".format(workflow_type_display, audit_id) # 接收人,仅发送给申请人 msg_to = [Users.objects.get(username=audit_detail.create_user)] msg_cc = Users.objects.filter(username__in=kwargs.get('cc_users', [])) # 消息内容 msg_content = '''发起时间:{}\n目标实例:{}\n数据库:{}\n工单名称:{}\n工单地址:{}\n驳回原因:{}\n提醒:此工单被审核不通过,请按照驳回原因进行修改!'''.format( workflow_detail.create_time.strftime('%Y-%m-%d %H:%M:%S'), instance, db_name, workflow_title, workflow_url, re.sub('[\r\n\f]{2,}', '\n', workflow_audit_remark)) elif status == WorkflowDict.workflow_status['audit_abort']: # 审核取消,通知所有审核人 msg_title = "[{}]提交人主动终止工单#{}".format(workflow_type_display, audit_id) # 接收人,发送给该资源组内对应权限组所有的用户 auth_group_names = [Group.objects.get(id=auth_group_id).name for auth_group_id in audit_detail.audit_auth_groups.split(',')] msg_to = auth_group_users(auth_group_names, audit_detail.group_id) msg_cc = Users.objects.filter(username__in=kwargs.get('cc_users', [])) # 消息内容 msg_content = '''发起时间:{}\n发起人:{}\n组:{}\n目标实例:{}\n数据库:{}\n工单名称:{}\n工单地址:{}\n终止原因:{}'''.format( workflow_detail.create_time.strftime('%Y-%m-%d %H:%M:%S'), workflow_from, group_name, instance, db_name, workflow_title, workflow_url, re.sub('[\r\n\f]{2,}', '\n', workflow_audit_remark)) else: raise Exception('工单状态不正确') logger.info(f"通知Debug{msg_to}{msg_cc}") # 发送通知 __send(msg_title, msg_content, msg_to, msg_cc, feishu_webhook=feishu_webhook, dingding_webhook=dingding_webhook, qywx_webhook=qywx_webhook)
def notify_for_audit(audit_id, **kwargs): """ 工作流消息通知,不包含工单执行结束的通知 :param audit_id: :param kwargs: :return: """ # 判断是否开启消息通知,未开启直接返回 sys_config = SysConfig() if not sys_config.get('mail') and not sys_config.get('ding'): logger.info('未开启消息通知,可在系统设置中开启') return None # 获取审核信息 audit_detail = Audit.detail(audit_id=audit_id) audit_id = audit_detail.audit_id workflow_audit_remark = kwargs.get('audit_remark', '') base_url = sys_config.get('archery_base_url', 'http://127.0.0.1:8000').rstrip('/') workflow_url = "{base_url}/workflow/{audit_id}".format( base_url=base_url, audit_id=audit_detail.audit_id) msg_cc_email = kwargs.get('email_cc', []) workflow_id = audit_detail.workflow_id workflow_type = audit_detail.workflow_type status = audit_detail.current_status workflow_title = audit_detail.workflow_title workflow_from = audit_detail.create_user_display group_name = audit_detail.group_name webhook_url = ResourceGroup.objects.get( group_id=audit_detail.group_id).ding_webhook # 获取当前审批和审批流程 workflow_auditors, current_workflow_auditors = Audit.review_info( audit_detail.workflow_id, audit_detail.workflow_type) # 准备消息内容 if workflow_type == WorkflowDict.workflow_type['query']: workflow_type_display = WorkflowDict.workflow_type['query_display'] workflow_detail = QueryPrivilegesApply.objects.get( apply_id=workflow_id) instance = workflow_detail.instance.instance_name db_name = '' if workflow_detail.priv_type == 1: workflow_content = '''数据库清单:{}\n授权截止时间:{}\n结果集:{}\n'''.format( workflow_detail.db_list, datetime.datetime.strftime(workflow_detail.valid_date, '%Y-%m-%d %H:%M:%S'), workflow_detail.limit_num) elif workflow_detail.priv_type == 2: db_name = workflow_detail.db_list workflow_content = '''数据库:{}\n表清单:{}\n授权截止时间:{}\n结果集:{}\n'''.format( workflow_detail.db_list, workflow_detail.table_list, datetime.datetime.strftime(workflow_detail.valid_date, '%Y-%m-%d %H:%M:%S'), workflow_detail.limit_num) else: workflow_content = '' elif workflow_type == WorkflowDict.workflow_type['sqlreview']: workflow_type_display = WorkflowDict.workflow_type['sqlreview_display'] workflow_detail = SqlWorkflow.objects.get(pk=workflow_id) instance = workflow_detail.instance.instance_name db_name = workflow_detail.db_name workflow_content = re.sub( '[\r\n\f]{2,}', '\n', workflow_detail.sqlworkflowcontent.sql_content[0:500].replace( '\r', '')) else: raise Exception('工单类型不正确') # 准备消息格式 if status == WorkflowDict.workflow_status['audit_wait']: # 申请阶段 msg_title = "[{}]新的工单申请#{}".format(workflow_type_display, audit_id) # 接收人,发送给该资源组内对应权限组所有的用户 auth_group_names = Group.objects.get( id=audit_detail.current_audit).name msg_to = auth_group_users([auth_group_names], audit_detail.group_id) # 消息内容 msg_content = '''发起时间:{}\n发起人:{}\n组:{}\n目标实例:{}\n数据库:{}\n审批流程:{}\n当前审批:{}\n工单名称:{}\n工单地址:{}\n工单详情预览:{}\n'''.format( workflow_detail.create_time.strftime('%Y-%m-%d %H:%M:%S'), workflow_from, group_name, instance, db_name, workflow_auditors, current_workflow_auditors, workflow_title, workflow_url, workflow_content) elif status == WorkflowDict.workflow_status['audit_success']: # 审核通过 msg_title = "[{}]工单审核通过#{}".format(workflow_type_display, audit_id) # 接收人,仅发送给申请人 msg_to = [Users.objects.get(username=audit_detail.create_user)] # 消息内容 msg_content = '''发起时间:{}\n发起人:{}\n组:{}\n目标实例:{}\n数据库:{}\n审批流程:{}\n工单名称:{}\n工单地址:{}\n工单详情预览:{}\n'''.format( workflow_detail.create_time.strftime('%Y-%m-%d %H:%M:%S'), workflow_from, group_name, instance, db_name, workflow_auditors, workflow_title, workflow_url, workflow_content) elif status == WorkflowDict.workflow_status['audit_reject']: # 审核驳回 msg_title = "[{}]工单被驳回#{}".format(workflow_type_display, audit_id) # 接收人,仅发送给申请人 msg_to = [Users.objects.get(username=audit_detail.create_user)] # 消息内容 msg_content = '''发起时间:{}\n目标实例:{}\n数据库:{}\n工单名称:{}\n工单地址:{}\n驳回原因:{}\n提醒:此工单被审核不通过,请按照驳回原因进行修改!'''.format( workflow_detail.create_time.strftime('%Y-%m-%d %H:%M:%S'), instance, db_name, workflow_title, workflow_url, workflow_audit_remark) elif status == WorkflowDict.workflow_status['audit_abort']: # 审核取消,通知所有审核人 msg_title = "[{}]提交人主动终止工单#{}".format(workflow_type_display, audit_id) # 接收人,发送给该资源组内对应权限组所有的用户 auth_group_names = [ Group.objects.get(id=auth_group_id).name for auth_group_id in audit_detail.audit_auth_groups.split(',') ] msg_to = auth_group_users(auth_group_names, audit_detail.group_id) # 消息内容 msg_content = '''发起时间:{}\n发起人:{}\n组:{}\n目标实例:{}\n数据库:{}\n工单名称:{}\n工单地址:{}\n终止原因:{}'''.format( workflow_detail.create_time.strftime('%Y-%m-%d %H:%M:%S'), workflow_from, group_name, instance, db_name, workflow_title, workflow_url, workflow_audit_remark) else: raise Exception('工单状态不正确') # 处理接收人信息 msg_to_email = [user.email for user in msg_to if user.email] # 发送通知 msg_sender = MsgSender() if sys_config.get('mail'): msg_sender.send_email(msg_title, msg_content, msg_to_email, list_cc_addr=msg_cc_email) if sys_config.get('ding'): if webhook_url: msg_sender.send_ding(webhook_url, msg_title + '\n' + msg_content)
def post(self, request): # 参数验证 serializer = AuditWorkflowSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) audit_type = request.data['audit_type'] workflow_type = request.data['workflow_type'] workflow_id = request.data['workflow_id'] audit_remark = request.data['audit_remark'] engineer = request.data['engineer'] user = Users.objects.get(username=engineer) # 审核查询权限申请 if workflow_type == 1: audit_status = 1 if audit_type == 'pass' else 2 if audit_remark is None: audit_remark = '' if Audit.can_review(user, workflow_id, workflow_type) is False: raise serializers.ValidationError({"errors": "你无权操作当前工单!"}) # 使用事务保持数据一致性 try: with transaction.atomic(): audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict.workflow_type['query'] ).audit_id # 调用工作流接口审核 audit_result = Audit.audit(audit_id, audit_status, user.username, audit_remark) # 按照审核结果更新业务表审核状态 audit_detail = Audit.detail(audit_id) if audit_detail.workflow_type == WorkflowDict.workflow_type[ 'query']: # 更新业务表审核状态,插入权限信息 _query_apply_audit_call_back( audit_detail.workflow_id, audit_result['data']['workflow_status']) except Exception as msg: logger.error(traceback.format_exc()) raise serializers.ValidationError({'errors': msg}) else: # 消息通知 async_task(notify_for_audit, audit_id=audit_id, audit_remark=audit_remark, timeout=60, task_name=f'query-priv-audit-{workflow_id}') return Response({ 'msg': 'passed' }) if audit_type == 'pass' else Response({'msg': 'canceled'}) # 审核SQL上线申请 elif workflow_type == 2: # SQL上线申请通过 if audit_type == 'pass': # 权限验证 if Audit.can_review(user, workflow_id, workflow_type) is False: raise serializers.ValidationError({"errors": "你无权操作当前工单!"}) # 使用事务保持数据一致性 try: with transaction.atomic(): # 调用工作流接口审核 audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict. workflow_type['sqlreview']).audit_id audit_result = Audit.audit( audit_id, WorkflowDict.workflow_status['audit_success'], user.username, audit_remark) # 按照审核结果更新业务表审核状态 if audit_result['data'][ 'workflow_status'] == WorkflowDict.workflow_status[ 'audit_success']: # 将流程状态修改为审核通过 SqlWorkflow(id=workflow_id, status='workflow_review_pass').save( update_fields=['status']) except Exception as msg: logger.error(traceback.format_exc()) raise serializers.ValidationError({'errors': msg}) else: # 开启了Pass阶段通知参数才发送消息通知 sys_config = SysConfig() is_notified = 'Pass' in sys_config.get('notify_phase_control').split(',') \ if sys_config.get('notify_phase_control') else True if is_notified: async_task(notify_for_audit, audit_id=audit_id, audit_remark=audit_remark, timeout=60, task_name=f'sqlreview-pass-{workflow_id}') return Response({'msg': 'passed'}) # SQL上线申请驳回/取消 elif audit_type == 'cancel': workflow_detail = SqlWorkflow.objects.get(id=workflow_id) if audit_remark is None: raise serializers.ValidationError({"errors": "终止原因不能为空"}) if can_cancel(user, workflow_id) is False: raise serializers.ValidationError({"errors": "你无权操作当前工单!"}) # 使用事务保持数据一致性 try: with transaction.atomic(): # 调用工作流接口取消或者驳回 audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict. workflow_type['sqlreview']).audit_id # 仅待审核的需要调用工作流,审核通过的不需要 if workflow_detail.status != 'workflow_manreviewing': # 增加工单日志 if user.username == workflow_detail.engineer: Audit.add_log(audit_id=audit_id, operation_type=3, operation_type_desc='取消执行', operation_info="取消原因:{}".format( audit_remark), operator=user.username, operator_display=user.display) else: Audit.add_log(audit_id=audit_id, operation_type=2, operation_type_desc='审批不通过', operation_info="审批备注:{}".format( audit_remark), operator=user.username, operator_display=user.display) else: if user.username == workflow_detail.engineer: Audit.audit( audit_id, WorkflowDict. workflow_status['audit_abort'], user.username, audit_remark) # 非提交人需要校验审核权限 elif user.has_perm('sql.sql_review'): Audit.audit( audit_id, WorkflowDict. workflow_status['audit_reject'], user.username, audit_remark) else: raise serializers.ValidationError( {"errors": "Permission Denied"}) # 删除定时执行task if workflow_detail.status == 'workflow_timingtask': schedule_name = f"sqlreview-timing-{workflow_id}" del_schedule(schedule_name) # 将流程状态修改为人工终止流程 workflow_detail.status = 'workflow_abort' workflow_detail.save() except Exception as msg: logger.error(f"取消工单报错,错误信息:{traceback.format_exc()}") raise serializers.ValidationError({'errors': msg}) else: # 发送取消、驳回通知,开启了Cancel阶段通知参数才发送消息通知 sys_config = SysConfig() is_notified = 'Cancel' in sys_config.get('notify_phase_control').split(',') \ if sys_config.get('notify_phase_control') else True if is_notified: audit_detail = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict. workflow_type['sqlreview']) if audit_detail.current_status in ( WorkflowDict.workflow_status['audit_abort'], WorkflowDict.workflow_status['audit_reject']): async_task( notify_for_audit, audit_id=audit_detail.audit_id, audit_remark=audit_remark, timeout=60, task_name=f'sqlreview-cancel-{workflow_id}') return Response({'msg': 'canceled'}) # 审核数据归档申请 elif workflow_type == 3: audit_status = 1 if audit_type == 'pass' else 2 if audit_remark is None: audit_remark = '' if Audit.can_review(user, workflow_id, workflow_type) is False: raise serializers.ValidationError({"errors": "你无权操作当前工单!"}) # 使用事务保持数据一致性 try: with transaction.atomic(): audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict.workflow_type['archive'] ).audit_id # 调用工作流插入审核信息,更新业务表审核状态 audit_status = Audit.audit( audit_id, audit_status, user.username, audit_remark)['data']['workflow_status'] ArchiveConfig( id=workflow_id, status=audit_status, state=True if audit_status == WorkflowDict.workflow_status['audit_success'] else False).save(update_fields=['status', 'state']) except Exception as msg: logger.error(traceback.format_exc()) raise serializers.ValidationError({'errors': msg}) else: # 消息通知 async_task(notify_for_audit, audit_id=audit_id, audit_remark=audit_remark, timeout=60, task_name=f'archive-audit-{workflow_id}') return Response({ 'msg': 'passed' }) if audit_type == 'pass' else Response({'msg': 'canceled'})