-
Notifications
You must be signed in to change notification settings - Fork 689
0.3.x文档
从1.x版本开始文档迁移至 https://loonflow.readthedocs.io/
a workflow engine base django 基于django的工作流引擎系统,通过http接口调用。 可以作为企业内部统一的工作流引擎,提供诸如权限申请、资源申请、发布申请、请假、报销、it服务等所有工作流场景的服务。如果有一定的开发能力建议只使用后端引擎功能,前端根据场景定制开发可分散于各个内部后台管理系统(如人事、运维、监控、cmdb等等)
本人2011年开始接触工作流,2013年开始开发工作流第一版本,至今经历了多个版本。目前着手开发一个开源版本,致力于提供企业统一工作流引擎方案
欢迎加入qq群一起交流工作流相关技术: 558788490
建议使用Centos,Redhat,Ubuntu这类linux操作系统 因为celery4以后不支持windows,所以状态脚本和通知脚本执行会无法使用。 可以参考此文档兼容下: 参考文档
推荐使用最新的版本.可以直接通过此链接下载release版本, 或者使用以下命令
# loonflow
git clone [email protected]:blackholll/loonflow.git
git checkout vx.x.x #(具体的版本号,如v0.3.15)拉取代码
# shutongflow: vue.js版本前端+django后端的调用方demo
https://github.com/youshutong2080/shutongFlow
# workflowdemo: bootstrap版本前后端(前后端为分离的调用方demo)
https://github.com/jimmy201602/workflowdemo
# 其他版本demo: 欢迎有兴趣的同学提供react版本调用方demo
# loonflow-helper: 一些脚本的demo,如通知脚本、执行脚本、用户同步脚本,欢迎大家一起完善(直接提pr)
https://github.com/blackholll/loonflow-helper
- 创建数据库并修改settings/dev.py中相应配置(数据库配置、redis地址配置、日志路径配置等等)
- 修改tasks.py中DJANGO_SETTINGS_MODULE对应的配置文件为实际使用的配置文件
- 创建python虚拟环境: python3.5
- 安装依赖包: pip install -r requirements/dev.txt
- 启动redis(用于生成唯一的工单流水号+celery异步任务[执行脚本、通知脚本])
- 初始化数据库
python manage.py makemigrations
python manage.py migrate
- 创建初始账户: python manage.py createsuperuser
- 启动开发环境: python manage.py runserver 如果需要启动在其他端口:python manage.py runserver 8888
- 启动celery任务: celery -A tasks worker -l info -Q loonflow
- 生产环境建议使用nginx+uwsgi的方式部署
- 创建数据库并修改settings/pro.py中相应配置(数据库配置、redis地址配置、日志路径配置等等)
- 修改tasks.py中DJANGO_SETTINGS_MODULE对应的配置文件为实际使用的配置文件
- 创建python虚拟环境:python3.5
- 安装依赖包: pip install -r requirements/pro.txt
- 启动redis(用于生成唯一的工单流水号+celery异步任务[执行脚本、通知脚本])
- 初始化数据库:
python manage.py makemigrations
python manage.py migrate
- 创建初始账户: python manage.py createsuperuser
- 启动celery任务: celery multi start -A tasks worker -l info -c 8 -Q loonflow --logfile=xxx.log --pidfile=xxx.pid # -c参数为启动的celery进程数, logfile为日志文件路径, pidfile为pid文件路径,可自行视情况调整
从v0.1.x-v.2.x升级。需要一些DDL操作
- workflow.models.Transition新增字段timer,新增字段attribute_type_id,condition_expression
- ticket.modles.TicketRecord新增script_run_last_result字段,新增is_end字段,新增is_rejected字段,新增multi_all_person字段
- 删除ticket.modles.TicketStateLastMan
- workfow.models.State新增remember_last_man_enable字段
- account.models.AppToken新增字段workflow_ids字段,用于给每个app授权可以访问的工作流资源(对应工作及对应的工单,升级后需要修改此配置).新增ticket_sn_prefix字段,用于支持配置工单流水号前缀
- workflow.models.Workflow新增字段limit_expression,用于新建工单权限的限制
- workflow.models.workflow新增字段notices,用于关联通知方式
- workflow.models新增表CustomNotice 用于支持自定义通知方式
- workflow.models.CustomField新增label字段用于调用方自行扩展
- 因为v0.3版本中username参数改成从header中获取,所以接口调用时需要将username通过header方式传递
- 为了脚本安全考虑,当状态的参与人类型为脚本时,参与人需要设置为脚本记录的id。 迁移时需要将这些状态的参与人从脚本名称改成脚本记录的id
工单:具体的待处理事项,用户新建的是工单,工单按照工作流的设计来实现不同状态不同处理人之间的流转
工作流:即工作流的设计,定义了工单的审批链、各状态的处理人、各状态可以执行的操作(提交、保存,处理完成,退回,关闭等等)、每个状态下显示哪些字段、哪些字段可以在哪些编辑
子工单:主要用于工单流转存在子集的情况,如在项目开发周期中存在项目周期和应用周期两个层级, 当项目处于开发中时,项目的多个涉及应用在项目开发中可能正处于不同的阶段(代码编写、静态扫描、单元测试、完成开发等状态)。当应用状态都完成开发时将触发项目的状态到提测中。在这个场景中应用的工单即为项目工单的子工单。 应用工单的父状态即为项目的“开发中”
子工作流:工作流的父子层级不体现在工作流记录中,而体现在状态记录中。在配置工作流时,可以给某个工作流的某个状态设置一个子工作流。可以在工作流的不同状态设置不同的子工作流。
流程图:为了方便用户了解工作流的流转规则,可以通过流程图的方式展示给用户,如下
转交:正常情况下工单的流转都是按照其对应工作流设定的规则来流转(状态、处理人类型、处理人等).在实际操作中,比如A提交了个工单,到达运维处理中状态,B接单处理,B在处理过程中发现自己其实处理不了,需要C才能处理。于是将工单转交给C。
加签:加签与转交不同。正常情况下工单的流转都是按照其对应工作流设定的规则来流转(状态、处理人类型、处理人等).在实际操作中,比如A提交了个工单,到达运维处理中状态,B接单处理,B在处理过程中发现需要C做些操作或者提供些信息,才能处理,于是将工单加签给C.C处理完成后工单处理人会回到B.于是B可以继续处理
工单自定义字段与工作流自定义字段的区别: workflow里面自定义字段规定工作流有哪些自定义的字段。比如配置一个请假的工作流。 需要有请假天数这个字段。工单里面的自定义字段 存的是自定义字段具体的值。 比如现在用于新建了一个请假工单,填写了请假天数。那么工单的自定义字段表中会保存这个值
工作流处理过程可以理解为工单状态的变化,如一个工作流处理过程中可以有:发起人新建中、发起人编辑中、部门经理审核中、技术人员处理中、发起人验证中、结束等状态,每个状态对应相应的处理人(如部门经理审核中这个状态下只有部门经理才可以处理该工单)。如用户在新建工单的时候处于“发起人新建中”,(用户)提交后工单处于“部门经理审核中”, 部门经理(即“部门经理审核中”状态的处理人)审批通过后,工单的状态变更为“技术人员处理中”。 注意:"转交"和"加签"使用场景不同,使用时前端需要做必要的说明,避免用户使用错误
LOONFLOW 分为两部分:
- 工作流配置的管理后台
- 提供http api供各个系统(如果oa、cmdb、运维系统、客服系统)调用以完成各自系统定制化的工单需求
.
├── apps
│ ├── account # 用户应用
│ ├── manage # 管理后台应用
│ ├── ticket # 工单应用
│ └── workflow # 工作流应用
├── docs # 文档目录,后续会将文档全部迁移到wiki中
│ ├── apis # 接口文档
│ ├── images # 相关图片
│ └── specs # 代码规范
├── loonflow
│ └── __init__.py
| └── url.py # url路由主入口
| └── wsgi.py # wsgi配置
├── media # 静态文件目录
│ ├── flowchart # 工作流流程图,用户上次的流程图,后续将弃用
│ ├── notice_script # 通知脚本目录
│ └── workflow_script # 工作流执行脚本目录
├── requirements # 依赖文件目录
│ ├── common.txt # 通用依赖
│ ├── dev.txt # 开发环境依赖
│ ├── pro.txt # 生产环境依赖
│ ├── test.txt # 测试环境依赖
├── service # 服务层
│ ├── account # 用户相关服务
│ ├── common # 通用服务
│ ├── manage # 管理后台相关服务
│ ├── permission # 权限相关服务
│ ├── ticket # 工单相关服务
│ └── workflow # 工作流相关服务
├── settings # 配置文件目录
│ └── __init__.py
│ └── common.py #通用配置
│ └── dev.py # 开发环境配置
│ └── prod.py # 生产环境配置
│ └── test.py # 测试环境配置
├── static # 静态文件,管理后台页面使用
│ ├── bower_components
│ ├── dist
│ └── plugins
├── templates # 模板文件,管理后台页面使用,因为管理后台未前后端分离,所以有模板文件
│ ├── admin
│ ├── doc
│ ├── user_and_permission
│ └── workflow
└── tests # 单元测试目录
├── test_models # model层测试
├── test_services # service层测试
└── test_views # view层测试
使用部署过程中创建的(python manage.py creatsuperuser)用户名密码 登录http://host_ip:port
同步账户中用户、角色、用户角色(用户具有的角色)、部门信息。 loonflow中工单的流转过程中需要根据用户的相关信息来确定新的处理人,因为不同公司用户组织架构信息保存方式各不一样(如ldap、AD或者直接保存在企业微信、钉钉等等),需要你自己编写用户组织信息的脚本,定时将你司的最新用户组织信息同步到loonflow中。可参考从AD中同步。 非常欢迎大家将自己的脚本pr到https://github.com/blackholll/loonflow-helper。
注意:
- loonflow中的用户只是用于流转的时候确定新的处理人,无需要用户登录loonflow的管理后台,所以同步脚本中往loonflow插入用户记录时,密码随便插入。 当然超级管理员除外(超级管理员可以通过python manage.py createsuperuser命令来创建)
- 用户表中 dept_id为loonflow的部门表中主键id, 非你司用户信息中的部门id。 同步部门时可以将你司部门id保存在loonflow部门表中的label字段中来关联. label字段建议使用字段的json格式,方便以后扩展。如{"source_dept_id":11}
处理人类型及处理人
1.个人
当选择类型为个人时,处理人输入框中请填写此人的username(loonflow用户表中的username字段的值)。如zhangsan
2.多人
当选择类型为多人时,处理人输入框中请填写这些人username(loonflow用户表中的username字段的值),以逗号隔开。如zhangsan,lisi,wangwu
3.部门
当选择类型为部门时,处理人输入框中请填写对应的部门的id(loonflow部门表中的主键id)
4.角色
当选择类型为角色时,处理人输入框中请填写对应角色的id(loonflow角色表中的主键id)
5.变量
当选择类型为变量时,处理人输入框中请填写creator(工单的创建人)、creator_tl(工单创建人的tl,tl的逻辑是:先获取工单创建人所在部门,然后获取该部门的approvel字段的值,如果为空则取leader字段的值为)。因为部门tl是一个人,但是审批的时候有时候会需要多个人来审批,所以用approvel来支持部门多审批人的情况
6.脚本
当选择类型为脚本时,处理人输入框中请填写脚本(工作流管理-执行脚本中)的id
7.工单字段
当选择类型为工单字段,处理人输入框中请填写该字段的标识。 如请假工单中自定义了一个字段proxy,后续某个状态的处理人是这个代理人,那么处理类型选择工单字段,处理人填写proxy
8.父工单字段
当选择类型为父工单字段,处理人输入框中请填写父工作流中定义的某个字段的标识
9. hook(v0.3.17新增的类型)
当选择类型为hook(工单到达此状态时,loonflow将对外发起一个post请求,请求接受方可以执行相应的动作)时,处理人输入框中需要填写{"hook_url":"http://xxx.com/xx", "hook_token":"xxxx", "wait":true}。其中含义如下:
hook_url:请求的地址
hook_token: 发起post请求时用来生成签名信息放到请求的header中,由被请求方分配,此签名是为了保证被请求方能够识别loonflow发起的请求,以保障安全性
wait: 可选true,false,为true时,loonflow发起hook请求后,立即流转到下个状态。为false时,需要等待被请求方回调后,才会流转
签名算法如下:
import time
timestamp = str(time.time())[:10]
ori_str = timestamp + hook_token
signature = hashlib.md5(ori_str.encode(encoding='utf-8')).hexdigest()
被调用方接收到post请求后,需要采用同样的算法得出signature,校验与请求header中的signature是否一致,且header中的timestamp是在特定的时间内(如2分钟内),以避免请求被恶意重放。被请求方返回格式需要为{"code":0, "msg":""},code为-1时被认为调用失败,工单记录中script_run_last_result将被置为False,在工单详情内容中将会体现,调用方应用可以在前端显示一个重试按钮,允许用户在页面重试。当wait为true时,该工单到达此状态时触发hook请求,将停留在此状态直到hook目标方回调,才会继续流转。回调请求格式见: 工单接口中的“工单hook回调”
10.无
当状态为初始状态或者结束状态时,处理人类型和处理人可以选择无和留空
11. 包含子工作流的需要设置处理人为loonrobot,以保障所有子工单结束时,父工单顺利流转到下个状态
工作流流转控制工单状态的变化,流转的名称即工单处理中的按钮的名称,用户点击工单后,系统通过此表中的配置获取到下个状态信息,以更新工单的状态以及做相应的其他操作(执行脚本、通知相关人员等等)
- 当某个状态的参与人配置为脚本时,其直连下个状态只能有一个。因为脚本执行完成后会只获取一个下个状态来自动流转
- 当状态下配置了子工作流,其处理人类型需要为个人,处理人为'loonrobot',.且其直连下个状态只能一个,因为所有子工单都结束后会自动流转到下个状态(只取一个)
- 一些内置字段不得作为工作流自定义字段,在设置自定义字段时,字段名字尽量特殊点,如yw_username, oa_title等。这些字段包括但不限于以下:
title,workflow_id, sn, state_id, parent_ticket_id, parent_ticket_state_id, participant_type_id, participant, relation, in_add_node, add_node_man, transition_id, suggestion, usernane, creator, gmt_created, gmt_modified, is_deleted
当工单自定义字段表(ticket_ticketcustomfield)中记录较多时,调用工单详情接口时会出现响应很慢的情况。可以给此表添加一条索引(ticket_id,field_key字段)
Loonflow作为工作流引擎,正确是的使用姿势是各个系统的后端通过http api调用按照各自的需求来完成工单展示、工单新建、工单处理逻辑 在loonflow的管理后台中"账户-调用token"中新新增记录.填写调用方app_name新增后会生成一个签名token.调用方将签名信息写到http header中来调用具体的api 签名算法如下:
import time
timestamp = str(time.time())[:10]
ori_str = timestamp + token
signature = hashlib.md5(ori_str.encode(encoding='utf-8')).hexdigest()
api调用:
import requests
headers = dict(signature=signature, timestamp=timestamp, appname=app_name, username=username)
# get
get_data = dict(per_page=20, category='all')
r = requests.get('http://127.0.0.1:8000/api/v1.0/tickets', headers=headers, params=get_data)
result = r.json()
# post
data = dict(target_username='lisi', suggestion='请协助提供更多信息')
r = requests.post('http://127.0.0.1:8000/api/v1.0/tickets/{ticket_id}/add_node', headers=headers, json=data)
result = r.json()
# patch
requsts.patch,传参同post
# put
requests.put, 传参同post
注意: 开发阶段如果需要在postman中测试接口,避免每次都需要重新生成签名,可以将service.permission.api_permission.ApiPermissionCheck中签名有效期改长些
- URL
api/v1.0/workflows
- method
get
- 使用场景
获取到工作流列表后,用户选择对应的工作流来新建对应的工单。如果需要多级类型,可以在调用方系统保存对应关系。如调用方的“权限申请-VPN权限申请“对应loonflow中id为1的workflow,调用方的“权限申请-服务器权限申请“对应loonflow中id为2的workflow
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
page | int | 否 | 页码,默认1 |
per_page | int | 否 | 每页个数,默认10 |
name | varchar | 否 | 支持根据workflow name模糊查询 |
- 返回数据
{
"code": 0,
"data": {
"total": 2,
"page": 1,
"per_page": 10,
"value": [{
"name": "请假申请",
"creator": "admin",
"description": "请假申请",
"gmt_created": "2018-04-23 20:49:32"
}, {
"name": "vpn申请",
"creator": "admin",
"description": "vpn权限申请",
"gmt_created": "2018-05-06 12:32:36"
}]
},
"msg": ""
}
- URL
api/v1.0/workflows/{workflow_id}/init_state
- method
get
- 请求参数
无
- 返回数据
{
"msg": "",
"code": 0,
"data": {
"order_id": 0,
"workflow_id": 1,
"name": "新建中",
"participant_type_id": 1,
"distribute_type_id": 1,
"participant": "wangfei",
"is_hidden": false,
"type_id": 1,
"gmt_created": "2018-04-23 20:53:33",
"id": 1,
"transition": [{
"transition_id": 1,
"transition_name": "提交"
}, {
"transition_id": 2,
"transition_name": "保存"
}],
"sub_workflow_id": 0,
"creator": "admin",
"label": {},
"field_list": [{
"order_id": 20,
"field_key": "title",
"field_attribute": 2,
"value": null,
"name": "标题",
"field_type_id": 5
}, {
"order_id": 35,
"field_key": "leave_proxy",
"field_attribute": 2,
"field_type_id": 60,
"field_value": null,
"field_name": "代理人",
"field_choice": {}
}, {
"order_id": 25,
"field_key": "leave_end",
"field_attribute": 2,
"field_type_id": 30,
"field_value": null,
"field_name": "结束时间",
"field_choice": {}
}, {
"order_id": 20,
"field_key": "leave_start",
"field_attribute": 2,
"field_type_id": 30,
"field_value": null,
"field_name": "开始时间",
"field_choice": {}
}, {
"order_id": 40,
"field_key": "leave_type",
"field_attribute": 2,
"field_type_id": 40,
"field_value": null,
"field_name": "请假类型",
"field_choice": {
"1": "年假",
"2": "调休",
"3": "病假",
"4": "婚假"
}
}, {
"order_id": 45,
"field_key": "leave_reason",
"field_attribute": 2,
"field_type_id": 55,
"field_value": null,
"field_name": "请假原因及相关附件",
"field_choice": {}
}, {
"order_id": 30,
"field_key": "leave_days",
"field_attribute": 2,
"field_type_id": 5,
"field_value": null,
"field_name": "请假天数(0.5的倍数)",
"field_choice": {}
}]
}
}
- URL
api/v1.0/workflows/states/{state_id}
- method
get
-
使用场景
-
请求参数 无
-
返回数据
{
code: 0,
data: {
participant: "zhangsan",
sub_workflow_id: 0,
is_hidden: false,
distribute_type_id: 1,
state_field: {
model: 1
},
order_id: 0,
creator: "admin",
type_id: 0,
label: {},
#自定义标签, json格式保存, 接口调用方可针对状态的标签字段灵活显示前端页面。 如可以设置为 {
'appdetail': 1,
'projectdetail': 2
}
表示这个状态下前端需要显示应用详情、 项目详情,含义和前端约定好就行。 loonflow只负责将配置的信息返回给前端
gmt_created: "2018-04-23 20:53:33",
participant_type_id: 1,
workflow_id: 1,
name: "新建中",
id: 1
},
msg: ""
}
- URL
api/v1.0/workflows/{workflow_id}/states
- method
get
- 使用场景
可用于用户查询工单列表时选择工作流类型后,显示该工作流类型拥有的状态,然后可以再根据工单当前状态来查询
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
page | int | 否 | 第几页,默认第一页 |
per_page | int | 否 | 每页多少行数据 |
- 返回数据 { code: 0, data: { "total": 2, "page": 1, "per_page": 10, "value": [{ "id": 1, "name": "发起人编辑中", "workflow_id": 1, "sub_workflow_id": 0, "is_hidden": 0, "order_id": 0, "participant_type_id": 5, "participant": "creator", "distribute_type_id": 1, "state_field_str": { "title": 2, "description": 2, }, "label": {}, "creator": "loonflow", "gmt_created": "2018-02-27 06:00:00" }] }, msg: ""
- URL
/api/v1.0/tickets
- method
GET
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
sn | varchar | 否 | 流水号,支持根据sn的前几位模糊查询 |
title | varchar | 否 | 工单标题,模糊查询 |
create_start | varchar | 否 | 创建时间起 |
create_end | varchar | 否 | 创建时间止 |
workflow_ids | varchar | 否 | 工作流ids,逗号隔开多个工作流id, 如"1,2,3" |
state_ids | varchar | 否 | 状态ids,逗号隔开多个状态id,如"1,2,3" |
ticket_ids | varchar | 否 | 工单ids, 逗号隔开多个id,如"1,2,3" |
reverse | varchar | 否 | 是否按照创建时间倒序,0或者1 |
page | int | 否 | 页码,默认1 |
per_page | int | 否 | 每页个数,默认10 |
is_end | int | 否 | 是否已经结束的工单,0(未结束),1(已结束)或者不提供(不过滤是否已经结束) |
is_rejected | int | 否 | 是否已被拒绝的工单,0(未被拒绝),1(被拒绝)或者不提供(不过滤是否已被拒绝) |
category | varchar | 是 | 类型('all':所有工单, 'owner':我创建的工单, 'duty':我的待处理工单, 'relation':我的关联工单[包括我新建的、我处理过的、曾经需要我处理过的工单。注意这里只考虑历史状态,工单将来状态的处理人不考虑]) |
- 返回数据
{
"msg": "",
"code": 0,
"data": {
"value": [{
"participant_info": {
"participant_type_id": 1,
"participant": "1",
"participant_name": "zhangsan",
"participant_type_name": "个人",
"participant_alias": "张三"
},
"gmt_created": "2018-05-15 07:16:38",
"parent_ticket_state_id": 0,
"state": {
"state_name": "发起人-确认中",
"state_id": 10
},
"creator": "lilei",
"parent_ticket_id": 0,
"title": "vpn申请",
"gmt_modified": "2018-05-22 07:26:54",
"workflow": {
"workflow_name": "vpn申请",
"workflow_id": 2
},
"sn": "loonflow_201805150001",
"id": 17
}],
"total": 1,
"page": 1,
"per_page": 10
}
}
- URL
/api/v1.0/tickets
- method
POST
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
workflow_id | int | 是 | 工作流id(工单关联的工作流的id) |
transition_id | int | 是 | 新建工单时候的流转id(通过workflow/{id}/init_state接口可以获取新建工单时允许的transition) |
parent_ticket_id | int | 否 | 父工单的id(用于子工单的逻辑,如果新建的工单是某个工单的子工单需要填写父工单的id) |
parent_ticket_state_id | varchar | 否 | 父工单的状态(子工单是和父工单的某个状态关联的) |
suggestion | varchar | 否 | 处理意见(与处理工单类型,用户在处理工单的时候点击了按钮操作 可以填写附加的一些意见如:麻烦尽快处理) |
其他必填字段 | NULL | 否 | 其他必填字段或可选字段(在配置工作流过程中,会配置工作流初始状态必填和可选的字段。在新建工单时候必须提供必填字段。如请假申请工单,配置了自定义字段请假天数days,工单初始状态也设置了days为必填,那么新建此类工单时候就必选提供days) |
- 返回数据
{
code: 0,
msg: "",
data: {
ticket_id : 1 # 新建的工单的工单id
}
}
- URL
/api/v1.0/tickets/{ticket_id}
- method
GET
-
请求参数 无
-
返回数据
{
"code": 0,
"msg": "",
"data": {
"value": {
"workflow_id": 2,
"in_add_node": true,
"gmt_created": "2018-05-15 07:16:38",
"id": 17,
"relation": "guiji,wangwu,lilei",
"title": "vpn\u7533\u8bf72",
"sn": "loonflow_201805150001",
"parent_ticket_id": 0,
"creator": "lilei",
"script_run_last_result": true,
"gmt_modified": "2018-05-22 07:26:54",
"is_rejected": false,
"multi_all_person": "{}",
"creator_info": {
"email": "[email protected]",
"alias": "\u674e\u78ca",
"dept_info": {
"creator_info": {
"creator_id": 1,
"creator_alias": "\u8d85\u7ea7\u7ba1\u7406\u5458"
},
"leader": "lilei",
"parent_dept_info": {
"parent_dept_name": "\u603b\u90e8",
"parent_dept_id": 1
},
"approver_info": [],
"parent_dept_id": 1,
"name": "\u6280\u672f\u90e8",
"is_deleted": false,
"creator": "admin",
"gmt_modified": "2018-05-09 06:45:27",
"label": "",
"id": 2,
"approver": "",
"gmt_created": "2018-04-14 23:37:06",
"leader_info": {
"leader_alias": "\u674e\u78ca",
"leader_username": "lilei"
}
},
"username": "lilei",
"phone": "13888888888",
"is_active": true
},
"participant_type_id": 3,
"state_id": 10,
"is_end": false,
"is_deleted": false,
"field_list": [{
"field_value": "loonflow_201805150001",
"label": {},
"boolean_field_display": {},
"field_type_id": 5,
"field_template": "",
"field_choice": {},
"field_key": "sn",
"field_attribute": 1,
"description": "\u5de5\u5355\u7684\u6d41\u6c34\u53f7",
"default_value": null,
"order_id": 10,
"field_name": "\u6d41\u6c34\u53f7"
}, {
"field_value": "\u53d1\u8d77\u4eba-\u786e\u8ba4\u4e2d",
"label": {},
"boolean_field_display": {},
"field_type_id": 5,
"field_template": "",
"field_choice": {},
"field_key": "state.state_name",
"field_attribute": 1,
"description": "\u5de5\u5355\u5f53\u524d\u72b6\u6001\u7684\u540d\u79f0",
"default_value": null,
"order_id": 41,
"field_name": "\u72b6\u6001\u540d"
}, {
"field_value": "\u603b\u90e8",
"label": {},
"boolean_field_display": {},
"field_type_id": 5,
"field_template": "",
"field_choice": {},
"field_key": "participant_info.participant_name",
"field_attribute": 1,
"description": "\u5de5\u5355\u7684\u5f53\u524d\u5904\u7406\u4eba",
"default_value": null,
"order_id": 50,
"field_name": "\u5f53\u524d\u5904\u7406\u4eba"
}, {
"field_value": "vpn\u7533\u8bf7",
"label": {},
"boolean_field_display": {},
"field_type_id": 5,
"field_template": "",
"field_choice": {},
"field_key": "workflow.workflow_name",
"field_attribute": 1,
"description": "\u5de5\u5355\u6240\u5c5e\u5de5\u4f5c\u6d41\u7684\u540d\u79f0",
"default_value": null,
"order_id": 60,
"field_name": "\u5de5\u4f5c\u6d41\u540d\u79f0"
}],
"parent_ticket_state_id": 0,
"add_node_man": "zhangsan",
"participant": "1"
}
}
}
- URL
/api/v1.0/tickets/{ticket_id}/transitions
- method
GET
- 请求参数
无
- 返回数据
{
msg: "",
data: {
value: [
{
transition_name: "提交",
field_require_check: true, # 默认为ture,如果此为否时, 不校验表单必填内容
transition_id: 1,
is_accept: false, # 不是接单,
in_add_node: false, # 不处于加签状态下
enable_alert: false, # 是否弹窗告警,可用于当用户点击此操作的时确定是否弹窗信息
alert_text: "" # 弹窗中的消息内容
},
{
transition_name: "保存",
field_require_check: true, # 默认为ture,如果此为否时, 不校验表单必填内容
transition_id: 2,
is_accept: false, # 不是接单,
in_add_node: false, # 不处于加签状态下
enable_alert: false, # 是否弹窗告警,可用于当用户点击此操作的时确定是否弹窗信息
alert_text: "" # 弹窗中的消息内容
}
]
},
code: 0
}
如果当前处理人超过一个人(处理人类型为多人,部门、角色都有可能实际为多个人),且当前状态的分配方式为主动接单,则会要求先接单,返回数据如下。处理时需要处理人先接单(点击接单按钮时 调用接单接口).
{
msg: "",
code: 0,
data: {
value: [
{
transition_id: 0,
transition_name: "接单",
is_accept: true, # 接单,
in_add_node: false,
field_require_check: false
}
]
}
}
当工单当前处于加签状态下,返回格式如下。 则用户点击“完成”按钮时,需要调用完成加签操作接口
{
msg: "",
code: 0,
data: {
value: [
{
transition_id: 0,
transition_name: "完成",
is_accept: false,
in_add_node: true, # 处于加签状态
field_require_check: false
}
]
}
}
- URL
/api/v1.0/tickets/{ticket_id}/accept
- method
post
-
使用场景 使用接口获取工单当前可以做的的操作后,如果data.value.is_accept==true,则需要用户先接单才能处理,即页面显示接单按钮,用户点击后调用接单接口,将工单的当前处理人设置该用户
-
请求参数 无
-
返回数据
{
"data": true,
"code": 0,
"msg": ""
}
- URL
api/v1.0/tickets/{ticket_id}/deliver
- method
post
- 使用场景
在工单处理界面可以显示一个按钮“转交”,当用户认为当前工单自己处理不了时,可以将工单转交给合适的人处理
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
target_username | varchar | 是 | 转交对象的用户名 |
suggestion | varchar | 否 | 转交意见 |
- 返回内容
{
"data": true,
"code": 0,
"msg": ""
}
- URL
api/v1.0/tickets/{ticket_id}/add_node
- method
post
- 使用场景
当用户A提交了一个权限申请工单,达到运维人员处理人中状态,作为运维人员的B在处理过程中发现需要C先处理或者提供一些必要的信息,B才能处理。 那么B在处理工单界面可以点击”加签“按钮,弹窗中选择C。 系统调用loonflow的加签接口将工单加签给C。C处理完后点击”完成“按钮,系统调用loonflow的加签完成接口, 工单处理人将回到B. 那么B就可以按照之前既定流程正常流转下去
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
target_username | varchar | 是 | 加签对象 |
suggestion | varchar | 否 | 加签意见 |
- 返回结果
{
"data": true,
"code": 0,
"msg": ""
}
- URL
api/v1.0/tickets/{ticket_id}/add_node_end
- method
post
-
使用场景 当A将工单加签给B.B在处理工单时候,界面将只显示“完成“按钮,点击后后端调用此接口,将工单基础表中的is_add_node设置为false
-
请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
suggestion | varchar | 否 | 加签完成意见 |
- 返回结果
{
"data": true,
"code": 0,
"msg": ""
}
- URL
api/v1.0/tickets/{ticket_id}
- method
patch
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
transition_id | int | 是 | 流转id |
suggestion | varchar | 否 | 处理意见(与处理工单类型,用户在处理工单的时候点击了按钮操作 可以填写附加的一些意见如:麻烦尽快处理) |
其他必填字段 | NULL | 否 | 其他必填字段或可选字段(在配置工作流过程中,会配置工作流每个状态的必填和可选的字段。在处理工单时候必须提供必填字段。如请假申请工单,配置了自定义字段请假天数days,工单初始状态也设置了days为必填,那么处理此类工单时候就必选提供days)。工单详情接口中有当前处理是时必选的字段 |
- 返回数据
{
msg: "",
data: "",
code: 0
}
- URL
api/v1.0/tickets/{ticket_id}/flowlogs
- method
get
- 请求参数
无
- 返回数据
{
msg: "",
data: {
total: 4,
value: [
{
state: {
state_name: "发起人-确认中",
state_id: 5
},
transition: {
transition_name: "确认完成",
transition_id: 5,
attribute_type_id: 3
},
ticket_id: 1,
participant_info: {
participant_email: "[email protected]",
participant_alias: "李磊",
participant_phone: "13888888888",
participant: "lilei",
participant_type_id: 1
},
gmt_modified: "2018-04-30 15:57:26",
gmt_created: "2018-04-30 15:56:02",
suggestion: "已经生效,感谢"
},
{
state: {
state_name: "技术人员-处理中",
state_id: 4
},
transition: {
transition_name: "处理完成",
transition_id: 4
},
ticket_id: 1,
participant_info: {
participant_email: "[email protected]",
participant_alias: "李磊",
participant_phone: "13888888888",
participant: "lilei",
participant_type_id: 1
},
gmt_modified: "2018-04-30 15:57:14",
gmt_created: "2018-04-30 15:55:32",
suggestion: "处理完成"
},
{
state: {
state_name: "TL审批中",
state_id: 3
},
transition: {
transition_name: "同意",
transition_id: 3
},
ticket_id: 1,
participant_info: {
participant_email: "[email protected]",
participant_alias: "李磊",
participant_phone: "13888888888",
participant: "lilei",
participant_type_id: 1
},
gmt_modified: "2018-04-30 15:57:00",
gmt_created: "2018-04-30 15:53:19",
suggestion: "同意处理"
},
{
state: {
state_name: "新建中",
state_id: 1
},
transition: {
transition_name: "提交",
transition_id: 1
},
ticket_id: 1,
gmt_modified: "2018-04-30 15:52:35",
gmt_created: "2018-04-10 17:39:33",
suggestion: "请尽快处理,谢谢"
}],
page: 1,
per_page: 10
},
code: 0
}
- URL
api/v1.0/tickets/{ticket_id}/flowsteps
- method
get
- 请求参数
无
- 返回数据
{
"data": {
"value": [{
"state_id": 17,
"state_flow_log_list": [],
"order_id": 0,
"state_name": "test11111"
}, {
"state_id": 18,
"state_flow_log_list": [],
"order_id": 0,
"state_name": "2233222"
}, {
"state_id": 6,
"state_flow_log_list": [{
"gmt_created": "2018-05-15 07:16:38",
"participant_info": {
"participant_alias": "李磊",
"participant_type_id": 1,
"participant": "lilei",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "",
"participant": "lilei",
"state_id": 6,
"participant_type_id": 1,
"transition": {
"transition_name": "提交",
"transition_id": 7
},
"id": 32,
"intervene_type_id": 0
}],
"order_id": 1,
"state_name": "发起人-新建中"
}, {
"state_id": 7,
"state_flow_log_list": [{
"gmt_created": "2018-05-15 07:20:40",
"participant_info": {
"participant_alias": "李磊",
"participant_type_id": 1,
"participant": "lilei",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意申请",
"participant": "lilei",
"state_id": 7,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 8
},
"id": 33,
"intervene_type_id": 0
}],
"order_id": 2,
"state_name": "发起人tl-审批中"
}, {
"state_id": 8,
"state_flow_log_list": [{
"gmt_created": "2018-05-16 06:42:00",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "接单处理",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "未知操作",
"transition_id": 0
},
"id": 36,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 06:49:55",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 37,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 06:57:31",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "接单处理",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "未知操作",
"transition_id": 0
},
"id": 38,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 06:57:36",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 39,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 06:58:41",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 40,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 07:01:53",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 41,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 07:03:34",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 43,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 07:04:45",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 45,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 07:31:29",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 47,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 23:21:00",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 49,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 23:24:03",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 51,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 23:24:44",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 53,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 23:33:26",
"participant_info": {
"participant_alias": "轨迹",
"participant_type_id": 1,
"participant": "guiji",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "同意",
"participant": "guiji",
"state_id": 8,
"participant_type_id": 1,
"transition": {
"transition_name": "同意",
"transition_id": 9
},
"id": 55,
"intervene_type_id": 0
}],
"order_id": 3,
"state_name": "运维人员-审批中"
}, {
"state_id": 9,
"state_flow_log_list": [{
"gmt_created": "2018-05-16 07:01:54",
"participant_info": {
"participant_phone": "",
"participant_alias": "demo_script.py",
"participant_email": "",
"participant_type_id": 6,
"participant": "demo_script.py"
},
"suggestion": "False\n",
"participant": "demo_script.py",
"state_id": 9,
"participant_type_id": 6,
"transition": {
"transition_name": "脚本执行完成",
"transition_id": 10
},
"id": 42,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 07:03:34",
"participant_info": {
"participant_phone": "",
"participant_alias": "demo_script.py",
"participant_email": "",
"participant_type_id": 6,
"participant": "demo_script.py"
},
"suggestion": "False\n",
"participant": "demo_script.py",
"state_id": 9,
"participant_type_id": 6,
"transition": {
"transition_name": "脚本执行完成",
"transition_id": 10
},
"id": 44,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 07:04:45",
"participant_info": {
"participant_phone": "",
"participant_alias": "demo_script.py",
"participant_email": "",
"participant_type_id": 6,
"participant": "demo_script.py"
},
"suggestion": "False\n",
"participant": "demo_script.py",
"state_id": 9,
"participant_type_id": 6,
"transition": {
"transition_name": "脚本执行完成",
"transition_id": 10
},
"id": 46,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 07:31:29",
"participant_info": {
"participant_phone": "",
"participant_alias": "demo_script.py",
"participant_email": "",
"participant_type_id": 6,
"participant": "demo_script.py"
},
"suggestion": "lilei\n",
"participant": "demo_script.py",
"state_id": 9,
"participant_type_id": 6,
"transition": {
"transition_name": "脚本执行完成",
"transition_id": 10
},
"id": 48,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 23:21:00",
"participant_info": {
"participant_phone": "",
"participant_alias": "demo_script.py",
"participant_email": "",
"participant_type_id": 6,
"participant": "demo_script.py"
},
"suggestion": "lilei\n",
"participant": "demo_script.py",
"state_id": 9,
"participant_type_id": 6,
"transition": {
"transition_name": "脚本执行完成",
"transition_id": 10
},
"id": 50,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 23:24:03",
"participant_info": {
"participant_phone": "",
"participant_alias": "demo_script.py",
"participant_email": "",
"participant_type_id": 6,
"participant": "demo_script.py"
},
"suggestion": "lilei\n",
"participant": "demo_script.py",
"state_id": 9,
"participant_type_id": 6,
"transition": {
"transition_name": "脚本执行完成",
"transition_id": 10
},
"id": 52,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 23:24:44",
"participant_info": {
"participant_phone": "",
"participant_alias": "demo_script.py",
"participant_email": "",
"participant_type_id": 6,
"participant": "demo_script.py"
},
"suggestion": "lilei\n",
"participant": "demo_script.py",
"state_id": 9,
"participant_type_id": 6,
"transition": {
"transition_name": "脚本执行完成",
"transition_id": 10
},
"id": 54,
"intervene_type_id": 0
}, {
"gmt_created": "2018-05-16 23:33:26",
"participant_info": {
"participant_phone": "",
"participant_alias": "demo_script.py",
"participant_email": "",
"participant_type_id": 6,
"participant": "demo_script.py"
},
"suggestion": "lilei\n",
"participant": "demo_script.py",
"state_id": 9,
"participant_type_id": 6,
"transition": {
"transition_name": "脚本执行完成",
"transition_id": 10
},
"id": 56,
"intervene_type_id": 0
}],
"order_id": 4,
"state_name": "授权脚本-自动执行中"
}, {
"state_id": 10,
"state_flow_log_list": [{
"gmt_created": "2018-05-17 06:45:58",
"participant_info": {
"participant_alias": "李磊",
"participant_type_id": 1,
"participant": "lilei",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "请处理",
"participant": "lilei",
"state_id": 10,
"participant_type_id": 1,
"transition": {
"transition_name": "转交操作",
"transition_id": 0
},
"id": 57,
"intervene_type_id": 1
}, {
"gmt_created": "2018-05-17 06:47:46",
"participant_info": {
"participant_alias": "张三",
"participant_type_id": 1,
"participant": "zhangsan",
"participant_phone": "13888888888",
"participant_email": "[email protected]"
},
"suggestion": "请协助处理",
"participant": "zhangsan",
"state_id": 10,
"participant_type_id": 1,
"transition": {
"transition_name": "加签操作",
"transition_id": 0
},
"id": 58,
"intervene_type_id": 2
}],
"order_id": 6,
"state_name": "发起人-确认中"
}, {
"state_id": 11,
"state_flow_log_list": [],
"order_id": 7,
"state_name": "结束"
}]
},
"msg": "",
"code": 0
}
- URL
api/v1.0/tickets/{ticket_id}/state
- method
put
- 使用场景
用于干预工单的当前状态,可以直接将工单状态修改为指定状态,系统会根据state_id获取对应的处理人信息
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
state_id | int | 是 | 目标状态id |
- 返回格式
{
msg: "",
data: "",
code: 0
}
- URL
api/v1.0/tickets/states
- method
get
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
ticket_ids | str | 是 | 工单ids,逗号隔开的字符串 |
- 返回数据
{
code: 0,
data: {
1: {
state_id: 1,
state_name: "发起人-编辑中"
},
2: {
state_id: 2,
state_name: "新建中"
}
},
msg: ""
}
- URL
api/v1.0/tickets/{ticket_id}/fields
- method
patch
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
需要修改值的字段的key1 | varchar | 是 | 如需要修改标题,则就是title |
需要修改值的字段的key2 | varchar | 是 | 如需要修改标题,则就是title |
- 返回数据
{
msg: "",
data: "",
code: 0
}
- 使用场景 当工单的脚本(或者hook[v0.3.17版本支持])执行失败后,工单详情接口中获取的数据中script_run_last_result为false.这时可以调用此接口重新执行或触发hook
- URL
api/v1.0/tickets/{ticket_id}/retry_script
-
method post
-
请求参数 无
-
返回数据
{
msg: "Ticket script retry start successful",
data: "",
code: 0
}
- URL
/api/v1.0/tickets/{ticket_id}/comments
- method
POST
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
suggestion | varchar | 否 | 处理意见(与处理工单类型,用户在处理工单的时候点击了按钮操作 可以填写附加的一些意见如:麻烦尽快处理) |
- 返回数据
{
code: 0,
msg: "",
data: ""
}
- URL
/api/v1.0/tickets/{ticket_id}/hook_call_back
- method
POST
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
result | boolean | 是 | hook任务执行是否成功 |
msg | varchar | 是 | hook执行输出信息,可留空'' |
field_value | dict object | 否 | 需要修改值的字段. 这些字段需要在状态表单设置中为可选或者必填 |
-
使用场景 当工作流状态设置处理人类型为hook,工单到达此状态时,会触发hook请求,被请求方可以执行一些操作,执行完成后回调用loonflow,告知loonflow任务执行结果,以触发loonflow中工单状态的流转(当hook配置中wait为true时,无需回调,hook发出后会立即触发流转)
-
返回数据
{
code: 0,
msg: "",
data: ""
}
- URL
/api/v1.0/tickets/{ticket_id}/participant_info
- method
GET
- 请求参数
无
-
使用场景 此接口将返回该工单当前的参与人详细信息,如果是部门或角色会返回对应部门角色下所有用户。调用方可基于此提供工单催办的功能。 用户在前端点击催办按钮,前端弹窗要求用户选择通知的类型:短信、邮件、微信、钉钉等等 以及需要的备注信息,然后调用方后端发送相应的通知消息给工单的当前处理人
-
返回数据
{
"msg": "",
"data": {
"participant_info_list": [{
"alias": "\u8d85\u7ea7\u7ba1\u7406\u5458",
"username": "admin",
"phone": "13888888888",
"email": "[email protected]"
}, {
"alias": "\u8f68\u8ff9",
"username": "guiji",
"phone": "13888888888",
"email": "[email protected]"
}, {
"alias": "\u674e\u78ca",
"username": "lilei",
"phone": "13888888888",
"email": "[email protected]"
}, {
"alias": "\u5f20\u4e09",
"username": "zhangsan",
"phone": "13888888888",
"email": "[email protected]"
}, {
"alias": "\u674e\u56db",
"username": "lisi",
"phone": "13888888888",
"email": "[email protected]"
}, {
"alias": "\u738b\u4e94",
"username": "wangwu",
"phone": "13888888888",
"email": "[email protected]"
}, {
"alias": "\u6770\u514b",
"username": "jack",
"phone": "13888888888",
"email": "[email protected]"
}],
"participant_username_list": ["admin", "guiji", "lilei", "zhangsan", "lisi", "wangwu", "jack"]
},
"code": 0
}
- URL
/api/v1.0/tickets/{ticket_id}/close
- method
POST
- 请求参数
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
suggestion | varchar | 否 | 关闭原因 |
- 使用场景
超级管理员在查看工单详情时,可以在界面上显示一个强制关闭工单的按钮,点击后调用关闭工单按钮,实现强制关闭工单
- 返回数据
{
code: 0,
msg: "",
data: ""
}
状态类型
1: 开始状态.工单的最初始状态,如发起人新建中
2: 结束状态.工单的最终状态,如完成、结束、关闭等等
分配方式
1: 主动接单。工单到达时如果当前处理人是多人,需要用户先接单再处理(避免多人同时处理。场景: 开发人员提交了一个定制化的机器的申请, 在运维人员处理中这个状态,此状态下配置的处理人是整个运维部门,那么所有运维都会看到这个工单,其中一个运维人员点击接单后代表其将为其服务。这时候其他人将在工单详情中看到处理人已经是这运维人员)
2: 直接处理。工单到达时如果当前处理人是多人,不需要先接单,谁都可以处理
3: 随机分配。工单到达时候,如果处理人为多人,那么系统将随机分配给某个人。如上面这个例子,系统将直接给工单的�当前处理人设置为随机的一名运维人员�(v0.2版本支持)
4: 全部处理。当设置成某个状态为全部处理时,工单在此状态下需要所有相关人员都处理完成后,才会进入到下个状态(v0.2版本支持)
处理人类型
1: 个人
2: 多人
3: 部门
4: 角色
5: 变量 如工单创建人、工单创建人leader
6: 脚本/机器人 执行脚本的情况
7: 工单字段 工单的某个字段(需要是用户名或者是逗号隔开的用户名),如工单的某个自定义字段是测试人员'devs',工单流转过程中其中一个状态是测试人员测试中,�那么那个状态的处理人类型可以为7, 处理人为'devs')
8: 父工单字段 父工单的某个字段(需要是用户名或者是逗号隔开的用户名),如上述项目和应用周期的工单,应用工单在某个状态下需要项目的负责人'po'审批,那么该状态的处理人类型可以为8,处理人为'po'
9:多人全部处理(处理人为多个,且每个人都需要处理),当状态处理人配置为全部处理,且处理人数大于1时,实际的处理人类型则为此
10. hook方式,当工单状态叨叨处理人类型配置为kook的状态时,loonflow将触发一个hook请求,被请求方可以执行有些自动化操作然后回调loonflow
流转类型
1: 常规流转
2: 定时器流转(在v0.2以后版本中支持)
自定义字段类型
5: 字符串
10: 整形
15: 浮点型
20: 布尔类型
25: 日期类型
30: 日期时间类型
35: 单选框radio
40: 多选框checkbox
45: 下拉列表
50: 多选的下拉列表
55: 文本域
60: 用户名(需要调用方系统自行处理用户列表,loonflow只保存用户名)
70: 多选用户名(需要调用方系统自行处理用户列表,loonflow只保存用户名,多人的情况使用逗号隔开)
80: 附件,多个附件使用逗号隔开。调用方自己实现上传功能,loonflow只保存文件路径
字段属性:
1: 只读 调用新建或处理工单的接口时如果传了设置为只读的字段的值,loonflow将忽略,不会更新工单此字段的值
2: 必填 调用新建或处理工单的接口时必须传递此字段的值,如果未提供则新建或处理工单接口将调用失败
3: 可选 调用新建或处理工单的接口时可传可不传此字段的值,如果传了此类型的字段,则loonflow将更新工单此字段的值
工单权限类别:
1: 用户当前拥有此工单的处理权限(因为随着工单的状态变化,权限也会相应变化)
2: 用户当前拥有此工单的查看权限(因为随着工单的状态变化,权限也会相应变化)
-
为什么没使用django rest framework
因为不使用外键(为什么不使用?可以百度搜下)且使用框架不够用灵活
-
为什么使用http api方式提供服务
loonflow的理念是:工单应该是嵌入到各个系统中(如oa,cmdb,运维平台、客服系统等等), 这些系统通过后端api调用loonflow。所以loonflow只有管理界面(v0.1版本直接使用django admin,后面会重写管理界面)。后续会提供几个调用方demo供大家参考。感谢@youshutong帮忙写的调用方demo(vue+django): https://github.com/youshutong2080/shutongFlow 另外帮忙jimmy201602写的demo(bootstrap+django): https://github.com/jimmy201602/workflowdemo
-
为何不建议调用方前端直接调用loonflow
调用方和loonflow之前需要做权限验证,签名算法考虑到安全只能写在调用方后端;作为引擎,loonflow不提供用户登录验证功能,只校验调用方的合法性,所以登录验证需要做在调用方自己的后端;每个调用方除了纯粹的工单的功能,还会需要一些额外的功能,比如根据自定义字段筛选工单列表,loonflow提供了工单列表的接,但是因为loonflow的自定义字段是纵表形式存储的,无法提供根据这些字段来筛选工单列表。如果需要自定义字段的筛选,需要调用方自己保存一份工单数据,用于筛选;比如需要做一个项目全生命周期管理的系统,需要用到工作流。 但是还有比如发布,获取人员信息、和其他系统交互、日志查看、项目数据统计等等功能。这些需要做在自己的后端
-
调用方是否需要保存工单的基础数据
根据情况而定,如果调用方在显示工单数据的时候需要显示更多相关信息,可以本地保存一份附属信息与loonflow中对应关系。针对本地保存的情况,如果涉及工单流转的字段(如参与人等),在本地修改时需要同时调用loonflow修改loonflow中保存的字段的值(v0.2版本会提供修改工单字段值的接口)
-
如何限制用户查看工单权限
默认会限制工单的查看权限(通过api获取工单详情时,只有username参数是工单相关人员时才能获取到数据)。如果需要放开限制,可以修改工作流配置中的“查看权限校验”为否。权限配置只针对工作流的,多个类型的工作流需要单独配置
-
为何需要同步用户及部门信息到loonflow
因为工单流转涉及到较多的用户信息获取,所以需要将用户信息(包括部门)同步到loonflow的账户系统中。同步部门信息的时候,如果发现部门被删除,建议修改部门名字,如前面加个 “已废弃:”,否则如果该部门存在某个工单的当前处理人的时候会有问题。用户离职的情况设置is_active=0.另外用户密码请随便填写(为了不允许普通用户登录)。管理员账户请通过python manage.py creatsuperuser来创建。只需要管理员实现一个同步脚本定时执行即可,其他调用方不用考虑此问题
-
如何支持根据工单的自定义字段查询
loonflow只提供工单基础字段的查询,如果需要针对自定义字段的查询,请在自己系统中保存一份工单数据(注意工单处理过程中,如果有字段修改,也需要更新自己系统中的数据)
-
工单列表支持排序
只支持根据创建时间排序。其他字段排序可以在调用方系统中保存一份数据来自己实现排序,然后只有在获取工单详情的时候调用loonflow接口
-
工单类型需要支持多级
比如需要支持“运维-权限申请-vpn权限申请”。 因为loonflow的工作流只有一级,如果需要支持多级类型,需要在调用方保存一份工单类型与loonflow工作流关联的数据。表字段可以如下:type_id, type_name, up_type_id, loonflow_workflow_id
-
如何实现工单的评分和处理优先级功能
因为不同公司对于评分的需求不同,如评分有1星、2星、3星,满意、及格、不合格。 如优先级有高、中、低,紧急、中等、不急等等。因此loonflow不提供通用功能。用户可以针对不同的工作流定义不同的自定义字段以表示评分或者优先级,自定义字段可以选择checkbox类型,也可以通过字段的标签灵活处理(前端根据约定好的标签,特殊显示)。 当然如果你这边的需求非常统一,你可以给loonflow的基础表中添加一个字段,以实现公用。不过修改此逻辑后,后续loonflow更新时需要特别注意下
您的支持是我最大的动力,欢迎支付宝扫码捐助