Skip to content

Commit

Permalink
fix(backend): 我的待办区分处理和协助页面 #8750
Browse files Browse the repository at this point in the history
# Reviewed, transaction id: 28615
  • Loading branch information
ygcyao committed Jan 7, 2025
1 parent b553a46 commit 9c190df
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 38 deletions.
8 changes: 8 additions & 0 deletions dbm-ui/backend/configuration/models/dba.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,11 @@ def get_biz_db_type_admins(cls, bk_biz_id: int, db_type: str) -> List[str]:
if db_type == admin["db_type"]:
return admin["users"] or DEFAULT_DB_ADMINISTRATORS
return DEFAULT_DB_ADMINISTRATORS

@classmethod
def get_dba_for_db_type(cls, bk_biz_id: int, db_type: str) -> List[str]:
"""获取主dba、备dba、二线dba人员"""
dba_list = cls.list_biz_admins(bk_biz_id)
dba_content = next((dba for dba in dba_list if dba["db_type"] == db_type), {"users": []})
users = dba_content.get("users", [])
return users[:1], users[1:2], users[2:]
13 changes: 12 additions & 1 deletion dbm-ui/backend/ticket/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class TicketListFilter(filters.FilterSet):
cluster = filters.CharFilter(field_name="cluster", method="filter_cluster", label=_("集群域名"))
todo = filters.CharFilter(field_name="todo", method="filter_todo", label=_("代办状态"))
ordering = filters.CharFilter(field_name="ordering", method="order_ticket", label=_("排序字段"))
is_assist = filters.BooleanFilter(field_name="is_assist", method="filter_is_assist", label=_("是否协助"))

class Meta:
model = Ticket
Expand All @@ -47,11 +48,21 @@ def filter_ids(self, queryset, name, value):
def filter_todo(self, queryset, name, value):
user = self.request.user.username
if value == "running":
todo_filter = Q(todo_of_ticket__operators__contains=user, todo_of_ticket__status__in=TODO_RUNNING_STATUS)
todo_filter = Q(
Q(todo_of_ticket__operators__contains=user) | Q(todo_of_ticket__helpers__contains=user),
todo_of_ticket__status__in=TODO_RUNNING_STATUS,
)
else:
todo_filter = Q(todo_of_ticket__done_by=user)
return queryset.filter(todo_filter).distinct()

def filter_is_assist(self, queryset, name, value):
user = self.request.user.username
# 根据 value 的值选择不同的字段
field = "helpers" if value else "operators"
todo_filter = Q(**{f"todo_of_ticket__{field}__contains": user}, todo_of_ticket__status__in=TODO_RUNNING_STATUS)
return queryset.filter(todo_filter).distinct()

def filter_status(self, queryset, name, value):
status = value.split(",")
status_filter = Q()
Expand Down
2 changes: 1 addition & 1 deletion dbm-ui/backend/ticket/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
from backend.ticket.exceptions import TicketFlowsConfigException
from backend.ticket.flow_manager.manager import TicketFlowManager
from backend.ticket.models import Flow, Ticket, TicketFlowsConfig, Todo
from backend.ticket.serializers import TodoSerializer
from backend.ticket.todos import BaseTodoContext, TodoActorFactory
from backend.ticket.todos.itsm_todo import ItsmTodoContext

Expand Down Expand Up @@ -287,6 +286,7 @@ def batch_process_todo(cls, user, action, operations):
@param action 动作
@param operations: todo列表,每个item包含todo id和params
"""
from backend.ticket.serializers import TodoSerializer

results = []
for operation in operations:
Expand Down
18 changes: 18 additions & 0 deletions dbm-ui/backend/ticket/migrations/0013_todo_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.25 on 2025-01-03 02:28

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("ticket", "0012_alter_ticket_remark"),
]

operations = [
migrations.AddField(
model_name="todo",
name="helpers",
field=models.JSONField(default=list, verbose_name="协助人"),
),
]
42 changes: 28 additions & 14 deletions dbm-ui/backend/ticket/models/todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,39 +39,52 @@ def exist_unfinished(self):
def get_operators(self, todo_type, ticket, operators):
# 获得提单人,dba,业务协助人. TODO: 后续还会细分主、备、二线DBA,以及明确区分协助人角色
creator = [ticket.creator]
dba = DBAdministrator.get_biz_db_type_admins(ticket.bk_biz_id, ticket.group)
# dba = DBAdministrator.get_biz_db_type_admins(ticket.bk_biz_id, ticket.group)
dba, second_dba, other_dba = DBAdministrator.get_dba_for_db_type(ticket.bk_biz_id, ticket.group)
biz_helpers = BizSettings.get_assistance(ticket.bk_biz_id)

# 构造单据状态与处理人之间的对应关系
# - 审批中:提单人可撤销,dba可处理,
# 考虑某些单据审批人是特定配置(数据导出 -- 运维审批),所以从ItsmBuilder获得审批人
# - 待执行:提单人 + 单据协助人
# - 待继续:dba + 提单人 + 单据协助人
# - 待补货:dba + 提单人 + 单据协助人
# - 已失败:dba + 提单人 + 单据协助人
# - 待执行:operators[提单人] + helpers[单据协助人]
# - 待继续:operators[提单人 + dba] + helpers[单据协助人 + second_dba + other_dba]
# - 待补货:operators[提单人 + dba] + helpers[单据协助人 + second_dba + other_dba]
# - 已失败:operators[提单人 + dba] + helpers[单据协助人 + second_dba + other_dba]
itsm_builder = BuilderFactory.get_builder_cls(ticket.ticket_type).itsm_flow_builder(ticket)
itsm_operators = itsm_builder.get_approvers().split(",")
todo_operators_map = {
TodoType.ITSM: itsm_builder.get_approvers().split(","),
TodoType.APPROVE: creator + biz_helpers,
TodoType.INNER_APPROVE: dba + creator + biz_helpers,
TodoType.RESOURCE_REPLENISH: dba + creator + biz_helpers,
TodoType.INNER_FAILED: dba + creator + biz_helpers,
TodoType.ITSM: itsm_operators[:1],
TodoType.APPROVE: creator,
TodoType.INNER_APPROVE: creator + dba,
TodoType.RESOURCE_REPLENISH: creator + dba,
TodoType.INNER_FAILED: creator + dba,
}
todo_helpers_map = {
TodoType.ITSM: itsm_operators[1:],
TodoType.APPROVE: biz_helpers,
TodoType.INNER_APPROVE: biz_helpers + second_dba + other_dba,
TodoType.RESOURCE_REPLENISH: biz_helpers + second_dba + other_dba,
TodoType.INNER_FAILED: biz_helpers + second_dba + other_dba,
}
# 按照顺序去重
operators = list(dict.fromkeys(operators + todo_operators_map.get(todo_type, [])))
return operators
helpers = [item for item in todo_helpers_map.get(todo_type, []) if item not in operators]
return creator, biz_helpers, helpers, operators

def create(self, **kwargs):
operators = self.get_operators(kwargs["type"], kwargs["ticket"], kwargs.get("operators", []))
creator, biz_helpers, helpers, operators = self.get_operators(
kwargs["type"], kwargs["ticket"], kwargs.get("operators", [])
)
kwargs["operators"] = operators
kwargs["helpers"] = helpers
todo = super().create(**kwargs)
send_msg_for_flow.apply_async(
kwargs={
"flow_id": todo.flow.id,
"flow_msg_type": FlowMsgType.TODO.value,
"flow_status": FlowMsgStatus.UNCONFIRMED.value,
"processor": ",".join(todo.operators),
"receiver": todo.creator,
"processor": ",".join(todo.operators + todo.helpers),
"receiver": ",".join(creator + biz_helpers),
}
)
return todo
Expand All @@ -86,6 +99,7 @@ class Todo(AuditedModel):
flow = models.ForeignKey("Flow", help_text=_("关联流程任务"), related_name="todo_of_flow", on_delete=models.CASCADE)
ticket = models.ForeignKey("Ticket", help_text=_("关联工单"), related_name="todo_of_ticket", on_delete=models.CASCADE)
operators = models.JSONField(_("待办人"), default=list)
helpers = models.JSONField(_("协助人"), default=list)
type = models.CharField(
_("待办类型"),
choices=TodoType.get_choices(),
Expand Down
8 changes: 6 additions & 2 deletions dbm-ui/backend/ticket/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class TicketSerializer(AuditedSerializer, serializers.ModelSerializer):
details = TicketDetailsSerializer(help_text=_("单据详情"))
# 额外补充展示字段
todo_operators = serializers.SerializerMethodField(help_text=_("处理人列表"))
todo_helpers = serializers.SerializerMethodField(help_text=_("协助人列表"))
status = serializers.SerializerMethodField(help_text=_("状态"), read_only=True)
status_display = serializers.SerializerMethodField(help_text=_("状态名称"))
ticket_type_display = serializers.SerializerMethodField(help_text=_("单据类型名称"))
Expand Down Expand Up @@ -125,8 +126,12 @@ def get_todo_operators(self, obj):
obj.running_todos = [todo for todo in obj.todo_of_ticket.all() if todo.status == TodoStatus.TODO]
return obj.running_todos[0].operators if obj.running_todos else []

def get_todo_helpers(self, obj):
obj.running_todo_helpers = [todo for todo in obj.todo_of_ticket.all() if todo.status == TodoStatus.TODO]
return obj.running_todo_helpers[0].helpers if obj.running_todo_helpers else []

def get_status(self, obj):
if obj.status == TicketStatus.RUNNING and obj.running_todos:
if obj.status == TicketStatus.RUNNING and (obj.running_todos or obj.running_todo_helpers):
obj.status = TicketStatus.INNER_TODO
return obj.status

Expand Down Expand Up @@ -230,7 +235,6 @@ class TodoSerializer(serializers.ModelSerializer):
单据序列化
"""

operators = serializers.JSONField(help_text=_("待办人列表"))
cost_time = serializers.SerializerMethodField(help_text=_("耗时"))

def get_cost_time(self, obj):
Expand Down
2 changes: 1 addition & 1 deletion dbm-ui/backend/ticket/todos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def process(self, username, action, params):
return
# 允许超级用户和操作人确认
is_superuser = User.objects.get(username=username).is_superuser
if not is_superuser and username not in self.todo.operators:
if not is_superuser and username not in self.todo.operators + self.todo.helpers:
raise TodoWrongOperatorException(_("{}不在处理人: {}中,无法处理").format(username, self.todo.operators))

# 执行确认操作
Expand Down
57 changes: 38 additions & 19 deletions dbm-ui/backend/ticket/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,12 @@ def _get_self_manage_tickets(cls, user):
Q(group=manage.db_type) & Q(bk_biz_id=manage.bk_biz_id) if manage.bk_biz_id else Q(group=manage.db_type)
for manage in DBAdministrator.objects.filter(users__contains=user.username)
]
ticket_filter = Q(creator=user.username) | reduce(operator.or_, manage_filters or [Q()])
# 除了user管理的单据合集,处理人及协助人也能管理自己的单据
todo_filters = Q(
Q(todo_of_ticket__operators__contains=user.username) | Q(todo_of_ticket__helpers__contains=user.username),
todo_of_ticket__status__in=TODO_RUNNING_STATUS,
)
ticket_filter = Q(creator=user.username) | todo_filters | reduce(operator.or_, manage_filters or [Q()])
return Ticket.objects.filter(ticket_filter)

def filter_queryset(self, queryset):
Expand Down Expand Up @@ -447,29 +452,43 @@ def get_tickets_count(self, request, *args, **kwargs):
"""
user = request.user.username
tickets = self._get_self_manage_tickets(request.user)
count_map = {count_type: 0 for count_type in CountType.get_values()}
exclude_values = {"MY_APPROVE", "SELF_MANAGE", "DONE"}

# 初始化 count_map
def initialize_count_map():
return {count_type: 0 for count_type in CountType.get_values() if count_type not in exclude_values}

results = {}

# 通用的函数来计算待办和协助状态
def calculate_status_count(field_name, relation_name):
status_counts = (
tickets.filter(
status__in=TICKET_TODO_STATUS_SET,
**{f"{relation_name}__{field_name}__contains": user},
**{f"{relation_name}__status__in": TODO_RUNNING_STATUS},
)
.distinct()
.values_list("status", flat=True)
)
count_map = initialize_count_map()
for sts, count in Counter(status_counts).items():
sts = CountType.INNER_TODO.value if sts == "RUNNING" else sts
count_map[sts] = count
return count_map

# 计算我的代办
results["Pending"] = calculate_status_count("operators", "todo_of_ticket")
# 计算我的协助
results["to_help"] = calculate_status_count("helpers", "todo_of_ticket")
# 我负责的业务
count_map[CountType.SELF_MANAGE] = tickets.count()
results[CountType.SELF_MANAGE] = tickets.count()
# 我的申请
count_map[CountType.MY_APPROVE] = tickets.filter(creator=user).count()
# 我的代办
todo_status = (
tickets.filter(
status__in=TICKET_TODO_STATUS_SET,
todo_of_ticket__operators__contains=user,
todo_of_ticket__status__in=TODO_RUNNING_STATUS,
)
.distinct()
.values_list("status", flat=True)
)
for sts, count in Counter(todo_status).items():
sts = CountType.INNER_TODO.value if sts == "RUNNING" else sts
count_map[sts] = count
results[CountType.MY_APPROVE] = tickets.filter(creator=user).count()
# 我的已办
count_map[CountType.DONE] = tickets.filter(todo_of_ticket__done_by=user).count()
results[CountType.DONE] = tickets.filter(todo_of_ticket__done_by=user).count()

return Response(count_map)
return Response(results)

@common_swagger_auto_schema(
operation_summary=_("查询集群变更单据事件"),
Expand Down

0 comments on commit 9c190df

Please sign in to comment.