diff --git a/dbm-ui/backend/configuration/constants.py b/dbm-ui/backend/configuration/constants.py index 0b8a8824bf..9a77ceadfe 100644 --- a/dbm-ui/backend/configuration/constants.py +++ b/dbm-ui/backend/configuration/constants.py @@ -113,6 +113,7 @@ class SystemSettingsEnum(str, StructuredEnum): AFFINITY = EnumField("AFFINITY", _("容灾要求(各个环境可能不同,比如SG为空)")) SYSTEM_MSG_TYPE = EnumField("SYSTEM_MSG_TYPE", _("系统消息通知方式")) PADDING_PROXY_CLUSTER_LIST = EnumField("PADDING_PROXY_CLUSTER_LIST", _("补全proxy的集群域名列表")) + EXCLUSIVE_TICKET_MAP = EnumField("EXCLUSIVE_TICKET_MAP", _("单据互斥表(全局)")) # ITSM配置 BK_ITSM_SERVICE_ID = EnumField("BK_ITSM_SERVICE_ID", _("DBM的流程服务ID")) ITSM_APPROVAL_KEY = EnumField("ITSM_APPROVAL_KEY", _("ITSM审批意见key")) diff --git a/dbm-ui/backend/configuration/models/system.py b/dbm-ui/backend/configuration/models/system.py index d26b3bd82d..9698f1b66c 100644 --- a/dbm-ui/backend/configuration/models/system.py +++ b/dbm-ui/backend/configuration/models/system.py @@ -117,7 +117,7 @@ def insert_setting_value(cls, key: str, value: Any, value_type: str = "str", use ) @classmethod - def get_external_whitelist_cluster_ids(cls) -> List[int]: + def get_external_whitelist_cluster_ids(cls) -> List: return [ conf["cluster_id"] for conf in cls.get_setting_value( diff --git a/dbm-ui/backend/ticket/apps.py b/dbm-ui/backend/ticket/apps.py index 70cc495143..8fedb2a5d2 100644 --- a/dbm-ui/backend/ticket/apps.py +++ b/dbm-ui/backend/ticket/apps.py @@ -18,9 +18,11 @@ def init_ticket_flow_config(sender, **kwargs): from backend.ticket.handler import TicketHandler + from backend.ticket.models import ClusterOperateRecord try: TicketHandler.ticket_flow_config_init() + ClusterOperateRecord.objects.get_exclusive_ticket_map(force=True) except Exception as err: # pylint: disable=broad-except: logger.warning(f"ticket_flow_config_init occur error, {err}") diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_dump_data.py b/dbm-ui/backend/ticket/builders/mysql/mysql_dump_data.py index 9b6f6efdc5..5d070b030d 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_dump_data.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_dump_data.py @@ -36,17 +36,16 @@ class MySQLDumpDataDetailSerializer(MySQLBaseOperateDetailSerializer): dump_data = serializers.BooleanField(help_text=_("是否导出表数据")) force = serializers.BooleanField(help_text=_("是否强制执行"), default=False) + def to_internal_value(self, data): + data = super().to_internal_value(data) + data["is_external"] = getattr(self.context["request"], "is_external", False) + return data + class MySQLDumpDataItsmFlowParamsBuilder(builders.ItsmParamBuilder): - def get_params(self): - params = super().get_params() - bk_biz_id = self.ticket.bk_biz_id - # 数据导出的审批人是该业务下的产品,如果没有产品则按照原来审批人 - approve_index = [field["key"] for field in params["fields"]].index("approver") - old_approver = params["fields"].pop(approve_index)["value"] - biz_productor = AppCache.get_app_attr_from_cc(bk_biz_id, attr_name="bk_biz_productor") or old_approver - params["fields"].append({"key": "approver", "value": biz_productor}) - return params + def get_approvers(self): + bk_biz_maintainer = AppCache.get_app_attr_from_cc(self.ticket.bk_biz_id, attr_name="bk_biz_maintainer") + return bk_biz_maintainer or super().get_approvers() class MySQLDumpDataFlowParamBuilder(builders.FlowParamBuilder): @@ -77,3 +76,10 @@ class MySQLDumpDataFlowBuilder(BaseMySQLTicketFlowBuilder): inner_flow_builder = MySQLDumpDataFlowParamBuilder inner_flow_name = _("数据导出执行") itsm_flow_builder = MySQLDumpDataItsmFlowParamsBuilder + + @property + def need_itsm(self): + # 1. 导出数据和表结构 :运维人员审批 + # 2.导出数据 :运维人员审批 + # 3.导出表结构 :正常情况 - 不需要审批,PO环境 - 运维人员审批 + return self.ticket.details["dump_data"] or self.ticket.details["is_external"] diff --git a/dbm-ui/backend/ticket/exclusive_ticket.xlsx b/dbm-ui/backend/ticket/exclusive_ticket.xlsx index 8087278412..ada7c063a1 100644 Binary files a/dbm-ui/backend/ticket/exclusive_ticket.xlsx and b/dbm-ui/backend/ticket/exclusive_ticket.xlsx differ diff --git a/dbm-ui/backend/ticket/models/ticket.py b/dbm-ui/backend/ticket/models/ticket.py index c96287be4d..ef40b5bb84 100644 --- a/dbm-ui/backend/ticket/models/ticket.py +++ b/dbm-ui/backend/ticket/models/ticket.py @@ -20,7 +20,8 @@ from backend import env from backend.bk_web.constants import LEN_L_LONG, LEN_LONG, LEN_NORMAL, LEN_SHORT from backend.bk_web.models import AuditedModel -from backend.configuration.constants import PLAT_BIZ_ID, DBType +from backend.configuration.constants import PLAT_BIZ_ID, DBType, SystemSettingsEnum +from backend.configuration.models import SystemSettings from backend.db_monitor.exceptions import AutofixException from backend.ticket.constants import ( EXCLUSIVE_TICKET_EXCEL_PATH, @@ -272,13 +273,13 @@ def filter_actives(self, cluster_id, *args, **kwargs): return self.filter(cluster_id=cluster_id, ticket__status=TicketFlowStatus.RUNNING, *args, **kwargs) def filter_inner_actives(self, cluster_id, *args, **kwargs): - """获取集群正在运行的inner flow的单据记录。此时认为集群会在互斥阶段""" + """获取集群正在 运行/失败 的inner flow的单据记录。此时认为集群会在互斥阶段""" # 排除特定的单据,如自身单据重试排除自身 exclude_ticket_ids = kwargs.pop("exclude_ticket_ids", []) return self.filter( cluster_id=cluster_id, flow__flow_type=FlowType.INNER_FLOW, - flow__status=TicketFlowStatus.RUNNING, + flow__status__in=[TicketFlowStatus.RUNNING, TicketFlowStatus.FAILED], *args, **kwargs, ).exclude(flow__ticket_id__in=exclude_ticket_ids) @@ -290,28 +291,33 @@ def get_cluster_operations(self, cluster_id, **kwargs): def has_exclusive_operations(self, ticket_type, cluster_id, **kwargs): """判断当前单据类型与集群正在进行中的单据是否互斥""" active_records = self.filter_inner_actives(cluster_id, **kwargs) + exclusive_ticket_map = self.get_exclusive_ticket_map() exclusive_infos = [] for record in active_records: active_ticket_type = record.ticket.ticket_type # 记录互斥信息。不存在互斥表默认为互斥 - if self.exclusive_ticket_map[ticket_type].get(active_ticket_type, True): + if exclusive_ticket_map[ticket_type].get(active_ticket_type, True): exclusive_infos.append({"exclusive_ticket": record.ticket, "root_id": record.flow.flow_obj_id}) return exclusive_infos - @property - def exclusive_ticket_map(self): - if hasattr(self, "_exclusive_ticket_map"): - return self._exclusive_ticket_map + @staticmethod + def get_exclusive_ticket_map(force=False): + """获取单据互斥状态表, force为True表示强制刷新""" + exclusive_map = SystemSettings.get_setting_value(key=SystemSettingsEnum.EXCLUSIVE_TICKET_MAP, default={}) + if exclusive_map and not force: + return exclusive_map - _exclusive_matrix = ExcelHandler.paser_matrix(EXCLUSIVE_TICKET_EXCEL_PATH) - _exclusive_ticket_map = defaultdict(dict) - for row_label, inner_dict in _exclusive_matrix.items(): + exclusive_map = defaultdict(dict) + exclusive_matrix = ExcelHandler.paser_matrix(EXCLUSIVE_TICKET_EXCEL_PATH) + for row_label, inner_dict in exclusive_matrix.items(): for col_label, value in inner_dict.items(): row_key, col_key = TicketType.get_choice_value(row_label), TicketType.get_choice_value(col_label) - _exclusive_ticket_map[row_key][col_key] = value == "N" + exclusive_map[row_key][col_key] = value == "N" - setattr(self, "_exclusive_ticket_map", _exclusive_ticket_map) - return self._exclusive_ticket_map + SystemSettings.insert_setting_value( + key=SystemSettingsEnum.EXCLUSIVE_TICKET_MAP, value=exclusive_map, value_type="dict" + ) + return exclusive_map class ClusterOperateRecord(AuditedModel):