From 9ed725dce2c25fb726fc0c7a990f64fd741c05ac Mon Sep 17 00:00:00 2001 From: blackholll Date: Wed, 19 Oct 2022 22:23:27 +0800 Subject: [PATCH] participant support get from post response --- apps/workflow/models.py | 2 +- .../pages/Workflow/WorkflowState/index.tsx | 5 +-- service/common/constant_service.py | 1 + service/ticket/ticket_base_service.py | 31 +++++++++++++++++++ sphinx_docs/source/manage/workflow_config.rst | 12 +++++-- tasks.py | 2 +- 6 files changed, 47 insertions(+), 6 deletions(-) diff --git a/apps/workflow/models.py b/apps/workflow/models.py index a7679349..cb3c20e8 100644 --- a/apps/workflow/models.py +++ b/apps/workflow/models.py @@ -38,7 +38,7 @@ class State(BaseModel): enable_retreat = models.BooleanField('允许撤回', default=False, help_text='开启后允许工单创建人在此状态直接撤回工单到初始状态') remember_last_man_enable = models.BooleanField('记忆最后处理人', default=False, help_text='开启后,到达此状态时会先检查之前是否有人在此状态处理过,如果有则处理人为最后一次处理的人') - participant_type_id = models.IntegerField('参与者类型id', default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容,10.hook。 初始状态请选择类型5,参与人填creator') + participant_type_id = models.IntegerField('参与者类型id', default=1, blank=True, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本(已废弃),7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容,10.hook,11.外部获取。 初始状态请选择类型5,参与人填creator') participant = models.CharField('参与者', default='', blank=True, max_length=1000, help_text='可以为空(无处理人的情况,如结束状态)、username\多个username(以,隔开)\部门id\角色id\变量(creator,creator_tl)\脚本记录的id等,包含子工作流的需要设置处理人为loonrobot') distribute_type_id = models.IntegerField('分配方式', default=1, help_text='1.主动接单(如果当前处理人实际为多人的时候,需要先接单才能处理) 2.直接处理(即使当前处理人实际为多人,也可以直接处理) 3.随机分配(如果实际为多人,则系统会随机分配给其中一个人) 4.全部处理(要求所有参与人都要处理一遍,才能进入下一步)') diff --git a/frontend/src/pages/Workflow/WorkflowState/index.tsx b/frontend/src/pages/Workflow/WorkflowState/index.tsx index 80ef7b00..3b5a9ec7 100644 --- a/frontend/src/pages/Workflow/WorkflowState/index.tsx +++ b/frontend/src/pages/Workflow/WorkflowState/index.tsx @@ -408,15 +408,16 @@ class WorkflowState extends Component { - + {/*/!**!/ 已废弃*/} + 参与人} + label={参与人} > diff --git a/service/common/constant_service.py b/service/common/constant_service.py index 5b7c9e17..4b20bef3 100644 --- a/service/common/constant_service.py +++ b/service/common/constant_service.py @@ -32,6 +32,7 @@ def __init__(self): self.PARTICIPANT_TYPE_FIELD = 7 # 工单字段(用户名类型的) self.PARTICIPANT_TYPE_PARENT_FIELD = 8 # 父工单字段(用户名类型的) self.PARTICIPANT_TYPE_HOOK = 10 # hook方式,当工单状态叨叨处理人类型配置为kook的状态时,loonflow将触发一个hook请求,被请求方可以执行有些自动化操作然后回调loonflow, + self.PARTICIPANT_TYPE_FROM_EXTERNAL = 11 # 来自外部,通过触发请求获取当前的处理人 self.TRANSITION_TYPE_COMMON = 1 # 常规流转 self.TRANSITION_TYPE_TIMER = 2 # 定时器流转 diff --git a/service/ticket/ticket_base_service.py b/service/ticket/ticket_base_service.py index e6def29d..cc22e11b 100644 --- a/service/ticket/ticket_base_service.py +++ b/service/ticket/ticket_base_service.py @@ -1,6 +1,7 @@ import copy import json import datetime +import logging import random import redis @@ -2062,6 +2063,36 @@ def get_ticket_state_participant_info(cls, state_id: int, ticket_id: int=0, tick elif participant_type_id == constant_service_ins.PARTICIPANT_TYPE_HOOK: destination_participant = '***' # 敏感数据,不保存工单基础表中 + elif participant_type_id == constant_service_ins.PARTICIPANT_TYPE_FROM_EXTERNAL: + import requests + external_config = json.loads(participant) + external_url = external_config.get('external_url') + external_token = external_config.get('external_token') + extra_info = external_config.get('extra_info') + + flag, msg = common_service_ins.gen_hook_signature(external_token) + if not flag: + return False, msg + flag, all_ticket_data = ticket_base_service_ins.get_ticket_all_field_value(ticket_id) + if extra_info is not None: + all_ticket_data.update(dict(extra_info=extra_info)) + try: + r = requests.post(external_url, headers=msg, json=all_ticket_data, timeout=10) + result = r.json() # {code:0, msg:'', data:'zhangsan,lisi'} + if result.get('data').split(',') > 1: + destination_participant_type_id = constant_service_ins.PARTICIPANT_TYPE_MULTI + else: + destination_participant_type_id = constant_service_ins.PARTICIPANT_TYPE_PERSONAL + destination_participant = result.get('data') + except Exception as e: + import logging + import traceback + logger = logging.getLogger('django') + logger.error('get external participant error:') + logger.error(traceback.format_exc()) + destination_participant_type_id = constant_service_ins.PARTICIPANT_TYPE_PERSONA + destination_participant = 'admin' + # 参与人去重复+类型修正 if destination_participant_type_id in (constant_service_ins.PARTICIPANT_TYPE_PERSONAL, constant_service_ins.PARTICIPANT_TYPE_MULTI): destination_participant_list = destination_participant.split(',') diff --git a/sphinx_docs/source/manage/workflow_config.rst b/sphinx_docs/source/manage/workflow_config.rst index 9c18d1b6..52229a45 100644 --- a/sphinx_docs/source/manage/workflow_config.rst +++ b/sphinx_docs/source/manage/workflow_config.rst @@ -135,7 +135,7 @@ loonflow发送hook请求时,header头中将包含signature 和timestamp。 使用场景如:运维处理中状态下处理人有A、B、C,其中A处理了工单,然后到达发起人确认状态时,发起人发现处理的有问题, 那么发起人可能是希望将工单退回到之前处理的A。而不是A、B、C都收到工单 -参与人类型: 参与人类型包括个人、多人、部门、角色、脚本(建议使用hook替代)、工单字段、父工单字段、hook、无。 +参与人类型: 参与人类型包括个人、多人、部门、角色、工单字段、父工单字段、hook、外部获取、无。 注意:如果需要在此状态创建子工单,需要将参与人类型设置为个人,参与人使用loonrobot 参与人:参与人信息根据参与人类型不同而不同,如果参与人类型是个人,那么参与人需要填写用户的username。 如果参与人类型是多人, @@ -150,6 +150,8 @@ wait的值可以是true或者false,如果wait的值是false那么工单触发hoo 即code=-1 或者服务端无响应或者http status非200工单会标记script_run_last_result为False,你可以调用“重试工单脚本/任务”重新触发hook), 如果wait的值是true那么工单触发hook后会停留在当前状态,直到hook方回调(回调逻辑见文档中“工单相关接口”-"工单hook回调")loonflow成功 (请求参数中result=True)后工单的状态才继续流转。extra_info(非必填)可以用于传一些额外的信息,loonflow会将这个信息连同工单信息传给hook服务端。 +如果参与人类型是'外部获取',参与人信息需要填写{"external_url":"http://www.xx.com", "external_token":"xxx", "extra_info":""}, +系统将根据你配置的地址发起post请求,将结果中的内容作为参与人。 :: @@ -158,7 +160,7 @@ wait的值可以是true或者false,如果wait的值是false那么工单触发hoo ori_str = timestamp + token signature = hashlib.md5(ori_str.encode(encoding='utf-8')).hexdigest() -hook触发时loonflow向hook_url服务端post请求时带的数据如下: +hook触发时loonflow向hook_url服务端post请求时带的数据如下(外部获取类型post请求数据格式也如此): :: @@ -171,6 +173,12 @@ hook触发时loonflow向hook_url服务端post请求时带的数据如下: "extra_info": "xxxx", // 此处如果你配置hook的时候指定了extra_info那么会有这个字段,如果没配置就没这个信息 } +对于hook类型你的hook服务端需要response status code 200, 内容为json格式,包含code,msg字段,code为0 表示hook服务端已成功收到请求,并正常处理。 +如{"code":200, "msg":"ok"},其中msg会被记录到工单的flowlog中,你可以在你处理出错时将出错原因填充到msg字段。 +对于外部获取类型,你的external_url的服务端需要response status code 200,内容为json格式,包含code,msg, data字段,如{"code":0, "msg":"xxx", +"data":"zhansgan,lisi"}, 其中data字段中包含的是你需要将此工单的处理人设置为哪些username, 多个username使用逗号隔开 + + 分配方式: 分配方式包括直接处理、主动接单、随机分配、全部处理。如果设置为直接处理,工单的当前处理人可以直接点击配置的流 转(如同意、拒绝、完成)来处理。如果设置为主动接单,则当前处理人需要先接单,然后才可以按照配置的流转来处理(表现形式为获取用 户可执行的操作接口只会返回接单这个流转,具体参考关于接单接口的纤细描述)。 如果设置为随机分配,那么系统会自动将工单处理人设置 diff --git a/tasks.py b/tasks.py index 14a837bc..b74d930a 100644 --- a/tasks.py +++ b/tasks.py @@ -257,7 +257,7 @@ def flow_hook_task(ticket_id): if participant_type_id != constant_service_ins.PARTICIPANT_TYPE_HOOK: return False, '' hook_config = state_obj.participant - hook_config_dict= json.loads(hook_config) + hook_config_dict = json.loads(hook_config) hook_url = hook_config_dict.get('hook_url') hook_token = hook_config_dict.get('hook_token') wait = hook_config_dict.get('wait')