factory/mcp_server/tools/wprs.py

140 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from typing import Any, Literal
from django.db.models import Q
from apps.wpmw.models import Wpr, WprDefect
from mcp_server.context import authenticated_factory_user
from mcp_server.tools.common import json_safe_result, validate_result_size
WprLocation = Literal["all", "workshop", "warehouse", "unassigned"]
def _base_queryset():
return Wpr.objects.select_related(
"material",
"material_start",
"wm",
"mb",
"wpr_from",
)
def _wpr_summary(wpr: Wpr) -> dict[str, Any]:
material = wpr.material
return {
"id": str(wpr.id),
"number": wpr.number,
"number_out": wpr.number_out,
"version": wpr.version,
"state": wpr.state,
"state_name": wpr.get_state_display(),
"material": {
"id": str(material.id),
"name": material.name,
"model": material.model,
"specification": material.specification,
},
"workshop_batch": wpr.wm.batch if wpr.wm_id else None,
"warehouse_batch": wpr.mb.batch if wpr.mb_id else None,
"create_time": wpr.create_time,
"update_time": wpr.update_time,
}
def search_wprs(
query: str = "",
state: int | None = None,
material_id: str | None = None,
batch: str = "",
location: WprLocation = "all",
limit: int = 20,
) -> dict[str, Any]:
"""按编号、物料或批次搜索单件产品;仅提供只读摘要。"""
authenticated_factory_user()
if location not in {"all", "workshop", "warehouse", "unassigned"}:
raise ValueError(f"不支持的 WPR 位置:{location}")
safe_limit = max(1, min(limit, 100))
queryset = _base_queryset()
if query.strip():
keyword = query.strip()
queryset = queryset.filter(
Q(number__icontains=keyword)
| Q(number_out__icontains=keyword)
| Q(material__name__icontains=keyword)
| Q(material__model__icontains=keyword)
| Q(material__specification__icontains=keyword)
)
if state is not None:
queryset = queryset.filter(state=state)
if material_id:
queryset = queryset.filter(material_id=material_id)
if batch.strip():
queryset = queryset.filter(
Q(wm__batch__icontains=batch.strip())
| Q(mb__batch__icontains=batch.strip())
)
if location == "workshop":
queryset = queryset.filter(wm__isnull=False)
elif location == "warehouse":
queryset = queryset.filter(mb__isnull=False)
elif location == "unassigned":
queryset = queryset.filter(wm__isnull=True, mb__isnull=True)
items = [
_wpr_summary(wpr)
for wpr in queryset.distinct().order_by("number", "create_time")[:safe_limit]
]
result = json_safe_result({"items": items, "limit": safe_limit})
validate_result_size(result)
return result
def get_wpr(identifier: str) -> dict[str, Any]:
"""按 WPR ID、内部编号或对外编号读取单件详情。"""
authenticated_factory_user()
lookup = Q(number=identifier) | Q(number_out=identifier)
if identifier.isdigit():
lookup |= Q(pk=identifier)
wpr = _base_queryset().filter(lookup).order_by("-version", "-update_time").first()
if wpr is None:
raise ValueError(f"未找到 WPR{identifier}")
result = _wpr_summary(wpr)
material_start = wpr.material_start
result.update(
{
"material_start": (
{
"id": str(material_start.id),
"name": material_start.name,
"model": material_start.model,
"specification": material_start.specification,
}
if material_start
else None
),
"wpr_from": (
{"id": str(wpr.wpr_from.id), "number": wpr.wpr_from.number}
if wpr.wpr_from_id
else None
),
"oinfo": wpr.oinfo,
"data": wpr.data,
"pre_info": wpr.pre_info,
"defects": list(
WprDefect.objects.filter(wpr=wpr)
.select_related("defect")
.values("defect_id", "defect__name", "is_main")
),
}
)
result = json_safe_result(result)
validate_result_size(result)
return result
def register_wpr_tools(server) -> None:
server.tool()(search_wprs)
server.tool()(get_wpr)