def test_execute_workflow_success(self, _execute_command): sql = 'set 1 1' row = ReviewResult(id=1, errlevel=0, stagestatus='Execute Successfully', errormessage='None', sql=sql, affected_rows=0, execute_time=0, full_sql=sql) wf = SqlWorkflow.objects.create(workflow_name='some_name', group_id=1, group_name='g1', engineer_display='', audit_auth_groups='some_group', create_time=datetime.now() - timedelta(days=1), status='workflow_finish', is_backup='是', instance=self.ins, db_name='some_db', syntax_type=1) SqlWorkflowContent.objects.create(workflow=wf, sql_content=sql) new_engine = RedisEngine(instance=self.ins) execute_result = new_engine.execute_workflow(workflow=wf) self.assertIsInstance(execute_result, ReviewSet) self.assertEqual(execute_result.rows[0].__dict__, row.__dict__)
def execute_callback(task): """异步任务的回调, 将结果填入数据库等等 使用django-q的hook, 传入参数为整个task task.result 是真正的结果 """ # https://stackoverflow.com/questions/7835272/django-operationalerror-2006-mysql-server-has-gone-away if connection.connection and not connection.is_usable(): close_old_connections() workflow_id = task.args[0] # 判断工单状态,如果不是执行中的,不允许更新信息,直接抛错记录日志 with transaction.atomic(): workflow = SqlWorkflow.objects.get(id=workflow_id) if workflow.status != 'workflow_executing': raise Exception(f'工单{workflow.id}状态不正确,禁止重复更新执行结果!') workflow.finish_time = task.stopped if not task.success: # 不成功会返回错误堆栈信息,构造一个错误信息 workflow.status = 'workflow_exception' execute_result = ReviewSet( full_sql=workflow.sqlworkflowcontent.sql_content) execute_result.rows = [ ReviewResult(stage='Execute failed', errlevel=2, stagestatus='异常终止', errormessage=task.result, sql=workflow.sqlworkflowcontent.sql_content) ] elif task.result.warning or task.result.error: execute_result = task.result workflow.status = 'workflow_exception' else: execute_result = task.result workflow.status = 'workflow_finish' # 保存执行结果 workflow.sqlworkflowcontent.execute_result = execute_result.json() workflow.sqlworkflowcontent.save() workflow.save() # 增加工单日志 audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict.workflow_type['sqlreview']).audit_id Audit.add_log(audit_id=audit_id, operation_type=6, operation_type_desc='执行结束', operation_info='执行结果:{}'.format( workflow.get_status_display()), operator='', operator_display='系统') # DDL工单结束后清空实例资源缓存 if workflow.syntax_type == 1: r = get_redis_connection("default") for key in r.scan_iter(match='*insRes*', count=2000): r.delete(key) # 发送消息 notify_for_execute(workflow)
def detail(request, workflow_id): workflow_detail = get_object_or_404(SqlWorkflow, pk=workflow_id) if workflow_detail.status in ['workflow_finish', 'workflow_exception'] \ and workflow_detail.is_manual == 0: rows = workflow_detail.execute_result else: rows = workflow_detail.review_content # 自动审批不通过的不需要获取下列信息 if workflow_detail.status != 'workflow_autoreviewwrong': # 获取当前审批和审批流程 audit_auth_group, current_audit_auth_group = Audit.review_info( workflow_id, 2) # 是否可审核 is_can_review = Audit.can_review(request.user, workflow_id, 2) # 是否可执行 is_can_execute = can_execute(request.user, workflow_id) # 是否可定时执行 is_can_timingtask = can_timingtask(request.user, workflow_id) # 是否可取消 is_can_cancel = can_cancel(request.user, workflow_id) else: audit_auth_group = '系统自动驳回' current_audit_auth_group = '系统自动驳回' is_can_review = False is_can_execute = False is_can_timingtask = False is_can_cancel = False # 获取定时执行任务信息 if workflow_detail.status == 'workflow_timingtask': job_id = Const.workflowJobprefix['sqlreview'] + '-' + str(workflow_id) job = job_info(job_id) if job: run_date = job.next_run else: run_date = '' else: run_date = '' # 兼容旧数据'[[]]'格式,转换为新格式[{}] if isinstance(json.loads(rows)[0], list): review_result = ReviewSet() for r in json.loads(rows): review_result.rows += [ReviewResult(inception_result=r)] rows = review_result.json() context = { 'workflow_detail': workflow_detail, 'rows': rows, 'is_can_review': is_can_review, 'is_can_execute': is_can_execute, 'is_can_timingtask': is_can_timingtask, 'is_can_cancel': is_can_cancel, 'audit_auth_group': audit_auth_group, 'current_audit_auth_group': current_audit_auth_group, 'run_date': run_date } return render(request, 'detail.html', context)
def execute_callback(task): """异步任务的回调, 将结果填入数据库等等 使用django-q的hook, 传入参数为整个task task.result 是真正的结果 """ workflow_id = task.args[0] workflow = SqlWorkflow.objects.get(id=workflow_id) workflow.finish_time = task.stopped if not task.success: # 不成功会返回错误堆栈信息,构造一个错误信息追加到执行结果后面 workflow.status = 'workflow_exception' if workflow.sqlworkflowcontent.execute_result: execute_result = json.loads( workflow.sqlworkflowcontent.execute_result) else: execute_result = [] execute_result.append( ReviewResult(id=0, stage='Execute failed', errlevel=2, stagestatus='异常终止', errormessage=task.result, sql='执行异常信息', affected_rows=0, actual_affected_rows=0, sequence='0_0_0', backup_dbname=None, execute_time=0, sqlsha1='').__dict__) execute_result = json.dumps(execute_result) elif task.result.warning or task.result.error: execute_result = task.result workflow.status = 'workflow_exception' execute_result = execute_result.json() else: execute_result = task.result workflow.status = 'workflow_finish' execute_result = execute_result.json() # 保存执行结果 workflow.sqlworkflowcontent.execute_result = execute_result workflow.sqlworkflowcontent.save() workflow.save() # 增加工单日志 audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict.workflow_type['sqlreview']).audit_id Audit.add_log(audit_id=audit_id, operation_type=6, operation_type_desc='执行结束', operation_info='执行结果:{}'.format( workflow.get_status_display()), operator='', operator_display='系统') # 发送消息 notify_for_execute(workflow)
def detail_content(request): """获取工单内容""" workflow_id = request.GET.get('workflow_id') workflow_detail = get_object_or_404(SqlWorkflow, pk=workflow_id) if not can_view(request.user, workflow_id): raise PermissionDenied if workflow_detail.status in ['workflow_finish', 'workflow_exception']: rows = workflow_detail.sqlworkflowcontent.execute_result else: rows = workflow_detail.sqlworkflowcontent.review_content review_result = ReviewSet() if rows: try: # 检验rows能不能正常解析 loaded_rows = json.loads(rows) # 兼容旧数据'[[]]'格式,转换为新格式[{}] if isinstance(loaded_rows[-1], list): for r in loaded_rows: review_result.rows += [ReviewResult(inception_result=r)] rows = review_result.json() except IndexError: review_result.rows += [ ReviewResult( id=1, sql=workflow_detail.sqlworkflowcontent.sql_content, errormessage="Json decode failed." "执行结果Json解析失败, 请联系管理员") ] rows = review_result.json() except json.decoder.JSONDecodeError: review_result.rows += [ ReviewResult( id=1, sql=workflow_detail.sqlworkflowcontent.sql_content, # 迫于无法单元测试这里加上英文报错信息 errormessage="Json decode failed." "执行结果Json解析失败, 请联系管理员") ] rows = review_result.json() else: rows = workflow_detail.sqlworkflowcontent.review_content result = {"rows": json.loads(rows)} return HttpResponse(json.dumps(result), content_type='application/json')
def test_execute_check_critical_sql(self): sql = 'alter table user' row = ReviewResult(id=1, errlevel=2, stagestatus='SQL语法错误', errormessage='ALTER TABLE 必须带有选项', sql=sql) new_engine = InceptionEngine() check_result = new_engine.execute_check(db_name=0, sql=sql) self.assertIsInstance(check_result, ReviewSet) self.assertEqual(check_result.rows[0].__dict__, row.__dict__)
def test_execute_check_select_sql(self): sql = 'select * from user' row = ReviewResult(id=1, errlevel=2, stagestatus='驳回高危SQL', errormessage='仅支持DML和DDL语句,查询语句请使用SQL查询功能!', sql=sql) new_engine = MysqlEngine(instance=self.ins1) check_result = new_engine.execute_check(db_name='archery', sql=sql) self.assertIsInstance(check_result, ReviewSet) self.assertEqual(check_result.rows[0].__dict__, row.__dict__)
def test_execute_check_critical_sql(self): self.sys_config.set('critical_ddl_regex', '^|update') self.sys_config.get_all_config() sql = 'update user set id=1' row = ReviewResult(id=1, errlevel=2, stagestatus='驳回高危SQL', errormessage='禁止提交匹配' + '^|update' + '条件的语句!', sql=sql) new_engine = MysqlEngine(instance=self.ins1) check_result = new_engine.execute_check(db_name='archery', sql=sql) self.assertIsInstance(check_result, ReviewSet) self.assertEqual(check_result.rows[0].__dict__, row.__dict__)
def execute_callback(task): """异步任务的回调, 将结果填入数据库等等 使用django-q的hook, 传入参数为整个task task.result 是真正的结果 """ close_old_connections() workflow_id = task.args[0] workflow = SqlWorkflow.objects.get(id=workflow_id) workflow.finish_time = task.stopped if not task.success: # 不成功会返回错误堆栈信息,构造一个错误信息 workflow.status = 'workflow_exception' execute_result = ReviewSet( full_sql=workflow.sqlworkflowcontent.sql_content) execute_result.rows = [ ReviewResult(stage='Execute failed', errlevel=2, stagestatus='异常终止', errormessage=task.result, sql=workflow.sqlworkflowcontent.sql_content) ] elif task.result.warning or task.result.error: execute_result = task.result workflow.status = 'workflow_exception' else: execute_result = task.result workflow.status = 'workflow_finish' # 保存执行结果 workflow.sqlworkflowcontent.execute_result = execute_result.json() workflow.sqlworkflowcontent.save() workflow.save() # 增加工单日志 audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict.workflow_type['sqlreview']).audit_id Audit.add_log(audit_id=audit_id, operation_type=6, operation_type_desc='执行结束', operation_info='执行结果:{}'.format( workflow.get_status_display()), operator='', operator_display='系统') # DDL工单结束后清空实例资源缓存 if workflow.syntax_type == 1: r = get_redis_connection("default") for key in r.scan_iter(match='*insRes*', count=2000): r.delete(key) # 发送消息 notify_for_execute(workflow)
def test_execute_check(self): sql = 'set 1 1' row = ReviewResult(id=1, errlevel=0, stagestatus='Audit completed', errormessage='None', sql=sql, affected_rows=0, execute_time=0, full_sql=sql) new_engine = RedisEngine(instance=self.ins) check_result = new_engine.execute_check(db_name=0, sql=sql) self.assertIsInstance(check_result, ReviewSet) self.assertEqual(check_result.rows[0].__dict__, row.__dict__)
def test_execute_check_normal_sql(self, _inception_engine): sql = 'update user set id=1' row = ReviewResult(id=1, errlevel=0, stagestatus='Audit completed', errormessage='None', sql=sql, affected_rows=0, execute_time=0, ) _inception_engine.return_value.execute_check.return_value = ReviewSet(full_sql=sql, rows=[row]) new_engine = MysqlEngine(instance=self.ins1) check_result = new_engine.execute_check(db_name='archery', sql=sql) self.assertIsInstance(check_result, ReviewSet) self.assertEqual(check_result.rows[0].__dict__, row.__dict__)
def test_execute_check_normal_sql(self): self.sys_config.purge() sql = 'update user set id=1' row = ReviewResult(id=1, errlevel=0, stagestatus='Audit completed', errormessage='None', sql=sql, affected_rows=0, execute_time=0, ) new_engine = OracleEngine(instance=self.ins) check_result = new_engine.execute_check(db_name='archery', sql=sql) self.assertIsInstance(check_result, ReviewSet) self.assertEqual(check_result.rows[0].__dict__, row.__dict__)
def is_auto_review(workflow_id): """ 判断SQL上线是否无需审批,无需审批的提交会自动审核通过 :param workflow_id: :return: """ workflow = SqlWorkflow.objects.get(id=workflow_id) auto_review_tags = SysConfig().get('auto_review_tag', '').split(',') auto_review_db_type = SysConfig().get('auto_review_db_type', '').split(',') # TODO 这里也可以放到engine中实现,但是配置项可能会相对复杂 if workflow.instance.db_type in auto_review_db_type and workflow.instance.instance_tag.filter( tag_code__in=auto_review_tags).exists(): # 获取正则表达式 auto_review_regex = SysConfig().get( 'auto_review_regex', '^alter|^create|^drop|^truncate|^rename|^delete') p = re.compile(auto_review_regex, re.I) # 判断是否匹配到需要手动审核的语句 auto_review = True all_affected_rows = 0 review_content = workflow.sqlworkflowcontent.review_content for review_row in json.loads(review_content): review_result = ReviewResult(**review_row) # 去除SQL注释 https://github.com/hhyo/Archery/issues/949 sql = remove_comments(review_result.sql) # 正则匹配 if p.match(sql): auto_review = False break # 影响行数加测, 总语句影响行数超过指定数量则需要人工审核 all_affected_rows += int(review_result.affected_rows) if all_affected_rows > int(SysConfig().get( 'auto_review_max_update_rows', 50)): auto_review = False else: auto_review = False return auto_review
def newquery(request): """正式提交SQL, 此处生成工单""" workflow_name = request.POST.get('workflow_name') demand_url = request.POST.get('demand_url', '') group_name = request.POST.get('group_name') instance_name = request.POST.get('instance_name') db_name = request.POST.get('db_name') first_run_time = request.POST.get('first_run_time') schedule_type = request.POST.get('period') minutes = request.POST.get('minutes') receivers = request.POST.getlist('receivers') cc_list = request.POST.getlist('cc_list') is_backup = False sql_content = request.POST.get('sql_content').strip() result = {'status': 0, 'msg': 'ok', 'data': {}} # 服务器端参数验证 if not all([ workflow_name, group_name, instance_name, db_name, first_run_time, schedule_type, sql_content ]): result['status'] = 1 result['msg'] = '请检查提交工单填写内容是否完整' return HttpResponse(json.dumps(result), content_type='application/json') user = request.user group_id = ResourceGroup.objects.get(group_name=group_name).group_id instance = Instance.objects.get(instance_name=instance_name) # 验证组权限(用户是否在该组、该组是否有指定实例) try: user_instances(user, tag_codes=['can_read']).get(instance_name=instance_name) except instance.DoesNotExist: result['status'] = 1 result['msg'] = '你所在组未关联该实例' return HttpResponse(json.dumps(result), content_type='application/json') try: config = SysConfig() # 查询前的检查,禁用语句检查,语句切分 query_engine = get_engine(instance=instance) query_check_info = query_engine.query_check(db_name=db_name, sql=sql_content) if query_check_info.get('bad_query'): # 引擎内部判断为 bad_query result['status'] = 1 result['msg'] = query_check_info.get('msg') return HttpResponse(json.dumps(result), content_type='application/json') if query_check_info.get( 'has_star') and config.get('disable_star') is True: # 引擎内部判断为有 * 且禁止 * 选项打开 result['status'] = 1 result['msg'] = query_check_info.get('msg') return HttpResponse(json.dumps(result), content_type='application/json') sql_content = query_check_info['filtered_sql'] # 查询权限校验,并且获取limit_num priv_check_info = query_priv_check(user, instance, db_name, sql_content, 0) if priv_check_info['status'] != 0: result['status'] = 1 result['msg'] = priv_check_info['msg'] return HttpResponse(json.dumps(result), content_type='application/json') except Exception as e: logger.error( f'查询异常报错,查询语句:{sql_content}\n,错误信息:{traceback.format_exc()}') result['status'] = 1 result['msg'] = f'查询异常报错,错误信息:{e}' return HttpResponse(json.dumps(result), content_type='application/json') check_result = ReviewSet(full_sql=sql_content) check_result.rows = [ ReviewResult( id=1, errlevel=0, stagestatus='Audit completed', errormessage='None', sql=sql_content, affected_rows=0, execute_time=0, ) ] # 调用工作流生成工单 # 使用事务保持数据一致性 try: with transaction.atomic(): # 存进数据库里 sql_workflow = SqlWorkflow.objects.create( order_type="sqlcron_order", workflow_name=workflow_name, demand_url=demand_url, group_id=group_id, group_name=group_name, engineer=request.user.username, engineer_display=request.user.display, audit_auth_groups=Audit.settings( group_id, WorkflowDict.workflow_type['sqlreview']), status='workflow_manreviewing', is_backup=is_backup, instance=instance, db_name=db_name, is_manual=0, syntax_type=3, #0、未知,1、DDL,2、DML 3、QUERY' create_time=timezone.now(), run_date_start=None, run_date_end=None, cc_list=cc_list, ) sql_workflow.receivers.set( Users.objects.filter(username__in=receivers)) sql_workflow_content = SqlWorkflowContent.objects.create( workflow=sql_workflow, sql_content=sql_content, review_content=check_result.json(), execute_result='') sched = schedule( 'sql.utils.query_sql.query', sql_workflow.id, hook='sql.utils.query_sql.query_callback', name=f'sqlcron-{sql_workflow.id}', schedule_type=schedule_type, minutes=minutes, next_run=first_run_time, repeats=0, # 在审批结束前创建但不启用 timeout=-1, ) sql_workflow.schedule = sched sql_workflow.save() sql_workflow_content.save() # 调用工作流插入审核信息, 查询权限申请workflow_type=2 Audit.add(WorkflowDict.workflow_type['sqlreview'], sql_workflow.id) except Exception as e: logger.error(f"提交工单报错,错误信息:{traceback.format_exc()}") result['status'] = 1 result['msg'] = f'提交工单报错,错误信息:{e}' return HttpResponse(json.dumps(result), content_type='application/json') else: # 进行消息通知 audit_id = Audit.detail_by_workflow_id( workflow_id=sql_workflow.id, workflow_type=WorkflowDict.workflow_type['sqlreview']).audit_id async_task(notify_for_audit, audit_id=audit_id, cc_users=receivers, timeout=60, task_name=f'sqlreview-submit-{sql_workflow.id}') result['status'] = 0 result['data'] = { 'redirect': reverse('sql:sqlcrondetail', args=(sql_workflow.id, )) } return HttpResponse(json.dumps(result), content_type='application/json')
def query_callback(task): """异步任务的回调, 将结果填入数据库等等 使用django-q的hook, 传入参数为整个task task.review_set 是真正的结果 """ # https://stackoverflow.com/questions/7835272/django-operationalerror-2006-mysql-server-has-gone-away if connection.connection and not connection.is_usable(): close_old_connections() workflow_id = task.args[0] workflow = SqlWorkflow.objects.get(id=workflow_id) workflow.finish_time = task.stopped query_result = task.result filename = '' review_set = ReviewSet(full_sql=workflow.sqlworkflowcontent.sql_content) if not task.success: # 不成功会返回错误堆栈信息,构造一个错误信息 workflow.status = 'workflow_exception' review_set.rows = [ ReviewResult(stage='Execute failed', errlevel=2, stagestatus='异常终止', errormessage=query_result, sql=workflow.sqlworkflowcontent.sql_content) ] elif query_result.error: # 不成功会返回错误堆栈信息,构造一个错误信息 workflow.status = 'workflow_exception' review_set.rows = [ ReviewResult(stage='Execute failed', errlevel=2, stagestatus='异常终止', errormessage=query_result.error, sql=workflow.sqlworkflowcontent.sql_content) ] else: filename = f"QueryTask{workflow.id}-{int(time.time())}.xls" save2excel(f"{settings.DOWNLOAD_DIR}/{filename}", [query_result.column_list] + list(query_result.rows)) review_set.rows = [ ReviewResult( errlevel=0, stagestatus='Execute Successfully', errormessage='None', sql=query_result.full_sql, affected_rows=query_result.affected_rows, execute_time=query_result.query_time, ) ] workflow.status = 'workflow_finish' # 保存执行结果 workflow.sqlworkflowcontent.execute_result = review_set.json() workflow.sqlworkflowcontent.save() workflow.save() # 增加工单日志 audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict.workflow_type['sqlreview']).audit_id Audit.add_log(audit_id=audit_id, operation_type=6, operation_type_desc='执行工单', operation_info=f'执行结果:{workflow.get_status_display()}' + (f',文件: [{filename}]' if filename else ''), operator='', operator_display='系统') # 发送消息 notify_for_execute(workflow, filename_list=([f"{settings.DOWNLOAD_DIR}/{filename}"] if filename else None))
def sqlcrondetail(request, workflow_id): """展示SQL工单详细页面""" workflow_detail = get_object_or_404(SqlWorkflow, pk=workflow_id) if not can_view(request.user, workflow_id): raise PermissionDenied if workflow_detail.status in ['workflow_finish', 'workflow_exception']: rows = workflow_detail.sqlworkflowcontent.execute_result else: rows = workflow_detail.sqlworkflowcontent.review_content # 自动审批不通过的不需要获取下列信息 if workflow_detail.status != 'workflow_autoreviewwrong': # 获取当前审批和审批流程 audit_auth_group, current_audit_auth_group = Audit.review_info( workflow_id, 2) # 是否可审核 is_can_review = Audit.can_review(request.user, workflow_id, 2) # 是否可执行 is_can_execute = False # 是否可定时执行 is_can_timingtask = False # 是否可取消 is_can_cancel = can_cancel(request.user, workflow_id) # 是否可查看回滚信息 is_can_rollback = False # 获取审核日志 try: audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict.workflow_type['sqlreview']).audit_id last_operation_info = Audit.logs( audit_id=audit_id).latest('id').operation_info except Exception as e: logger.debug(f'无审核日志记录,错误信息{e}') last_operation_info = '' else: audit_auth_group = '系统自动驳回' current_audit_auth_group = '系统自动驳回' is_can_review = False is_can_execute = False is_can_timingtask = False is_can_cancel = False is_can_rollback = False last_operation_info = None # 获取是否开启手工执行确认 manual = SysConfig().get('manual') review_result = ReviewSet() if rows: try: # 检验rows能不能正常解析 loaded_rows = json.loads(rows) # 兼容旧数据'[[]]'格式,转换为新格式[{}] if isinstance(loaded_rows[-1], list): for r in loaded_rows: review_result.rows += [ReviewResult(inception_result=r)] rows = review_result.json() except IndexError: review_result.rows += [ ReviewResult( id=1, sql=workflow_detail.sqlworkflowcontent.sql_content, errormessage="Json decode failed." "执行结果Json解析失败, 请联系管理员") ] rows = review_result.json() except json.decoder.JSONDecodeError: review_result.rows += [ ReviewResult( id=1, sql=workflow_detail.sqlworkflowcontent.sql_content, # 迫于无法单元测试这里加上英文报错信息 errormessage="Json decode failed." "执行结果Json解析失败, 请联系管理员") ] rows = review_result.json() else: rows = workflow_detail.sqlworkflowcontent.review_content schedule = workflow_detail.schedule context = { 'workflow_detail': workflow_detail, 'rows': rows, 'last_operation_info': last_operation_info, 'is_can_review': is_can_review, 'is_can_execute': is_can_execute, 'is_can_timingtask': is_can_timingtask, 'is_can_cancel': is_can_cancel, 'is_can_rollback': is_can_rollback, 'audit_auth_group': audit_auth_group, 'current_audit_auth_group': current_audit_auth_group, 'next_run': schedule.next_run, 'receivers': workflow_detail.receivers.all(), 'schedule_period': { 'O': '单次执行', 'H': '每小时执行一次', 'D': '每天执行一次', 'W': '每周执行一次', 'M': '每月执行一次', 'Y': '每年执行一次', 'I': f'每{schedule.minutes}分钟执行一次' }[schedule.schedule_type] } return render(request, 'sqlcron/detail.html', context)
def detail(request, workflow_id): """展示SQL工单详细页面""" workflow_detail = get_object_or_404(SqlWorkflow, pk=workflow_id) if workflow_detail.status in ['workflow_finish', 'workflow_exception']: rows = workflow_detail.sqlworkflowcontent.execute_result else: rows = workflow_detail.sqlworkflowcontent.review_content # 自动审批不通过的不需要获取下列信息 if workflow_detail.status != 'workflow_autoreviewwrong': # 获取当前审批和审批流程 audit_auth_group, current_audit_auth_group = Audit.review_info( workflow_id, 2) # 是否可审核 is_can_review = Audit.can_review(request.user, workflow_id, 2) # 是否可执行 is_can_execute = can_execute(request.user, workflow_id) # 是否可定时执行 is_can_timingtask = can_timingtask(request.user, workflow_id) # 是否可取消 is_can_cancel = can_cancel(request.user, workflow_id) # 获取审核日志 try: audit_id = Audit.detail_by_workflow_id( workflow_id=workflow_id, workflow_type=WorkflowDict.workflow_type['sqlreview']).audit_id last_operation_info = Audit.logs( audit_id=audit_id).latest('id').operation_info except Exception as e: logger.debug(f'无审核日志记录,错误信息{e}') last_operation_info = '' else: audit_auth_group = '系统自动驳回' current_audit_auth_group = '系统自动驳回' is_can_review = False is_can_execute = False is_can_timingtask = False is_can_cancel = False last_operation_info = None # 获取定时执行任务信息 if workflow_detail.status == 'workflow_timingtask': job_id = Const.workflowJobprefix['sqlreview'] + '-' + str(workflow_id) job = task_info(job_id) if job: run_date = job.next_run else: run_date = '' else: run_date = '' # 获取是否开启手工执行确认 manual = SysConfig().get('manual') review_result = ReviewSet() if rows: try: # 检验rows能不能正常解析 loaded_rows = json.loads(rows) # 兼容旧数据'[[]]'格式,转换为新格式[{}] if isinstance(loaded_rows[-1], list): for r in loaded_rows: review_result.rows += [ReviewResult(inception_result=r)] rows = review_result.json() except IndexError: review_result.rows += [ ReviewResult( id=1, sql=workflow_detail.sqlworkflowcontent.sql_content, errormessage="Json decode failed." "执行结果Json解析失败, 请联系管理员") ] rows = review_result.json() except json.decoder.JSONDecodeError: review_result.rows += [ ReviewResult( id=1, sql=workflow_detail.sqlworkflowcontent.sql_content, # 迫于无法单元测试这里加上英文报错信息 errormessage="Json decode failed." "执行结果Json解析失败, 请联系管理员") ] rows = review_result.json() else: rows = workflow_detail.sqlworkflowcontent.review_content context = { 'workflow_detail': workflow_detail, 'rows': rows, 'last_operation_info': last_operation_info, 'is_can_review': is_can_review, 'is_can_execute': is_can_execute, 'is_can_timingtask': is_can_timingtask, 'is_can_cancel': is_can_cancel, 'audit_auth_group': audit_auth_group, 'manual': manual, 'current_audit_auth_group': current_audit_auth_group, 'run_date': run_date } return render(request, 'detail.html', context)