更新登录页及优化相关功能

This commit is contained in:
焦龙言
2026-10-08 14:13:05 +08:00
parent 9a14b692e3
commit 56e646bcc1
139 changed files with 77587 additions and 811 deletions
@@ -1,4 +1,4 @@
from datetime import datetime
from datetime import date, datetime
from typing import Annotated, Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
@@ -16,6 +16,10 @@ def _ensure_unique_ids(value: list[int]) -> list[int]:
return value
def _normalize_fifo_reason(value: str | None) -> str | None:
return (value.strip() or None) if isinstance(value, str) else value
def _parse_page_query_date(value: str) -> datetime:
try:
parsed = datetime.strptime(value, '%Y-%m-%d')
@@ -98,10 +102,18 @@ class FillingCreateModel(BaseModel):
warehouse_id: int = Field(gt=0, description='仓库ID')
strain_id: int = Field(gt=0, description='菌种ID')
batch_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='生产批次')
production_date: date = Field(description='发货来源生产日期')
expiry_date: date = Field(description='有效期至')
package_spec: str = Field(min_length=1, max_length=64, pattern=r'\S', description='包装规格')
initial_packages: int = Field(strict=True, gt=0, description='初始包数')
initial_packages: Literal[100] = Field(description='初始包数(固定 100 包)')
idempotency_key: str = Field(min_length=1, max_length=64, pattern=r'\S', description='幂等键')
@model_validator(mode='after')
def validate_material_dates(self) -> 'FillingCreateModel':
if self.expiry_date < self.production_date:
raise ValueError('有效期不能早于生产日期')
return self
class ShipmentCreateModel(BaseModel):
"""整托发货草稿新增模型。"""
@@ -149,9 +161,41 @@ class UsageCreateModel(BaseModel):
)
qr_token: str = Field(min_length=1, max_length=128, pattern=r'\S', description='二维码固定标识')
line_id: int = Field(gt=0, description='产线ID')
tunnel_id: int = Field(gt=0, description='隧道ID')
line_id: int | None = Field(default=None, gt=0, description='产线ID(可选)')
used_packages: int = Field(strict=True, gt=0, description='本次使用包数')
idempotency_key: str = Field(min_length=1, max_length=64, pattern=r'\S', description='幂等键')
fifo_skip_reason: str | None = Field(default=None, max_length=500, description='跳过先到托盘原因')
_normalize_reason = field_validator('fifo_skip_reason', mode='before')(_normalize_fifo_reason)
class TunnelEntryCreateModel(BaseModel):
"""托盘扫码入隧命令模型。"""
model_config = ConfigDict(
alias_generator=to_camel,
from_attributes=True,
populate_by_name=True,
extra='forbid',
)
qr_token: str = Field(min_length=1, max_length=128, pattern=r'\S', description='二维码固定标识')
tunnel_id: int = Field(gt=0, description='隧道ID')
idempotency_key: str = Field(min_length=1, max_length=64, pattern=r'\S', description='幂等键')
fifo_skip_reason: str | None = Field(default=None, max_length=500, description='跳过先到托盘原因')
_normalize_reason = field_validator('fifo_skip_reason', mode='before')(_normalize_fifo_reason)
class TunnelEntrySuccessResponseModel(BaseModel):
"""托盘扫码入隧成功响应模型。"""
model_config = ConfigDict(alias_generator=to_camel, from_attributes=True, populate_by_name=True)
pallet_id: int = Field(gt=0, description='托盘ID')
tunnel_id: int = Field(gt=0, description='隧道ID')
trace_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='物料溯源号')
class UsageSuccessResponseModel(BaseModel):
@@ -244,6 +288,8 @@ class FillingModel(BaseModel):
strain_code_snapshot: str = Field(min_length=1, max_length=64, pattern=r'\S', description='菌种编码快照')
strain_name_snapshot: str = Field(min_length=1, max_length=100, pattern=r'\S', description='菌种名称快照')
batch_no: str = Field(min_length=1, max_length=64, pattern=r'\S', description='生产批次')
production_date: date | None = Field(default=None, description='生产日期;历史未登记时为空')
expiry_date: date | None = Field(default=None, description='有效期至;历史未登记时为空')
package_spec: str = Field(min_length=1, max_length=64, pattern=r'\S', description='包装规格')
initial_packages: int = Field(strict=True, gt=0, description='初始包数')
active_slot: Literal['ACTIVE'] | None = Field(default=None, description='活动装填槽')
@@ -293,6 +339,9 @@ class ShipmentModel(BaseModel):
confirm_request_hash: str | None = Field(default=None, max_length=64, description='确认请求摘要')
confirmed_by: int | None = Field(default=None, gt=0, description='确认人ID')
confirmed_at: datetime | None = Field(default=None, description='确认时间')
receipt_state: Literal['PENDING', 'RECEIVED', 'LEGACY'] | None = Field(default=None, description='收货核对状态')
received_by: int | None = Field(default=None, gt=0, description='收货核对人')
received_at: datetime | None = Field(default=None, description='实际收货核对时间')
create_by: str | None = Field(default=None, max_length=64, description='创建者')
create_time: datetime | None = Field(default=None, description='创建时间')
update_by: str | None = Field(default=None, max_length=64, description='更新者')
@@ -311,13 +360,17 @@ class UsageModel(BaseModel):
pallet_id: int = Field(gt=0, description='托盘ID')
active_filling_id: int = Field(gt=0, description='活动装填记录ID')
site_id: int = Field(gt=0, description='现场ID')
line_id: int = Field(gt=0, description='产线ID')
tunnel_id: int | None = Field(default=None, gt=0, description='隧道ID')
trace_no: str | None = Field(default=None, max_length=64, description='物料溯源号')
batch_no_snapshot: str | None = Field(default=None, max_length=64, description='生产批次快照')
line_id: int | None = Field(default=None, gt=0, description='产线ID(可选)')
used_packages: int = Field(strict=True, gt=0, description='本次使用包数')
remaining_packages_after: int = Field(strict=True, ge=0, description='使用后剩余包数')
idempotency_key: str = Field(min_length=1, max_length=64, pattern=r'\S', description='幂等键')
request_hash: str = Field(min_length=1, max_length=64, pattern=r'\S', description='请求摘要')
operator_id: int = Field(gt=0, description='操作人ID')
occurred_at: datetime = Field(description='发生时间')
fifo_skip_reason: str | None = Field(default=None, max_length=500, description='跳过先到托盘原因')
class ReturnItemModel(BaseModel):
@@ -329,6 +382,9 @@ class ReturnItemModel(BaseModel):
return_id: int = Field(gt=0, description='退回单ID')
pallet_id: int = Field(gt=0, description='托盘ID')
active_filling_id_snapshot: int | None = Field(default=None, gt=0, description='申请时活动装填记录快照')
tunnel_id_snapshot: int | None = Field(default=None, gt=0, description='申请时隧道ID快照')
trace_no_snapshot: str | None = Field(default=None, max_length=64, description='申请时物料溯源号快照')
batch_no_snapshot: str | None = Field(default=None, max_length=64, description='申请时生产批次快照')
remaining_packages_snapshot: int = Field(strict=True, ge=0, description='申请时剩余包数快照')
create_by: str | None = Field(default=None, max_length=64, description='创建者')
create_time: datetime | None = Field(default=None, description='创建时间')
@@ -420,6 +476,8 @@ class UsagePageQueryModel(BaseModel):
page_size: int = Field(default=10, gt=0, le=100, description='每页记录数')
usage_no: str | None = Field(default=None, min_length=1, max_length=64, pattern=r'\S', description='使用记录号')
site_id: int | None = Field(default=None, gt=0, description='现场ID')
tunnel_id: int | None = Field(default=None, gt=0, description='隧道ID')
trace_no: str | None = Field(default=None, min_length=1, max_length=64, pattern=r'\S', description='物料溯源号')
line_id: int | None = Field(default=None, gt=0, description='产线ID')
pallet_code: str | None = Field(default=None, min_length=1, max_length=64, pattern=r'\S', description='托盘编号')
begin_time: str | None = Field(default=None, min_length=1, pattern=r'\S', description='开始时间')
@@ -496,15 +554,20 @@ class PalletScanResponseModel(BaseModel):
pallet_id: int = Field(gt=0, description='托盘ID')
pallet_code: str = Field(min_length=1, max_length=64, pattern=r'\S', description='托盘编号')
qr_token: str | None = Field(default=None, min_length=1, max_length=128, description='解析后的固定二维码标识,写入时使用')
flow_state: PalletFlowState = Field(description='托盘流转状态')
warehouse_id: int | None = Field(default=None, gt=0, description='当前仓库ID')
site_id: int | None = Field(default=None, gt=0, description='当前现场ID')
tunnel_id: int | None = Field(default=None, gt=0, description='当前隧道ID')
trace_no: str | None = Field(default=None, max_length=64, description='当前物料溯源号')
current_line_id: int | None = Field(default=None, gt=0, description='当前绑定产线ID')
active_filling_id: int | None = Field(default=None, gt=0, description='当前活动装填记录ID')
strain_id: int | None = Field(default=None, gt=0, description='菌种ID')
strain_code: str | None = Field(default=None, max_length=64, description='菌种编码')
strain_name: str | None = Field(default=None, max_length=100, description='菌种名称')
batch_no: str | None = Field(default=None, max_length=64, description='生产批次')
production_date: date | None = Field(default=None, description='生产日期')
expiry_date: date | None = Field(default=None, description='有效期至')
package_spec: str | None = Field(default=None, max_length=64, description='包装规格')
capacity_packages: int = Field(strict=True, gt=0, description='容量包数')
remaining_packages: int = Field(strict=True, ge=0, description='当前剩余包数')