Skip to content

Commit 9c190df

Browse files
committed
fix(backend): 我的待办区分处理和协助页面 #8750
# Reviewed, transaction id: 28615
1 parent b553a46 commit 9c190df

File tree

8 files changed

+112
-38
lines changed

8 files changed

+112
-38
lines changed

dbm-ui/backend/configuration/models/dba.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,11 @@ def get_biz_db_type_admins(cls, bk_biz_id: int, db_type: str) -> List[str]:
6060
if db_type == admin["db_type"]:
6161
return admin["users"] or DEFAULT_DB_ADMINISTRATORS
6262
return DEFAULT_DB_ADMINISTRATORS
63+
64+
@classmethod
65+
def get_dba_for_db_type(cls, bk_biz_id: int, db_type: str) -> List[str]:
66+
"""获取主dba、备dba、二线dba人员"""
67+
dba_list = cls.list_biz_admins(bk_biz_id)
68+
dba_content = next((dba for dba in dba_list if dba["db_type"] == db_type), {"users": []})
69+
users = dba_content.get("users", [])
70+
return users[:1], users[1:2], users[2:]

dbm-ui/backend/ticket/filters.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class TicketListFilter(filters.FilterSet):
2424
cluster = filters.CharFilter(field_name="cluster", method="filter_cluster", label=_("集群域名"))
2525
todo = filters.CharFilter(field_name="todo", method="filter_todo", label=_("代办状态"))
2626
ordering = filters.CharFilter(field_name="ordering", method="order_ticket", label=_("排序字段"))
27+
is_assist = filters.BooleanFilter(field_name="is_assist", method="filter_is_assist", label=_("是否协助"))
2728

2829
class Meta:
2930
model = Ticket
@@ -47,11 +48,21 @@ def filter_ids(self, queryset, name, value):
4748
def filter_todo(self, queryset, name, value):
4849
user = self.request.user.username
4950
if value == "running":
50-
todo_filter = Q(todo_of_ticket__operators__contains=user, todo_of_ticket__status__in=TODO_RUNNING_STATUS)
51+
todo_filter = Q(
52+
Q(todo_of_ticket__operators__contains=user) | Q(todo_of_ticket__helpers__contains=user),
53+
todo_of_ticket__status__in=TODO_RUNNING_STATUS,
54+
)
5155
else:
5256
todo_filter = Q(todo_of_ticket__done_by=user)
5357
return queryset.filter(todo_filter).distinct()
5458

59+
def filter_is_assist(self, queryset, name, value):
60+
user = self.request.user.username
61+
# 根据 value 的值选择不同的字段
62+
field = "helpers" if value else "operators"
63+
todo_filter = Q(**{f"todo_of_ticket__{field}__contains": user}, todo_of_ticket__status__in=TODO_RUNNING_STATUS)
64+
return queryset.filter(todo_filter).distinct()
65+
5566
def filter_status(self, queryset, name, value):
5667
status = value.split(",")
5768
status_filter = Q()

dbm-ui/backend/ticket/handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
from backend.ticket.exceptions import TicketFlowsConfigException
4141
from backend.ticket.flow_manager.manager import TicketFlowManager
4242
from backend.ticket.models import Flow, Ticket, TicketFlowsConfig, Todo
43-
from backend.ticket.serializers import TodoSerializer
4443
from backend.ticket.todos import BaseTodoContext, TodoActorFactory
4544
from backend.ticket.todos.itsm_todo import ItsmTodoContext
4645

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

291291
results = []
292292
for operation in operations:
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 3.2.25 on 2025-01-03 02:28
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("ticket", "0012_alter_ticket_remark"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="todo",
15+
name="helpers",
16+
field=models.JSONField(default=list, verbose_name="协助人"),
17+
),
18+
]

dbm-ui/backend/ticket/models/todo.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,39 +39,52 @@ def exist_unfinished(self):
3939
def get_operators(self, todo_type, ticket, operators):
4040
# 获得提单人,dba,业务协助人. TODO: 后续还会细分主、备、二线DBA,以及明确区分协助人角色
4141
creator = [ticket.creator]
42-
dba = DBAdministrator.get_biz_db_type_admins(ticket.bk_biz_id, ticket.group)
42+
# dba = DBAdministrator.get_biz_db_type_admins(ticket.bk_biz_id, ticket.group)
43+
dba, second_dba, other_dba = DBAdministrator.get_dba_for_db_type(ticket.bk_biz_id, ticket.group)
4344
biz_helpers = BizSettings.get_assistance(ticket.bk_biz_id)
4445

4546
# 构造单据状态与处理人之间的对应关系
4647
# - 审批中:提单人可撤销,dba可处理,
4748
# 考虑某些单据审批人是特定配置(数据导出 -- 运维审批),所以从ItsmBuilder获得审批人
48-
# - 待执行:提单人 + 单据协助人
49-
# - 待继续:dba + 提单人 + 单据协助人
50-
# - 待补货:dba + 提单人 + 单据协助人
51-
# - 已失败:dba + 提单人 + 单据协助人
49+
# - 待执行:operators[提单人] + helpers[单据协助人]
50+
# - 待继续:operators[提单人 + dba] + helpers[单据协助人 + second_dba + other_dba]
51+
# - 待补货:operators[提单人 + dba] + helpers[单据协助人 + second_dba + other_dba]
52+
# - 已失败:operators[提单人 + dba] + helpers[单据协助人 + second_dba + other_dba]
5253
itsm_builder = BuilderFactory.get_builder_cls(ticket.ticket_type).itsm_flow_builder(ticket)
54+
itsm_operators = itsm_builder.get_approvers().split(",")
5355
todo_operators_map = {
54-
TodoType.ITSM: itsm_builder.get_approvers().split(","),
55-
TodoType.APPROVE: creator + biz_helpers,
56-
TodoType.INNER_APPROVE: dba + creator + biz_helpers,
57-
TodoType.RESOURCE_REPLENISH: dba + creator + biz_helpers,
58-
TodoType.INNER_FAILED: dba + creator + biz_helpers,
56+
TodoType.ITSM: itsm_operators[:1],
57+
TodoType.APPROVE: creator,
58+
TodoType.INNER_APPROVE: creator + dba,
59+
TodoType.RESOURCE_REPLENISH: creator + dba,
60+
TodoType.INNER_FAILED: creator + dba,
61+
}
62+
todo_helpers_map = {
63+
TodoType.ITSM: itsm_operators[1:],
64+
TodoType.APPROVE: biz_helpers,
65+
TodoType.INNER_APPROVE: biz_helpers + second_dba + other_dba,
66+
TodoType.RESOURCE_REPLENISH: biz_helpers + second_dba + other_dba,
67+
TodoType.INNER_FAILED: biz_helpers + second_dba + other_dba,
5968
}
6069
# 按照顺序去重
6170
operators = list(dict.fromkeys(operators + todo_operators_map.get(todo_type, [])))
62-
return operators
71+
helpers = [item for item in todo_helpers_map.get(todo_type, []) if item not in operators]
72+
return creator, biz_helpers, helpers, operators
6373

6474
def create(self, **kwargs):
65-
operators = self.get_operators(kwargs["type"], kwargs["ticket"], kwargs.get("operators", []))
75+
creator, biz_helpers, helpers, operators = self.get_operators(
76+
kwargs["type"], kwargs["ticket"], kwargs.get("operators", [])
77+
)
6678
kwargs["operators"] = operators
79+
kwargs["helpers"] = helpers
6780
todo = super().create(**kwargs)
6881
send_msg_for_flow.apply_async(
6982
kwargs={
7083
"flow_id": todo.flow.id,
7184
"flow_msg_type": FlowMsgType.TODO.value,
7285
"flow_status": FlowMsgStatus.UNCONFIRMED.value,
73-
"processor": ",".join(todo.operators),
74-
"receiver": todo.creator,
86+
"processor": ",".join(todo.operators + todo.helpers),
87+
"receiver": ",".join(creator + biz_helpers),
7588
}
7689
)
7790
return todo
@@ -86,6 +99,7 @@ class Todo(AuditedModel):
8699
flow = models.ForeignKey("Flow", help_text=_("关联流程任务"), related_name="todo_of_flow", on_delete=models.CASCADE)
87100
ticket = models.ForeignKey("Ticket", help_text=_("关联工单"), related_name="todo_of_ticket", on_delete=models.CASCADE)
88101
operators = models.JSONField(_("待办人"), default=list)
102+
helpers = models.JSONField(_("协助人"), default=list)
89103
type = models.CharField(
90104
_("待办类型"),
91105
choices=TodoType.get_choices(),

dbm-ui/backend/ticket/serializers.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class TicketSerializer(AuditedSerializer, serializers.ModelSerializer):
9696
details = TicketDetailsSerializer(help_text=_("单据详情"))
9797
# 额外补充展示字段
9898
todo_operators = serializers.SerializerMethodField(help_text=_("处理人列表"))
99+
todo_helpers = serializers.SerializerMethodField(help_text=_("协助人列表"))
99100
status = serializers.SerializerMethodField(help_text=_("状态"), read_only=True)
100101
status_display = serializers.SerializerMethodField(help_text=_("状态名称"))
101102
ticket_type_display = serializers.SerializerMethodField(help_text=_("单据类型名称"))
@@ -125,8 +126,12 @@ def get_todo_operators(self, obj):
125126
obj.running_todos = [todo for todo in obj.todo_of_ticket.all() if todo.status == TodoStatus.TODO]
126127
return obj.running_todos[0].operators if obj.running_todos else []
127128

129+
def get_todo_helpers(self, obj):
130+
obj.running_todo_helpers = [todo for todo in obj.todo_of_ticket.all() if todo.status == TodoStatus.TODO]
131+
return obj.running_todo_helpers[0].helpers if obj.running_todo_helpers else []
132+
128133
def get_status(self, obj):
129-
if obj.status == TicketStatus.RUNNING and obj.running_todos:
134+
if obj.status == TicketStatus.RUNNING and (obj.running_todos or obj.running_todo_helpers):
130135
obj.status = TicketStatus.INNER_TODO
131136
return obj.status
132137

@@ -230,7 +235,6 @@ class TodoSerializer(serializers.ModelSerializer):
230235
单据序列化
231236
"""
232237

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

236240
def get_cost_time(self, obj):

dbm-ui/backend/ticket/todos/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def process(self, username, action, params):
5858
return
5959
# 允许超级用户和操作人确认
6060
is_superuser = User.objects.get(username=username).is_superuser
61-
if not is_superuser and username not in self.todo.operators:
61+
if not is_superuser and username not in self.todo.operators + self.todo.helpers:
6262
raise TodoWrongOperatorException(_("{}不在处理人: {}中,无法处理").format(username, self.todo.operators))
6363

6464
# 执行确认操作

dbm-ui/backend/ticket/views.py

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,12 @@ def _get_self_manage_tickets(cls, user):
155155
Q(group=manage.db_type) & Q(bk_biz_id=manage.bk_biz_id) if manage.bk_biz_id else Q(group=manage.db_type)
156156
for manage in DBAdministrator.objects.filter(users__contains=user.username)
157157
]
158-
ticket_filter = Q(creator=user.username) | reduce(operator.or_, manage_filters or [Q()])
158+
# 除了user管理的单据合集,处理人及协助人也能管理自己的单据
159+
todo_filters = Q(
160+
Q(todo_of_ticket__operators__contains=user.username) | Q(todo_of_ticket__helpers__contains=user.username),
161+
todo_of_ticket__status__in=TODO_RUNNING_STATUS,
162+
)
163+
ticket_filter = Q(creator=user.username) | todo_filters | reduce(operator.or_, manage_filters or [Q()])
159164
return Ticket.objects.filter(ticket_filter)
160165

161166
def filter_queryset(self, queryset):
@@ -447,29 +452,43 @@ def get_tickets_count(self, request, *args, **kwargs):
447452
"""
448453
user = request.user.username
449454
tickets = self._get_self_manage_tickets(request.user)
450-
count_map = {count_type: 0 for count_type in CountType.get_values()}
455+
exclude_values = {"MY_APPROVE", "SELF_MANAGE", "DONE"}
456+
457+
# 初始化 count_map
458+
def initialize_count_map():
459+
return {count_type: 0 for count_type in CountType.get_values() if count_type not in exclude_values}
451460

461+
results = {}
462+
463+
# 通用的函数来计算待办和协助状态
464+
def calculate_status_count(field_name, relation_name):
465+
status_counts = (
466+
tickets.filter(
467+
status__in=TICKET_TODO_STATUS_SET,
468+
**{f"{relation_name}__{field_name}__contains": user},
469+
**{f"{relation_name}__status__in": TODO_RUNNING_STATUS},
470+
)
471+
.distinct()
472+
.values_list("status", flat=True)
473+
)
474+
count_map = initialize_count_map()
475+
for sts, count in Counter(status_counts).items():
476+
sts = CountType.INNER_TODO.value if sts == "RUNNING" else sts
477+
count_map[sts] = count
478+
return count_map
479+
480+
# 计算我的代办
481+
results["Pending"] = calculate_status_count("operators", "todo_of_ticket")
482+
# 计算我的协助
483+
results["to_help"] = calculate_status_count("helpers", "todo_of_ticket")
452484
# 我负责的业务
453-
count_map[CountType.SELF_MANAGE] = tickets.count()
485+
results[CountType.SELF_MANAGE] = tickets.count()
454486
# 我的申请
455-
count_map[CountType.MY_APPROVE] = tickets.filter(creator=user).count()
456-
# 我的代办
457-
todo_status = (
458-
tickets.filter(
459-
status__in=TICKET_TODO_STATUS_SET,
460-
todo_of_ticket__operators__contains=user,
461-
todo_of_ticket__status__in=TODO_RUNNING_STATUS,
462-
)
463-
.distinct()
464-
.values_list("status", flat=True)
465-
)
466-
for sts, count in Counter(todo_status).items():
467-
sts = CountType.INNER_TODO.value if sts == "RUNNING" else sts
468-
count_map[sts] = count
487+
results[CountType.MY_APPROVE] = tickets.filter(creator=user).count()
469488
# 我的已办
470-
count_map[CountType.DONE] = tickets.filter(todo_of_ticket__done_by=user).count()
489+
results[CountType.DONE] = tickets.filter(todo_of_ticket__done_by=user).count()
471490

472-
return Response(count_map)
491+
return Response(results)
473492

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

0 commit comments

Comments
 (0)