打通 T+ 快照、业务、发货、回款和核算准备链路,保留来源差异并提供待补状态。 新增业务自动接入任务、跨平台读取服务托管、权限复核、来源关联和前端核对页面。 补充迁移、隔离集成测试、浏览器模拟验收及角色授权边界修复。
178 lines
4.8 KiB
Python
178 lines
4.8 KiB
Python
"""T+ 管理接口合同;只暴露范围、数量和受控核验结果。"""
|
|
|
|
from datetime import datetime
|
|
from typing import Literal
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, Field, model_validator
|
|
|
|
from app.core.response import PageQuery
|
|
|
|
EntityType = Literal[
|
|
"personnel",
|
|
"products",
|
|
"customers",
|
|
"businesses",
|
|
"shipments",
|
|
"receipts",
|
|
"business_items",
|
|
"shipment_items",
|
|
"receipt_items",
|
|
]
|
|
TaskType = Literal["READ", "AUDIT"]
|
|
TaskStatus = Literal["QUEUED", "RUNNING", "SUCCEEDED", "PARTIAL", "FAILED"]
|
|
|
|
|
|
class TPlusCreateIn(BaseModel):
|
|
"""创建长任务;幂等键是调用意图的一部分,必须由调用方保留。"""
|
|
|
|
task_type: TaskType = "READ"
|
|
entity_types: list[EntityType] = Field(default_factory=list, max_length=9)
|
|
request_key: UUID
|
|
|
|
@model_validator(mode="after")
|
|
def check_scope(self):
|
|
if self.task_type == "READ" and not self.entity_types:
|
|
raise ValueError("请选择至少一种读取数据")
|
|
if self.task_type == "AUDIT" and self.entity_types:
|
|
raise ValueError("关联核验不接受自定义读取范围")
|
|
if len(set(self.entity_types)) != len(self.entity_types):
|
|
raise ValueError("读取类型不能重复")
|
|
return self
|
|
|
|
|
|
class TPlusRetryIn(BaseModel):
|
|
"""失败批次重试不接受任意新范围。"""
|
|
|
|
request_key: UUID
|
|
|
|
|
|
class TPlusBatchQuery(PageQuery):
|
|
"""批次筛选,与读取状态和任务类型保持一致。"""
|
|
|
|
status: TaskStatus | None = None
|
|
task_type: TaskType | None = None
|
|
|
|
|
|
class TPlusErrorQuery(PageQuery):
|
|
"""异常只能在指定批次内查询。"""
|
|
|
|
entity_type: EntityType | None = None
|
|
|
|
|
|
class TPlusEntityProgress(BaseModel):
|
|
entity_type: str
|
|
total_records: int | None = None
|
|
processed_records: int | None = None
|
|
status: str = "WAITING"
|
|
|
|
|
|
class TPlusProgressOut(BaseModel):
|
|
phase: str
|
|
total_records: int | None = None
|
|
processed_records: int = 0
|
|
percentage: float | None = None
|
|
active_entity: str | None = None
|
|
updated_at: datetime | None = None
|
|
elapsed_seconds: int = 0
|
|
entities: list[TPlusEntityProgress] = Field(default_factory=list)
|
|
|
|
|
|
class TPlusBatchOut(BaseModel):
|
|
"""批次列表不包含原始数据、源配置或数据库异常。"""
|
|
|
|
batch_no: str
|
|
task_type: TaskType
|
|
status: str
|
|
entity_types: list[str] | None
|
|
source_record_count: int
|
|
staged_record_count: int
|
|
skipped_record_count: int
|
|
failed_record_count: int
|
|
parent_batch_no: str | None
|
|
create_time: datetime
|
|
started_at: datetime | None
|
|
finished_at: datetime | None
|
|
can_retry: bool
|
|
error_summary: str | None = None
|
|
trigger_type: str = "MANUAL"
|
|
automatic_attempt: int = 0
|
|
progress: TPlusProgressOut | None = None
|
|
|
|
|
|
class TPlusBatchDetailOut(TPlusBatchOut):
|
|
"""详情按需携带受控的关联核验结果。"""
|
|
|
|
audit_result: dict | None = None
|
|
|
|
|
|
class TPlusErrorOut(BaseModel):
|
|
"""异常使用固定说明,源定位仅开放整数 ID。"""
|
|
|
|
id: int
|
|
entity_type: str
|
|
source_id: str | None
|
|
error_code: str
|
|
error_message: str
|
|
retry_count: int
|
|
create_time: datetime
|
|
|
|
|
|
class TPlusAutomationAlert(BaseModel):
|
|
code: str
|
|
message: str
|
|
batch_no: str | None = None
|
|
|
|
|
|
class TPlusAutomationUpdate(BaseModel):
|
|
enabled: bool = Field(strict=True)
|
|
expected_version: int = Field(ge=0, strict=True)
|
|
|
|
|
|
class TPlusRuntimeUpdate(BaseModel):
|
|
desired_state: Literal["RUNNING", "STOPPED"]
|
|
expected_version: int = Field(ge=0, strict=True)
|
|
|
|
|
|
class TPlusRuntimeOut(BaseModel):
|
|
desired_state: str = "STOPPED"
|
|
actual_state: str = "OFFLINE"
|
|
worker_phase: str = "UNKNOWN"
|
|
heartbeat_at: datetime | None = None
|
|
last_error: str | None = None
|
|
host: str | None = None
|
|
platform: str | None = None
|
|
version: int = 0
|
|
updated_by: int | None = None
|
|
editable: bool = False
|
|
|
|
|
|
class TPlusAutomationOverview(BaseModel):
|
|
enabled: bool = False
|
|
version: int = 0
|
|
editable: bool = False
|
|
configuration_origin: str = "ENVIRONMENT"
|
|
weekday: int = 0
|
|
time: str = "02:00"
|
|
timezone: str = "Asia/Shanghai"
|
|
worker_phase: str = "UNKNOWN"
|
|
heartbeat_at: datetime | None = None
|
|
last_success_at: datetime | None = None
|
|
next_retry_at: datetime | None = None
|
|
stop_requested: bool = False
|
|
alerts: list[TPlusAutomationAlert] = Field(default_factory=list)
|
|
|
|
|
|
class TPlusOverviewOut(BaseModel):
|
|
"""配置就绪不等同于源库连通或兼容验收通过。"""
|
|
|
|
is_configured: bool
|
|
configuration_message: str
|
|
status_counts: dict[str, int]
|
|
total_staged_records: int = 0
|
|
current_batch: TPlusBatchOut | None = None
|
|
compatibility_status: str = "PENDING"
|
|
business_import_enabled: bool = False
|
|
latest_audit: dict | None = None
|
|
automation: TPlusAutomationOverview = Field(default_factory=TPlusAutomationOverview)
|