diff --git a/dbm-ui/backend/flow/signal/handlers.py b/dbm-ui/backend/flow/signal/handlers.py index 0ec687707b..62c26d140d 100644 --- a/dbm-ui/backend/flow/signal/handlers.py +++ b/dbm-ui/backend/flow/signal/handlers.py @@ -10,19 +10,18 @@ """ import logging -from celery import current_app from django.utils import timezone from django.utils.translation import ugettext as _ -from backend.components.cmsi.handler import CmsiHandler from backend.db_dirty.handlers import DBDirtyMachineHandler from backend.flow.consts import StateType from backend.flow.engine.bamboo.engine import BambooEngine from backend.flow.models import FlowNode, FlowTree -from backend.ticket.constants import FlowCallbackType, FlowType, TicketFlowStatus +from backend.ticket.constants import FlowCallbackType, FlowMsgType, FlowType, TicketFlowStatus from backend.ticket.flow_manager.inner import InnerFlow from backend.ticket.flow_manager.manager import TicketFlowManager -from backend.ticket.models import Flow, Ticket +from backend.ticket.models import Ticket +from backend.ticket.tasks.ticket_tasks import send_msg_for_flow logger = logging.getLogger("flow") @@ -95,7 +94,15 @@ def callback_ticket(ticket_id, root_id): # 在认为inner flow执行结束情况下,执行inner flow的后继动作 if inner_flow_obj.status not in [TicketFlowStatus.PENDING, TicketFlowStatus.RUNNING]: - send_msg_for_flow.apply_async(args=[current_flow.id]) + send_msg_for_flow.apply_async( + kwargs={ + "flow_id": current_flow.id, + "flow_msg_type": FlowMsgType.DONE.value, + "flow_status": current_flow.status, + "processor": ticket.creator, + "receiver": ticket.creator, + } + ) inner_flow_obj.callback(callback_type=FlowCallbackType.POST_CALLBACK.value) # 如果flow type的类型为快速任务,则跳过callback @@ -105,32 +112,3 @@ def callback_ticket(ticket_id, root_id): if current_flow and current_flow.flow_obj_id == root_id: manager = TicketFlowManager(ticket=ticket) manager.run_next_flow() - - -@current_app.task(ignore_result=True) -def send_msg_for_flow(flow_id: int): - """ - 发送消息 - """ - flow = Flow.objects.get(id=flow_id) - inner_flow_obj = InnerFlow(flow_obj=flow) - ticket = flow.ticket - ticket_type = ticket.get_ticket_type_display() - - msg = ticket.send_msg_config or {} - msg.update( - { - "receiver__username": msg.get("receiver__username") or ticket.creator, - "title": _("DBM数据库管理 {ticket_type} 执行结果").format(ticket_type=ticket_type), - "content": _( - "{ticket_type} {flow_alias} 执行{flow_status}。\n" "单据详情:{ticket_url}\n" "任务详情:{flow_url}\n" - ).format( - ticket_type=ticket_type, - flow_alias=flow.flow_alias, - flow_status=flow.get_status_display(), - ticket_url=ticket.url, - flow_url=inner_flow_obj.url, - ), - } - ) - CmsiHandler.send_msg(msg) diff --git a/dbm-ui/backend/ticket/constants.py b/dbm-ui/backend/ticket/constants.py index 830cd06e67..ea643dc424 100644 --- a/dbm-ui/backend/ticket/constants.py +++ b/dbm-ui/backend/ticket/constants.py @@ -607,3 +607,15 @@ class OperateNodeActionType(str, StructuredEnum): class ItsmTicketNodeEnum(str, StructuredEnum): ApprovalOption = EnumField("审批意见", "审批意见") Remark = EnumField("备注", "备注") + + +class FlowMsgType(str, StructuredEnum): + DONE = EnumField(_("完成"), _("完成")) + TODO = EnumField(_("代办"), _("代办")) + PENDING = EnumField(_("待审批"), _("待审批")) + + +class FlowMsgStatus(str, StructuredEnum): + DONE = EnumField(_("完成"), _("完成")) + UNCONFIRMED = EnumField(_("待确认"), _("待确认")) + PENDING = EnumField(_("待审批"), _("待审批")) diff --git a/dbm-ui/backend/ticket/flow_manager/itsm.py b/dbm-ui/backend/ticket/flow_manager/itsm.py index 518f99f8d3..67346e57df 100644 --- a/dbm-ui/backend/ticket/flow_manager/itsm.py +++ b/dbm-ui/backend/ticket/flow_manager/itsm.py @@ -15,9 +15,10 @@ from backend.components import ItsmApi from backend.components.itsm.constants import ItsmTicketStatus -from backend.ticket.constants import TicketFlowStatus, TicketStatus +from backend.ticket.constants import FlowMsgStatus, FlowMsgType, TicketFlowStatus, TicketStatus from backend.ticket.flow_manager.base import BaseTicketFlow from backend.ticket.models import Flow +from backend.ticket.tasks.ticket_tasks import send_msg_for_flow from backend.utils.time import datetime2str, standardized_time_str @@ -98,4 +99,15 @@ def _url(self) -> str: def _run(self) -> str: data = ItsmApi.create_ticket(self.flow_obj.details) + # 异步发送待审批消息 + itsm_fields = {f["key"]: f["value"] for f in self.flow_obj.details["fields"]} + send_msg_for_flow.apply_async( + kwargs={ + "flow_id": self.flow_obj.id, + "flow_msg_type": FlowMsgType.PENDING.value, + "flow_status": FlowMsgStatus.PENDING.value, + "processor": itsm_fields["approver"], + "receiver": self.ticket.creator, + } + ) return data["sn"] diff --git a/dbm-ui/backend/ticket/models/todo.py b/dbm-ui/backend/ticket/models/todo.py index dde8f1880c..9624e7a5bf 100644 --- a/dbm-ui/backend/ticket/models/todo.py +++ b/dbm-ui/backend/ticket/models/todo.py @@ -17,8 +17,8 @@ from backend import env from backend.bk_web.constants import LEN_MIDDLE, LEN_SHORT from backend.bk_web.models import AuditedModel -from backend.components.cmsi.handler import CmsiHandler -from backend.ticket.constants import TicketFlowStatus, TodoStatus, TodoType +from backend.ticket.constants import FlowMsgStatus, FlowMsgType, TicketFlowStatus, TodoStatus, TodoType +from backend.ticket.tasks.ticket_tasks import send_msg_for_flow logger = logging.getLogger("root") @@ -29,21 +29,16 @@ def exist_unfinished(self): def create(self, **kwargs): todo = super().create(**kwargs) - ticket = todo.ticket - ticket_type = ticket.get_ticket_type_display() - - msg = ticket.send_msg_config or {} - msg.update( - { - "receiver__username": ",".join(todo.operators), - "title": _("DBM数据库管理 待办通知").format(ticket_type=ticket_type), - "content": _("有一条[{ticket_type}]待办需要您处理\n" "待办详情:{todo_url}\n").format( - ticket_type=ticket_type, - todo_url=todo.url, - ), + send_msg_for_flow.apply_async( + kwargs={ + "flow_id": todo.flow.id, + "flow_msg_type": FlowMsgType.TODO.value, + "flow_status": FlowMsgStatus.UNCONFIRMED.value, + "processor": todo.operators, + "receiver": todo.creator, + "detail_address": todo.url, } ) - CmsiHandler.send_msg(msg) return todo diff --git a/dbm-ui/backend/ticket/tasks/ticket_tasks.py b/dbm-ui/backend/ticket/tasks/ticket_tasks.py index 6b19216afc..4e346281bc 100644 --- a/dbm-ui/backend/ticket/tasks/ticket_tasks.py +++ b/dbm-ui/backend/ticket/tasks/ticket_tasks.py @@ -23,11 +23,12 @@ from django.utils.translation import ugettext_lazy as _ from backend import env -from backend.components import BKLogApi +from backend.components import BKLogApi, ItsmApi +from backend.components.cmsi.handler import CmsiHandler from backend.db_meta.enums import ClusterType, InstanceInnerRole -from backend.db_meta.models import Cluster, StorageInstance +from backend.db_meta.models import AppCache, Cluster, StorageInstance from backend.ticket.builders.common.constants import MYSQL_CHECKSUM_TABLE, MySQLDataRepairTriggerMode -from backend.ticket.constants import FlowErrCode, TicketType +from backend.ticket.constants import FlowErrCode, FlowType, TicketType from backend.ticket.exceptions import TicketTaskTriggerException from backend.ticket.flow_manager.inner import InnerFlow from backend.ticket.models.ticket import Flow, Ticket @@ -239,3 +240,70 @@ def apply_ticket_task( raise TicketTaskTriggerException(_("不支持的定时类型: {}").format(eta)) return res + + +@shared_task +def send_msg_for_flow( + flow_id: int, + flow_msg_type: str, + flow_status: str, + processor: str, + receiver: str = "", + detail_address: str = None, +): + """ + 异步发送消息通知 + @param flow_id: 流程ID + @param flow_msg_type: 流程类型 + @param flow_status: 流程状态 + @param receiver: 通知人(多个处理人用,分割) + @param processor: 处理人(多个处理人用,分割) + @param detail_address: 查看详情链接 + """ + flow = Flow.objects.get(id=flow_id) + ticket = flow.ticket + receiver = receiver or ticket.creator + ticket_type = ticket.get_ticket_type_display() + biz_name = AppCache.get_biz_name(ticket.bk_biz_id) + + # 通知模板 + content = _( + """ + 单据类型:{ticket_type} + 所属业务:{biz_name} + 提单人:{creator} + 提单时间:{submit_time} + 处理人:{processor} + 执行情况:{flow_status} + 查看详情:{detail_address} + """ + ).format( + ticket_type=ticket_type, + biz_name=biz_name, + creator=ticket.creator, + submit_time=ticket.create_at.astimezone(), + processor=processor, + flow_status=flow_status, + detail_address=detail_address or ticket.url, + ) + + if flow.flow_type == FlowType.BK_ITSM.value: + # 调用ITSM接口查询审批状态 + data = ItsmApi.ticket_approval_result({"sn": [flow.flow_obj_id]}, use_admin=True) + try: + approval_address = data[0]["ticket_url"] + except IndexError: + approval_address = "" + content += _("审批链接:{approval_address}").format(approval_address=approval_address) + + # 通知人 = 额外通知人 + 处理人 + 提单人 + receiver__username = set(f"{receiver},{processor},{ticket.creator}".split(",")) + msg = ticket.send_msg_config or {} + msg.update( + { + "receiver__username": ",".join(receiver__username), + "title": _("【数据库管理】 {flow_msg_type}通知").format(flow_msg_type=flow_msg_type), + "content": content, + } + ) + CmsiHandler.send_msg(msg)