def get(self, request, *args, **kwargs): request_data = request.GET ticket_id = kwargs.get('ticket_id') # username = request_data.get('username', '') # 可用于权限控制 username = request.META.get('HTTP_USERNAME') from service.account.account_base_service import AccountBaseService app_name = request.META.get('HTTP_APPNAME') app_permission_check, msg = AccountBaseService.app_ticket_permission_check( app_name, ticket_id) if not app_permission_check: return api_response(-1, msg, '') if not username: return api_response(-1, '参数不全,请提供username', '') result, msg = TicketBaseService.get_ticket_flow_step( ticket_id, username) if result is not False: data = dict(value=result) code, msg, = 0, '' else: code, data = -1, '' return api_response(code, msg, data)
def send_ticket_notice(ticket_id): """ 发送工单通知 :param ticket_id: :return: """ # 获取工单信息 # 获取工作流信息,获取工作流的通知信息 # 获取通知信息的标题和内容模板 # 将通知内容,通知标题,通知人,作为变量传给通知脚本 ticket_obj = TicketRecord.objects.filter(id=ticket_id, is_deleted=0).first() if not ticket_obj: return False, 'ticket is not exist or has been deleted' workflow_id = ticket_obj.workflow_id workflow_obj = Workflow.objects.filter(id=workflow_id, is_deleted=0).first() notices = workflow_obj.notices if not notices: return True, 'no notice defined' notice_str_list = notices.split(',') notice_id_list = [int(notice_str) for notice_str in notice_str_list] send_notice_result_list = [] for notice_id in notice_id_list: notice_obj = CustomNotice.objects.filter(id=notice_id, is_deleted=0).first() if not notice_obj: continue title_template = notice_obj.title_template content_template = notice_obj.content_template # 获取工单所有字段的变量 ticket_value_info, msg = TicketBaseService.get_ticket_all_field_value( ticket_id) if not ticket_value_info: return False, msg title_result = title_template.format(**ticket_value_info) content_result = content_template.format(**ticket_value_info) notice_script_file_name = notice_obj.script.name notice_script_file = os.path.join(settings.MEDIA_ROOT, notice_script_file_name) # 获取工单最后一条操作记录 flow_log_list, msg = TicketBaseService.get_ticket_flow_log( ticket_id, 'loonrobot') last_flow_log = flow_log_list[0] participant_info_list = [] participant_username_list = [] from apps.account.models import LoonUser if ticket_obj.participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_PERSONAL: participant_username_list = [ticket_obj.participant] elif ticket_obj.participant_type_id in ( CONSTANT_SERVICE.PARTICIPANT_TYPE_MULTI, CONSTANT_SERVICE.PARTICIPANT_TYPE_MULTI_ALL): participant_username_list = ticket_obj.participant.split(',') elif ticket_obj.participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_ROLE: participant_username_list, msg = AccountBaseService.get_role_username_list( ticket_obj.participant) elif ticket_obj.participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_DEPT: participant_username_list, msg = AccountBaseService.get_dept_username_list( ticket_obj.participant) if participant_username_list: participant_queryset = LoonUser.objects.filter( username__in=participant_username_list, is_deleted=0) for participant_0 in participant_queryset: participant_info_list.append( dict(username=participant_0.username, alias=participant_0.alias, phone=participant_0.phone, email=participant_0.email)) globals = { 'title_result': title_result, 'content_result': content_result, 'participant': ticket_obj.participant, 'participant_type_id': ticket_obj.participant_type_id, 'multi_all_person': ticket_obj.multi_all_person, 'ticket_value_info': ticket_value_info, 'last_flow_log': last_flow_log, 'participant_info_list': participant_info_list } try: with stdoutIO() as s: # execfile(script_file, globals) # for python 2 exec( open(notice_script_file, encoding='utf-8').read(), globals) script_result = True # script_result_msg = ''.join(s.buflist) script_result_msg = ''.join(s.getvalue()) logger.info( 'send notice successful for ticket_id: {}, notice_id:{}'. format(ticket_id, notice_id)) except Exception as e: logger.error(traceback.format_exc()) script_result = False script_result_msg = e.__str__() send_notice_result_list.append( dict(notice_id=notice_id, result=script_result, msg=script_result_msg)) return send_notice_result_list
def run_flow_task(ticket_id, script_id_str, state_id, action_from='loonrobot'): """ 执行工作流脚本 :param script_id_star:通过脚本id来执行, 保存的是字符串 :param ticket_id: :param state_id: :param action_from: :return: """ script_id = int(script_id_str) ticket_obj = TicketRecord.objects.filter(id=ticket_id, is_deleted=False).first() if ticket_obj.participant == script_id_str and ticket_obj.participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_ROBOT: ## 校验脚本是否合法 # 获取脚本名称 script_obj = WorkflowScript.objects.filter(id=script_id, is_deleted=False, is_active=True).first() if not script_obj: return False, '脚本未注册或非激活状态' script_file = os.path.join(settings.MEDIA_ROOT, script_obj.saved_name.name) globals = {'ticket_id': ticket_id, 'action_from': action_from} # 如果需要脚本执行完成后,工单不往下流转(也就脚本执行失败或调用其他接口失败的情况),需要在脚本中抛出异常 try: with stdoutIO() as s: # execfile(script_file, globals) # for python 2 exec(open(script_file).read(), globals) script_result = True # script_result_msg = ''.join(s.buflist) script_result_msg = ''.join(s.getvalue()) except Exception as e: logger.error(traceback.format_exc()) script_result = False script_result_msg = e.__str__() logger.info('*' * 20 + '工作流脚本回调,ticket_id:[%s]' % ticket_id + '*' * 20) logger.info('*******工作流脚本回调,ticket_id:{}*****'.format(ticket_id)) # 因为上面的脚本执行时间可能会比较长,为了避免db session失效,重新获取ticket对象 ticket_obj = TicketRecord.objects.filter(id=ticket_id, is_deleted=False).first() # 新增处理记录,脚本后只允许只有一个后续直连状态 transition_obj = Transition.objects.filter(source_state_id=state_id, is_deleted=False).first() new_ticket_flow_dict = dict( ticket_id=ticket_id, transition_id=transition_obj.id, suggestion=script_result_msg, participant_type_id=CONSTANT_SERVICE.PARTICIPANT_TYPE_ROBOT, participant='脚本:(id:{}, name:{})'.format(script_obj.id, script_obj.name), state_id=state_id, creator='loonrobot') TicketBaseService.add_ticket_flow_log(new_ticket_flow_dict) if not script_result: # 脚本执行失败,状态不更新,标记任务执行结果 ticket_obj.script_run_last_result = False ticket_obj.save() return False, script_result_msg # 自动执行流转 tar_state_obj = State.objects.filter( id=transition_obj.destination_state_id, is_deleted=False).first() if tar_state_obj.participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_VARIABLE: if tar_state_obj.participant == 'creator': destination_participant_type_id = CONSTANT_SERVICE.PARTICIPANT_TYPE_PERSONAL destination_participant = ticket_obj.creator elif tar_state_obj.participant == 'creator_tl': approver, msg = AccountBaseService.get_user_dept_approver( ticket_obj.creator) if len(approver.split(',')) > 1: destination_participant_type_id = CONSTANT_SERVICE.PARTICIPANT_TYPE_MULTI else: destination_participant_type_id = CONSTANT_SERVICE.PARTICIPANT_TYPE_PERSONAL destination_participant = approver elif tar_state_obj.participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_FIELD: destination_participant, msg = TicketBaseService.get_ticket_field_value( ticket_id, tar_state_obj.participant) destination_participant_type_id = CONSTANT_SERVICE.PARTICIPANT_TYPE_PERSONAL if len(destination_participant.split(',')) > 1: destination_participant_type_id = CONSTANT_SERVICE.PARTICIPANT_TYPE_MULTI elif tar_state_obj.participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_PARENT_FIELD: parent_ticket_id = ticket_obj.parent_ticket_id destination_participant, msg = TicketBaseService.get_ticket_field_value( parent_ticket_id, tar_state_obj.participant) destination_participant_type_id = CONSTANT_SERVICE.PARTICIPANT_TYPE_PERSONAL if len(destination_participant.split(',')) > 1: destination_participant_type_id = CONSTANT_SERVICE.PARTICIPANT_TYPE_MULTI else: # 其他类型不换算成实际的处理人 destination_participant_type_id = tar_state_obj.participant_type_id destination_participant = tar_state_obj.participant ticket_obj.participant = destination_participant ticket_obj.participant_type_id = destination_participant_type_id ticket_obj.state_id = tar_state_obj.id ticket_obj.save() add_relation, msg = TicketBaseService.get_ticket_dest_relation( destination_participant_type_id, destination_participant) if add_relation: new_relation, msg = TicketBaseService.add_ticket_relation( ticket_id, add_relation) # 更新关系人信息 logger.info( '******脚本执行成功,工单基础信息更新完成, ticket_id:{}******'.format(ticket_id)) # 子工单处理 if tar_state_obj.type_id == CONSTANT_SERVICE.STATE_TYPE_END: if ticket_obj.parent_ticket_id: sub_ticket_queryset = TicketRecord.objects.filter( parent_ticket_id=ticket_obj.parent_ticket_id, is_deleted=False) sub_ticket_state_id_list = [] for sub_ticket_query0 in sub_ticket_queryset: sub_ticket_state_id_list.append(sub_ticket_query0.state_id) if set(sub_ticket_state_id_list) == set([ticket_obj.state_id]): # 父工单的所有子工单都已处理结束,自动流转父工单 parent_ticket_obj = TicketRecord.objects.filter( id=ticket_obj.parent_ticket_id, is_deleted=False).first() parent_transition_obj = Transition.object.filter( source_state_id=parent_ticket_obj.state_id, is_deleted=False).first() flag, msg = TicketBaseService.handle_ticket( parent_ticket_obj.id, dict(username='******', suggestion='所有子工单都已结束,自动流转', transition_id=parent_transition_obj.id)) if not flag: return True, msg # 下个状态也是脚本处理 if tar_state_obj.participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_ROBOT: run_flow_task.apply_async( args=[ticket_id, tar_state_obj.participant, tar_state_obj.id], queue='loonflow') return True, '' else: return False, '工单当前处理人为非脚本,不执行脚本'
def check_new_permission(cls, username: str, workflow_id: int)->tuple: """ 判断用户是否有新建工单的权限 check whether user can create ticket :param username: :param workflow_id: :return: """ # 获取workflow的限制表达式 flag, workflow_obj = cls.get_by_id(workflow_id) if not workflow_obj: return False, workflow_obj limit_expression = workflow_obj.limit_expression if not limit_expression: return True, 'no limit_expression set' #'限制周期({"period":24} 24小时), 限制次数({"count":1}在限制周期内只允许提交1次), 限制级别({"level":1} 针对(1单个用户 2全局)限制周期限制次数,默认特定用户);允许特定人员提交({"allow_persons":"zhangsan,lisi"}只允许张三提交工单,{"allow_depts":"1,2"}只允许部门id为1和2的用户提交工单,{"allow_roles":"1,2"}只允许角色id为1和2的用户提交工单) limit_expression_dict = json.loads(limit_expression) limit_period = limit_expression_dict.get('period') limit_count = limit_expression_dict.get('limit_count') limit_allow_persons = limit_expression_dict.get('allow_persons') limit_allow_depts = limit_expression_dict.get('allow_depts') limit_allow_roles = limit_expression_dict.get('allow_roles') from service.ticket.ticket_base_service import ticket_base_service_ins if limit_period: if limit_expression_dict.get('level'): if limit_expression_dict.get('level') == 1: flag, result = ticket_base_service_ins.get_ticket_count_by_args( workflow_id=workflow_id, username=username, period=limit_period) count_result = result.get('count_result') elif limit_expression_dict.get('level') == 2: flag, result = ticket_base_service_ins.get_ticket_count_by_args( workflow_id=workflow_id, period=limit_period) count_result = result.get('count_result') else: return False, 'level in limit_expression is invalid' if count_result is False: return False, result if not limit_expression_dict.get('count'): return False, 'count is need when level is not none' if count_result > limit_expression_dict.get('count'): return False, '{} tickets can be created in {}hours when workflow_id is {}'\ .format(limit_count, limit_period, workflow_id) if limit_allow_persons: if username not in limit_allow_persons.split(','): return False, '{} can not create ticket base on workflow_id:{}'.format(workflow_id) if limit_allow_depts: # 获取用户所属部门,包含上级部门 flag, user_all_dept_id_list = AccountBaseService.get_user_up_dept_id_list(username) if flag is False: return False, user_all_dept_id_list # 只要user_all_dept_id_list中的某个部门包含在允许范围内即可 limit_allow_dept_str_list = limit_allow_depts.split(',') limit_allow_dept_id_list = [int(limit_allow_dept_str) for limit_allow_dept_str in limit_allow_dept_str_list] limit_allow_dept_id_list = list(set(limit_allow_dept_id_list)) #去重 total_list = user_all_dept_id_list + limit_allow_dept_id_list if len(total_list) == len(set(total_list)): # 去重后长度相等,说明两个list完全没有重复,即用户所在部门id肯定不在允许的部门id列表内 return False, 'user is not in allow dept' if limit_allow_roles: # 获取用户所有的角色 flag, user_role_list = account_base_service_ins.get_user_role_id_list(username) if flag is False: return False, user_role_list limit_allow_role_str_list = limit_allow_roles.split(',') limit_allow_role_id_list = [int(limit_allow_role_str) for limit_allow_role_str in limit_allow_role_str_list] limit_allow_role_id_list = list(set(limit_allow_role_id_list)) total_list = limit_allow_role_id_list + user_role_list if len(total_list) == len(set(total_list)): return False, 'user is not in allow role' return True, ''
def get_format_participant_info(cls, participant_type_id, participant): """ 获取格式化的参与人信息 :param participant_type_id: :param participant: :return: """ participant_name = participant participant_type_name = '' participant_alias = '' if participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_PERSONAL: participant_type_name = '个人' participant_user_obj, msg = AccountBaseService.get_user_by_username( participant) if not participant_user_obj: participant_alias = participant else: participant_alias = participant_user_obj.alias elif participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_MULTI: participant_type_name = '多人' # 依次获取人员信息 participant_name_list = participant_name.split(',') participant_alias_list = [] for participant_name0 in participant_name_list: participant_user_obj, msg = AccountBaseService.get_user_by_username( participant_name0) if not participant_user_obj: participant_alias_list.append(participant_name0) else: participant_alias_list.append(participant_user_obj.alias) participant_alias = ','.join(participant_alias_list) elif participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_DEPT: participant_type_name = '部门' dept_obj, msg = AccountBaseService.get_dept_by_id(int(participant)) if not dept_obj: return False, 'dept is not existed or has been deleted' participant_name = dept_obj.name participant_alias = participant_name elif participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_ROLE: participant_type_name = '角色' role_obj, msg = AccountBaseService.get_role_by_id(int(participant)) if not role_obj: return False, 'role is not existedor has been deleted' participant_name = role_obj.name participant_alias = participant_name elif participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_VARIABLE: participant_type_name = '变量' if participant_name == 'creator': participant_alias = '工单创建人' elif participant_name == 'creator_tl': participant_alias = '工单创建人的tl' elif participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_ROBOT: if participant: flag, result = WorkflowRunScriptService.get_run_script_by_id( int(participant)) if flag: participant_alias = result.name elif participant_type_id == CONSTANT_SERVICE.PARTICIPANT_TYPE_HOOK: participant_type_name = 'hook' participant_alias = participant_name return dict(participant=participant, participant_name=participant_name, participant_type_id=participant_type_id, participant_type_name=participant_type_name, participant_alias=participant_alias), ''
def check_new_permission(cls, username, workflow_id): """ 判断用户是否有新建工单的权限 :param username: :param workflow_id: :return: """ # 获取workflow的限制表达式 workflow_obj, msg = cls.get_by_id(workflow_id) if not workflow_obj: return False, msg limit_expression = workflow_obj.limit_expression if not limit_expression: return True, 'no limit_expression set' #'限制周期({"period":24} 24小时), 限制次数({"count":1}在限制周期内只允许提交1次), 限制级别({"level":1} 针对(1单个用户 2全局)限制周期限制次数,默认特定用户);允许特定人员提交({"allow_persons":"zhangsan,lisi"}只允许张三提交工单,{"allow_depts":"1,2"}只允许部门id为1和2的用户提交工单,{"allow_roles":"1,2"}只允许角色id为1和2的用户提交工单) limit_expression_dict = json.loads(limit_expression) limit_period = limit_expression_dict.get('period') limit_count = limit_expression_dict.get('limit_count') limit_allow_persons = limit_expression_dict.get('allow_persons') limit_allow_depts = limit_expression_dict.get('allow_depts') limit_allow_roles = limit_expression_dict.get('allow_roles') if limit_period: from service.ticket.ticket_base_service import TicketBaseService if limit_expression_dict.get('level') == 1: count_result, msg = TicketBaseService.get_ticket_count_by_args( workflow_id=workflow_id, username=username, period=limit_period) elif limit_expression_dict.get('level') == 2: count_result, msg = TicketBaseService.get_ticket_count_by_args( workflow_id=workflow_id, period=limit_period) if count_result is False: return False, msg if count_result > limit_expression_dict.get('count'): return False, '{} tickets can be created in {}hours when workflow_id is {}'.format( limit_count, limit_period, workflow_id) if limit_allow_persons: if username not in limit_allow_persons.split(','): return False, '{} can not create ticket base on workflow_id:{}'.format( workflow_id) if limit_allow_depts: # 获取用户所属部门,包含上级部门 user_all_dept_id_list, msg = AccountBaseService.get_user_up_dept_id_list( ) if user_all_dept_id_list is False: return False, msg # 只要user_all_dept_id_list中的某个部门包含在允许范围内即可 limit_allow_dept_str_list = limit_allow_depts.split(',') limit_allow_dept_id_list = [ int(limit_allow_dept_str) for limit_allow_dept_str in limit_allow_dept_str_list ] limit_allow_dept_id_list = list(set(limit_allow_dept_id_list)) #去重 total_list = user_all_dept_id_list + limit_allow_dept_id_list if len(total_list) == len(set(total_list)): return False, 'user is not in allow dept' if limit_allow_roles: # 获取用户所有的角色 user_role_list, msg = AccountBaseService.get_user_role_id_list( username) if user_role_list is False: return False, msg limit_allow_role_str_list = limit_allow_roles.split(',') limit_allow_role_id_list = [ int(limit_allow_role_str) for limit_allow_role_str in limit_allow_role_str_list ] limit_allow_role_id_list = list(set(limit_allow_role_id_list)) total_list = limit_allow_role_id_list + user_role_list if len(total_list) == len(set(total_list)): return False, 'user is not in allow role' return True, ''