Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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, '工单当前处理人为非脚本,不执行脚本'
Ejemplo n.º 4
0
    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, ''
Ejemplo n.º 5
0
    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), ''
Ejemplo n.º 6
0
    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, ''