diff --git a/apps/mtm/models.py b/apps/mtm/models.py index a2de8d52..c825c912 100644 --- a/apps/mtm/models.py +++ b/apps/mtm/models.py @@ -3,6 +3,7 @@ from apps.system.models import CommonAModel, Dictionary, CommonBModel, CommonADM from rest_framework.exceptions import ParseError from apps.utils.models import CommonBDModel from collections import defaultdict, deque +from django.db.models import Q class Process(CommonBModel): """ @@ -245,21 +246,23 @@ class Route(CommonADModel): parent = models.ForeignKey('self', verbose_name='上级路线', on_delete=models.CASCADE, null=True, blank=True) @staticmethod - def get_routes(material: Material): + def get_routes(material: Material=None, routepack:RoutePack=None): """ 返回工艺路线带车间(不关联工艺包) """ - kwargs = {'material': material, 'routepack__isnull': True} - # 校验工艺路线是否正常 - rq = Route.objects.filter( - **kwargs).order_by('sort', 'process__sort', 'create_time') - if not rq.exists(): - raise ParseError('未配置工艺路线') - if rq.first().material_in is None or rq.last().material_out is None or rq.last().material_out != rq.last().material: - raise ParseError('首步缺少输入/最后一步缺少输出') - if not rq.filter(is_count_utask=True).exists(): - raise ParseError('未指定统计步骤') - return rq + if material: + kwargs = {'material': material, 'routepack__isnull': True} + rqs = Route.objects.filter( + **kwargs).order_by('sort', 'process__sort', 'create_time') + # if not rq.exists(): + # raise ParseError('未配置工艺路线') + # if rq.first().material_in is None or rq.last().material_out is None or rq.last().material_out != rq.last().material: + # raise ParseError('首步缺少输入/最后一步缺少输出') + # if not rq.filter(is_count_utask=True).exists(): + # raise ParseError('未指定统计步骤') + elif routepack: + rqs = Route.objects.filter(routepack=routepack).order_by('sort', 'process__sort', 'create_time') + return rqs @classmethod def validate_dag(cls, final_material_out:Material, rqs): @@ -345,6 +348,47 @@ class Route(CommonADModel): return True + @classmethod + def get_dag(cls, rqs): + # 收集所有相关批次和边 + nodes_set = set() + edges = [] + + for rq in rqs: + source = rq.material_in.id + target = rq.material_out.id + nodes_set.update([source, target]) + edges.append({ + 'source': source, + 'target': target, + 'label': rq.process.name, + }) + + # 去重边 + unique_edges = {} + for edge in edges: + key = (edge['source'], edge['target']) + if key not in unique_edges: + unique_edges[key] = edge + # 将批次号排序 + nodes_qs = Material.objects.filter(id__in=nodes_set).order_by("process__sort", "create_time") + + # batch_to_id = {batch: idx for idx, batch in enumerate(nodes_list)} + # 构建节点数据,默认使用'rect'形状 + nodes = [{ + 'id': item.id, + 'label': str(item), + 'shape': 'rect' # 可根据业务需求调整形状 + } for item in nodes_qs] + + # 构建边数据 + edges_converted = [{ + 'source': edge['source'], + 'target': edge['target'], + 'label': edge['label'] + } for edge in unique_edges.values()] + + return {'nodes': nodes, 'edges': edges_converted} class RouteMat(BaseModel): route = models.ForeignKey(Route, verbose_name='关联路线', on_delete=models.CASCADE, related_name="routemat_route") material = models.ForeignKey(Material, verbose_name='辅助物料', on_delete=models.CASCADE) \ No newline at end of file diff --git a/apps/mtm/views.py b/apps/mtm/views.py index cb180172..802c9c71 100644 --- a/apps/mtm/views.py +++ b/apps/mtm/views.py @@ -283,7 +283,9 @@ class RoutePackViewSet(CustomModelViewSet): ins.save() return Response() - + @action(methods=['get'], detail=True, perms_map={'get': '*'}) + def dag(self, request, *args, **kwargs): + return Response(Route.get_dag(rqs=Route.objects.filter(routepack=self.get_object()))) class RouteViewSet(CustomModelViewSet): queryset = Route.objects.all() serializer_class = RouteSerializer diff --git a/apps/pm/services.py b/apps/pm/services.py index 2bfcd8c4..fd659fd7 100644 --- a/apps/pm/services.py +++ b/apps/pm/services.py @@ -167,9 +167,9 @@ class PmService: else: # 获取产品的加工路线 if utask.routepack: # 指定工艺路线 - rqs = Route.objects.filter(routepack=utask.routepack).order_by('sort', 'process__sort', 'create_time') + rqs = Route.get_routes(routepack=utask.routepack) else: - rqs = Route.get_routes(product) + rqs = Route.get_routes(material=product) if not rqs.exists(): raise ParseError('未配置工艺路线')