first commit
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||
from fastapi import HTTPException
|
||||
from typing import List, Optional, Literal
|
||||
from ..common.validators import DateStr
|
||||
from ..common.base import BaseResponseWithID
|
||||
|
||||
class PlanNode(BaseModel):
|
||||
date: DateStr = Field(..., description="计划节点时间", examples=["2024-01-01"])
|
||||
quantity: float = Field(..., description="计划完成量", examples=[100.0])
|
||||
is_audit: Optional[bool] = Field(False, description="是否已审核", examples=[False])
|
||||
|
||||
@field_validator("quantity")
|
||||
def validate_price(cls, v):
|
||||
"""验证金额必须为非负数"""
|
||||
if v < 0:
|
||||
raise HTTPException(status_code=400, detail='计划完成量不能小于等于0')
|
||||
return v
|
||||
|
||||
class ConstructionResponse(BaseResponseWithID):
|
||||
"""
|
||||
施工任务模型,存储在CouchDB中的施工任务文档
|
||||
"""
|
||||
id : str = Field(..., description="施工任务ID")
|
||||
project_id: str = Field(..., description="所属项目ID", examples=["project123"])
|
||||
contract_id: Optional[str] = Field(None, description="所属合同ID", examples=["contract456"])
|
||||
show_name: str = Field(..., description="展示名称", examples=["桩基础施工任务"])
|
||||
construction_section: str = Field(..., description="施工区域", examples=["GF"])
|
||||
status: Optional[int] = Field(None, description="施工状态", examples=[0])
|
||||
task_type: str = Field(..., description="施工任务类型", examples=["zjcsgg"])
|
||||
plan_start_date: Optional[DateStr] = Field(None, description="计划开工时间", examples=["2024-01-01"])
|
||||
plan_complete_date: Optional[DateStr] = Field(None, description="计划完工时间", examples=["2024-06-01"])
|
||||
actual_complete_date: Optional[DateStr] = Field(None, description="实际完工时间", examples=["2024-05-30"])
|
||||
complete_quantity: float = Field(0.0, description="实际完成量累计", examples=[0.0])
|
||||
complete_rate: float = Field(0.0, description="实际完成率(%)", examples=[0.0])
|
||||
contract_quantity: Optional[float] = Field(None, description="设计量", examples=[1000.0])
|
||||
plan_node: List[PlanNode] = Field(default_factory=list, description="计划节点列表")
|
||||
statistic_date: Optional[DateStr] = Field(None, description="统计节点时间", examples=["2024-05-31"])
|
||||
# sort: int = Field(0, description="排序字段", examples=[0])
|
||||
is_crucial:Optional[bool] = Field(False, description="是否为关键线路")
|
||||
# 2025-10-20新增字段,延迟时间
|
||||
delay_days: Optional[int] = Field(None, description="延迟时间(天)", examples=[0])
|
||||
|
||||
class ConstructionCreate(BaseModel):
|
||||
"""
|
||||
施工任务创建模型
|
||||
不对应operation
|
||||
"""
|
||||
project_id: str = Field(..., description="所属项目ID", examples=["project123"])
|
||||
contract_id: Optional[str] = Field(None, description="所属合同ID", examples=["contract456"])
|
||||
show_name: str = Field(..., description="展示名称", examples=["桩基础施工任务"])
|
||||
construction_section: str = Field(..., description="施工区域", examples=["GF"])
|
||||
task_type: str = Field(..., description="施工任务类型", examples=["zjcsgg"])
|
||||
is_crucial:Optional[bool] = Field(False, description="是否为关键线路")
|
||||
|
||||
|
||||
|
||||
class ConstructionUpdate(BaseModel):
|
||||
"""
|
||||
施工任务更新模型
|
||||
CONSTRUCTION_BASE_INFO = "construction.base_info"
|
||||
"""
|
||||
id: str = Field(..., description="施工任务ID")
|
||||
contract_id: Optional[str] = Field(None, description="所属合同ID", examples=["contract456"])
|
||||
show_name: str | None = Field(None, description="展示名称(仅QT类施工可以修改名称)", examples=["桩基础施工任务"])
|
||||
is_crucial:bool | None = Field(False, description="是否为关键线路")
|
||||
|
||||
class ConstructionPlanUpdate(BaseModel):
|
||||
"""
|
||||
施工计划更新模型
|
||||
对应 CONSTRUCTION_PLAN = "construction.plan"
|
||||
|
||||
业务逻辑说明:
|
||||
- API要求计划节点时间必须晚于计划开工日期(不能等于)
|
||||
- 自动清理脏数据:如果 plan_node 有2个或以上节点,删除:
|
||||
1. 开头节点:如果其 date 等于 plan_start_date
|
||||
2. 结尾节点:如果其 date 等于 plan_complete_date
|
||||
- 这样避免提交包含边界节点的数据导致API报错
|
||||
"""
|
||||
id: str = Field(..., description="施工任务ID")
|
||||
plan_start_date: Optional[str] = Field(None, description="计划开工时间", examples=["2024-01-01"])
|
||||
plan_complete_date: Optional[str] = Field(None, description="计划完工时间", examples=["2024-01-01"])
|
||||
contract_quantity: float = Field(..., description="设计量")
|
||||
plan_node: Optional[List[PlanNode]] = Field(default_factory=list, description="计划节点列表")
|
||||
|
||||
@field_validator("contract_quantity")
|
||||
def validate_price(cls, v):
|
||||
"""验证金额必须为非负数"""
|
||||
if v < 0:
|
||||
raise HTTPException(status_code=400, detail='设计量不能小于0')
|
||||
return v
|
||||
|
||||
@model_validator(mode='after')
|
||||
def clean_plan_nodes(self):
|
||||
"""
|
||||
清理计划节点列表中的边界节点
|
||||
|
||||
如果 plan_node 有2个或以上节点,删除:
|
||||
- 开头节点:date 等于 plan_start_date
|
||||
- 结尾节点:date 等于 plan_complete_date
|
||||
|
||||
这避免了API报错"计划节点时间必须晚于计划开工日期"
|
||||
"""
|
||||
if not self.plan_node or len(self.plan_node) < 2:
|
||||
return self
|
||||
|
||||
cleaned_nodes = list(self.plan_node)
|
||||
|
||||
# 删除开头的边界节点
|
||||
if (self.plan_start_date and
|
||||
cleaned_nodes and
|
||||
cleaned_nodes[0].date == self.plan_start_date):
|
||||
cleaned_nodes.pop(0)
|
||||
|
||||
# 删除结尾的边界节点
|
||||
if (self.plan_complete_date and
|
||||
cleaned_nodes and
|
||||
cleaned_nodes[-1].date == self.plan_complete_date):
|
||||
cleaned_nodes.pop()
|
||||
|
||||
self.plan_node = cleaned_nodes
|
||||
return self
|
||||
Reference in New Issue
Block a user