fix(workbench): remove synthetic check projections
This commit is contained in:
@@ -5,7 +5,7 @@ from uuid import UUID
|
||||
from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictFloat, StrictInt, model_validator
|
||||
|
||||
from app.domain.schemas.shared import ObjectRef, TimeRange, WireDateTime
|
||||
from app.domain.schemas.workbench_runtime import WorkbenchRuntimeWorkOrderSubtask
|
||||
from app.domain.schemas.workbench_runtime import WorkOrderActivity
|
||||
|
||||
Text = Annotated[str, Field(min_length=1, max_length=4000)]
|
||||
PositiveInt = Annotated[StrictInt, Field(ge=1)]
|
||||
@@ -122,18 +122,6 @@ WorkOrderStatus = Literal[
|
||||
]
|
||||
Priority = Literal["low", "normal", "high", "urgent"]
|
||||
WorkOrderType = Literal["general", "water_quality_complaint", "burst_report"]
|
||||
WorkOrderSubtasks = Annotated[
|
||||
list[WorkbenchRuntimeWorkOrderSubtask], Field(min_length=1, max_length=50)
|
||||
]
|
||||
|
||||
|
||||
def _validate_unique_subtasks(
|
||||
subtasks: list[WorkbenchRuntimeWorkOrderSubtask] | None,
|
||||
) -> None:
|
||||
if subtasks is not None and len({item.id for item in subtasks}) != len(subtasks):
|
||||
raise ValueError("work order subtask ids must be unique")
|
||||
|
||||
|
||||
class CreateWorkOrder(Model):
|
||||
request_id: UUID
|
||||
title: Annotated[str, Field(min_length=1, max_length=200)]
|
||||
@@ -145,13 +133,12 @@ class CreateWorkOrder(Model):
|
||||
work_order_type: WorkOrderType = "general"
|
||||
location: Annotated[str, Field(min_length=1, max_length=200)] | None = None
|
||||
scenario_code: Annotated[str, Field(min_length=1, max_length=128)] | None = None
|
||||
subtasks: WorkOrderSubtasks | None = None
|
||||
feedback_requirements: list[Text] = Field(default_factory=list)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def aware_schedule(self):
|
||||
if self.scheduled_at is not None and self.scheduled_at.tzinfo is None:
|
||||
raise ValueError("scheduled_at must include timezone")
|
||||
_validate_unique_subtasks(self.subtasks)
|
||||
return self
|
||||
|
||||
|
||||
@@ -170,8 +157,8 @@ class UpdateWorkOrder(Model):
|
||||
clear_location: StrictBool = False
|
||||
scenario_code: Annotated[str, Field(min_length=1, max_length=128)] | None = None
|
||||
clear_scenario_code: StrictBool = False
|
||||
subtasks: WorkOrderSubtasks | None = None
|
||||
clear_subtasks: StrictBool = False
|
||||
feedback_requirements: list[Text] | None = None
|
||||
progress_note: Text | None = None
|
||||
|
||||
@model_validator(mode="after")
|
||||
def valid_update(self):
|
||||
@@ -186,11 +173,8 @@ class UpdateWorkOrder(Model):
|
||||
raise ValueError("clear_location conflicts with location")
|
||||
if self.clear_scenario_code and self.scenario_code is not None:
|
||||
raise ValueError("clear_scenario_code conflicts with scenario_code")
|
||||
if self.clear_subtasks and self.subtasks is not None:
|
||||
raise ValueError("clear_subtasks conflicts with subtasks")
|
||||
if self.scheduled_at is not None and self.scheduled_at.tzinfo is None:
|
||||
raise ValueError("scheduled_at must include timezone")
|
||||
_validate_unique_subtasks(self.subtasks)
|
||||
return self
|
||||
|
||||
|
||||
@@ -217,12 +201,15 @@ class WorkOrder(Model):
|
||||
work_order_type: WorkOrderType = "general"
|
||||
location: str | None = None
|
||||
scenario_code: str | None = None
|
||||
subtasks: WorkOrderSubtasks | None = None
|
||||
feedback_requirements: list[str] = Field(default_factory=list)
|
||||
activity: list[WorkOrderActivity] = Field(default_factory=list)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def unique_subtasks(self):
|
||||
_validate_unique_subtasks(self.subtasks)
|
||||
return self
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def accept_legacy_document(cls, value):
|
||||
if isinstance(value, dict):
|
||||
return {key: item for key, item in value.items() if key != "subtasks"}
|
||||
return value
|
||||
|
||||
|
||||
class WorkOrderPage(Model):
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
"""Stable identity used only by the explicit peripheral demo-data seed."""
|
||||
|
||||
from uuid import NAMESPACE_URL, uuid5
|
||||
|
||||
|
||||
SEED_NAMESPACE = uuid5(NAMESPACE_URL, "tjwater/peripheral-sample/seed/v1")
|
||||
SEED_ACTOR_ID = uuid5(SEED_NAMESPACE, "actor")
|
||||
@@ -24,6 +24,11 @@ from .schemas import (
|
||||
|
||||
TRANSITIONS = {"pending": ["processing"], "processing": ["pending_review"],
|
||||
"pending_review": ["processing", "completed"], "completed": []}
|
||||
WORK_ORDER_FIELD_LABELS = {
|
||||
"title": "标题", "summary": "说明", "status": "状态", "priority": "优先级",
|
||||
"assignee": "负责人", "scheduled_at": "预约时间", "location": "位置",
|
||||
"scenario_code": "场景编号", "feedback_requirements": "回执要求",
|
||||
}
|
||||
|
||||
|
||||
class PostgresStore:
|
||||
@@ -268,8 +273,13 @@ class PostgresStore:
|
||||
@staticmethod
|
||||
def _active_managed_document(document: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Project a current active row without legacy soft-delete metadata."""
|
||||
return {key: value for key, value in document.items()
|
||||
if key not in {"deleted_at", "deleted_by"}}
|
||||
active = {key: value for key, value in document.items()
|
||||
if key not in {"deleted_at", "deleted_by"}}
|
||||
if "work_order_id" in active:
|
||||
active.pop("subtasks", None)
|
||||
active.setdefault("feedback_requirements", [])
|
||||
active.setdefault("activity", [])
|
||||
return active
|
||||
|
||||
def _list_managed(self, who: Identity, table: str, id_column: str,
|
||||
limit: int, cursor: UUID | None) -> dict[str, Any]:
|
||||
@@ -314,6 +324,8 @@ class PostgresStore:
|
||||
"work_order_id": str(uuid4()), "project_id": who.project_id, "revision": 1,
|
||||
"origin": "synthetic", "created_at": now, "updated_at": now,
|
||||
"created_by": who.user_id, "updated_by": who.user_id,
|
||||
"activity": [{"sequence": 1, "occurred_at": now, "actor": who.user_id,
|
||||
"kind": "created", "title": "工单创建", "detail": "预约工单已创建。"}],
|
||||
}
|
||||
return self._save_work_order(conn, who, document)
|
||||
return self._mutate(who, "create_work_order", command, write)
|
||||
@@ -326,7 +338,7 @@ class PostgresStore:
|
||||
ensure(document["revision"] == command.expected_revision, "REVISION_CONFLICT", "工单版本已变化,请重新读取", 409)
|
||||
changes = command.model_dump(mode="json", exclude_unset=True,
|
||||
exclude={"request_id", "expected_revision", "clear_assignee", "clear_scheduled_at",
|
||||
"clear_location", "clear_scenario_code", "clear_subtasks"})
|
||||
"clear_location", "clear_scenario_code", "progress_note"})
|
||||
if command.clear_assignee:
|
||||
changes["assignee"] = None
|
||||
if command.clear_scheduled_at:
|
||||
@@ -335,10 +347,22 @@ class PostgresStore:
|
||||
changes["location"] = None
|
||||
if command.clear_scenario_code:
|
||||
changes["scenario_code"] = None
|
||||
if command.clear_subtasks:
|
||||
changes["subtasks"] = None
|
||||
changed_fields = [key for key, value in changes.items() if document.get(key) != value]
|
||||
document.update(changes)
|
||||
document.update(revision=document["revision"] + 1, updated_at=datetime.now(timezone.utc).isoformat(),
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
revision = document["revision"] + 1
|
||||
if command.progress_note:
|
||||
title, detail, kind = "现场反馈", command.progress_note, "feedback"
|
||||
elif changed_fields:
|
||||
title, detail, kind = "工单信息更新", "更新了" + "、".join(
|
||||
WORK_ORDER_FIELD_LABELS.get(field, field) for field in changed_fields
|
||||
) + "。", "updated"
|
||||
else:
|
||||
title, detail, kind = "工单信息确认", "工单信息已确认,未记录新的现场进展。", "updated"
|
||||
document["activity"].append({"sequence": revision, "occurred_at": now,
|
||||
"actor": who.user_id, "kind": kind,
|
||||
"title": title, "detail": detail})
|
||||
document.update(revision=revision, updated_at=now,
|
||||
updated_by=who.user_id)
|
||||
return self._save_work_order(conn, who, document)
|
||||
return self._mutate(who, f"update_work_order:{work_order_id}", command, write)
|
||||
|
||||
Reference in New Issue
Block a user