first commit
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
from pydantic import Field, BaseModel,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_quantity(cls, v):
|
||||
"""验证计划到场人数必须为非负数"""
|
||||
if v < 0:
|
||||
raise HTTPException(status_code=400, detail='计划节点人数不能小于0')
|
||||
return v
|
||||
|
||||
class PowerResponse(BaseResponseWithID):
|
||||
"""
|
||||
力能模型
|
||||
"""
|
||||
code: Optional[str] = Field(
|
||||
"personnel",
|
||||
description="力能类型(人员模块拓展为力能模块,可以在本字段保存人员/机具等编码)",
|
||||
examples=["personnel"],
|
||||
)
|
||||
project_id: str = Field(..., description="所属项目", examples=["project123"])
|
||||
contract_id: Optional[str] = Field(
|
||||
None,
|
||||
description="所属合同(目前前端页面显示没有按这个字段,仅通过脚本绑定了epc合同,用于接口数据推送。后续行为待讨论)",
|
||||
examples=["contract456"],
|
||||
)
|
||||
show_name: str = Field("", description="展示名称", examples=["施工班组A"])
|
||||
status: Optional[int] = Field(None, description="人员状态", examples=[0])
|
||||
plan_attendance: int = Field(
|
||||
0,
|
||||
description="计划出勤人数(暂时定为最后一次报明细时的计划出勤人数)",
|
||||
examples=[10],
|
||||
)
|
||||
actual_daily_attendance: int = Field(0, description="实际日均出勤", examples=[8])
|
||||
attendance_rate: float = Field(0.0, description="出勤率", examples=[0.8])
|
||||
statistic_date: Optional[DateStr] = Field(
|
||||
None, description="统计节点", examples=["2024-05-31"]
|
||||
)
|
||||
plan_exit_date: Optional[DateStr] = Field(
|
||||
None, description="计划退场时间", examples=["2024-06-30"]
|
||||
)
|
||||
actual_exit_date: Optional[DateStr] = Field(
|
||||
None, description="实际退场时间", examples=["2024-07-05"]
|
||||
)
|
||||
plan_node: List[PlanNode] = Field(default_factory=list, description="计划节点列表")
|
||||
plan_start_date: Optional[DateStr] = Field(
|
||||
None, description="计划首批到场时间", examples=["2024-05-01"]
|
||||
)
|
||||
plan_start_quantity: int = Field(0, description="计划首批到场人数", examples=[5], ge=0)
|
||||
actual_start_date: Optional[DateStr] = Field(
|
||||
None, description="实际首批到场时间", examples=["2024-05-02"]
|
||||
)
|
||||
actual_start_quantity: Optional[int] = Field(
|
||||
None, description="实际首批到场人数", examples=[4]
|
||||
)
|
||||
|
||||
class PowerCreate(BaseModel):
|
||||
"""
|
||||
创建力能
|
||||
POWER_BASE_INFO = "personnel.base_info"
|
||||
"""
|
||||
project_id: str = Field(..., description="所属项目ID", examples=["project123"])
|
||||
contract_id: Optional[str] = Field(
|
||||
None, description="所属合同ID", examples=["contract456"]
|
||||
)
|
||||
#show_name: str = Field(..., description="展示名称", examples=["施工班组A"])
|
||||
code: Optional[str] = Field(
|
||||
"personnel",
|
||||
description="力能类型(人员模块拓展为力能模块,可以在本字段保存人员/机具等编码)",
|
||||
examples=["personnel"],
|
||||
)
|
||||
|
||||
class PowerUpdate(BaseModel):
|
||||
"""
|
||||
更新力能
|
||||
POWER_BASE_INFO = "personnel.base_info"
|
||||
"""
|
||||
id: str = Field(..., description="力能ID")
|
||||
contract_id: Optional[str] = Field(
|
||||
None, description="所属合同ID", examples=["contract456"]
|
||||
)
|
||||
#show_name: Optional[str] = Field(None, description="展示名称", examples=["施工班组A"])
|
||||
|
||||
class PowerPlanUpdate(BaseModel):
|
||||
"""
|
||||
力能计划更新模型
|
||||
POWER_PLAN = "personnel.plan"
|
||||
|
||||
业务逻辑说明:
|
||||
- API要求计划节点时间必须晚于计划首批到场时间(不能等于)
|
||||
- 自动清理脏数据:如果 plan_node 有2个或以上节点,删除边界节点
|
||||
"""
|
||||
id: str = Field(..., description="力能ID")
|
||||
plan_start_date: str = Field(..., description="计划首批到场时间")
|
||||
plan_start_quantity: int = Field(..., description="计划首批到场人数")
|
||||
plan_exit_date: DateStr = Field(
|
||||
..., description="计划退场时间", examples=["2024-06-30"]
|
||||
)
|
||||
plan_node: List[PlanNode] = Field(default_factory=list, description="计划节点列表")
|
||||
|
||||
@field_validator("plan_start_quantity")
|
||||
def validate_quantity(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_exit_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_exit_date and
|
||||
cleaned_nodes and
|
||||
cleaned_nodes[-1].date == self.plan_exit_date):
|
||||
cleaned_nodes.pop()
|
||||
|
||||
self.plan_node = cleaned_nodes
|
||||
return self
|
||||
@@ -0,0 +1,133 @@
|
||||
from pydantic import Field, BaseModel,field_validator
|
||||
from fastapi import HTTPException
|
||||
from ..common.datetime import get_current_date
|
||||
from typing import List, Optional, Literal
|
||||
from ..common.validators import DateStr
|
||||
from ..common.base import BaseResponseWithID
|
||||
from ..common.enums import ApprovalStatus
|
||||
from datetime import datetime
|
||||
|
||||
class PowerDetail(BaseResponseWithID):
|
||||
"""
|
||||
力能配置明细
|
||||
"""
|
||||
project_id: str = Field(..., description="所属项目", examples=["project123"])
|
||||
parent_id: str = Field(..., description="所属力能配置ID", examples=["personnel789"])
|
||||
date: DateStr = Field(..., description="本次日期", examples=["2024-05-31"])
|
||||
quantity: int = Field(..., description="累计完成量", examples=[0], ge=0)
|
||||
remark: Optional[str] = Field(None, description="填报备注", examples=[""])
|
||||
is_complete: Optional[bool] = Field(
|
||||
None, description="是否为全部退场", examples=[False]
|
||||
)
|
||||
change_count: int = Field(0, description="变更次数", examples=[0])
|
||||
status: Optional[int] = Field(None, description="进度状态", examples=[0])
|
||||
rate: Optional[float] = Field(None, description="出勤率", examples=[0])
|
||||
|
||||
class PowerDetailCreate(BaseModel):
|
||||
"""
|
||||
力能配置明细创建模型
|
||||
POWER_DETAIL = "power.detail"
|
||||
"""
|
||||
project_id: str = Field(..., description="所属项目", examples=["project123"])
|
||||
parent_id: str = Field(..., description="所属力能配置ID", examples=["personnel789"])
|
||||
date: DateStr = Field(..., description="本次日期", examples=["2024-05-31"])
|
||||
quantity: int = Field(..., description="累计完成量", examples=[0], ge=0)
|
||||
remark: Optional[str] = Field(None, description="填报备注", examples=[""])
|
||||
is_complete: Optional[bool] = Field(
|
||||
None, description="是否为全部退场", examples=[False]
|
||||
)
|
||||
confirmation_time: str | None = Field(None, description="确认时间", examples=["2025-12-17 11:13:49"])
|
||||
approval_time: str | None = Field(None, description="审批时间", examples=["2025-12-17 11:13:49"])
|
||||
approval_status: ApprovalStatus = Field("registered", description="审批状态", examples=["registered"])
|
||||
|
||||
@field_validator("approval_status")
|
||||
def validate_approval_status(cls, v):
|
||||
"""验证审批字段"""
|
||||
try:
|
||||
return ApprovalStatus(v) # 尝试转换
|
||||
except ValueError:
|
||||
raise HTTPException(400, f"审批状态 '{v}' 无效")
|
||||
|
||||
@field_validator("confirmation_time", "approval_time")
|
||||
def validate_time(cls, v):
|
||||
"""验证时间"""
|
||||
if v is None:
|
||||
return v
|
||||
try:
|
||||
datetime.strptime(v, "%Y-%m-%d %H:%M:%S")
|
||||
except ValueError:
|
||||
raise HTTPException(detail=f'{v}时间格式错误,应为:2025-12-17 11:13:49', status_code=400)
|
||||
return v
|
||||
|
||||
@field_validator("quantity")
|
||||
def validate_quantity(cls, v):
|
||||
"""验证实际完成量累计必须为非负数"""
|
||||
if v < 0:
|
||||
raise HTTPException(status_code=400, detail='实际完成量累计不能小于0')
|
||||
return v
|
||||
|
||||
@field_validator("date")
|
||||
def validate_date(cls, v):
|
||||
if v > get_current_date():
|
||||
raise HTTPException(detail='节点日期不能大于今天', status_code=400)
|
||||
return v
|
||||
|
||||
class PowerDetailUpdate(BaseModel):
|
||||
"""
|
||||
力能配置明细更新模型
|
||||
POWER_DETAIL = "power.detail"
|
||||
"""
|
||||
id: str = Field(..., description="力能配置明细ID")
|
||||
date: Optional[DateStr] = Field(None, description="本次日期", examples=["2024-05-31"])
|
||||
quantity: Optional[int] = Field(None, description="累计完成量", examples=[0], ge=0)
|
||||
remark: Optional[str] = Field(None, description="填报备注", examples=[""])
|
||||
is_complete: Optional[bool] = Field(
|
||||
None, description="是否为全部退场", examples=[False]
|
||||
)
|
||||
confirmation_time: str | None = Field(None, description="确认时间", examples=["2025-12-17 11:13:49"])
|
||||
approval_time: str | None = Field(None, description="审批时间", examples=["2025-12-17 11:13:49"])
|
||||
approval_status: ApprovalStatus | None = Field("registered", description="审批状态", examples=["registered"])
|
||||
|
||||
@field_validator("approval_status")
|
||||
def validate_approval_status(cls, v):
|
||||
"""验证审批字段"""
|
||||
if v is None:
|
||||
return v
|
||||
try:
|
||||
return ApprovalStatus(v) # 尝试转换
|
||||
except ValueError:
|
||||
raise HTTPException(400, f"审批状态 '{v}' 无效")
|
||||
|
||||
@field_validator("confirmation_time", "approval_time")
|
||||
def validate_time(cls, v):
|
||||
"""验证时间"""
|
||||
if v is None:
|
||||
return v
|
||||
|
||||
try:
|
||||
datetime.strptime(v, "%Y-%m-%d %H:%M:%S")
|
||||
except ValueError:
|
||||
raise HTTPException(detail=f'{v}时间格式错误,应为:2025-12-17 11:13:49', status_code=400)
|
||||
return v
|
||||
|
||||
@field_validator("quantity")
|
||||
def validate_quantity(cls, v):
|
||||
"""验证实际完成量累计必须为非负数"""
|
||||
if v is None:
|
||||
return None
|
||||
|
||||
if v < 0:
|
||||
raise HTTPException(status_code=400, detail='实际完成量累计不能小于0')
|
||||
return v
|
||||
|
||||
@field_validator("date")
|
||||
def validate_date(cls, v):
|
||||
if v is None:
|
||||
return None
|
||||
|
||||
if v > get_current_date():
|
||||
raise HTTPException(detail='节点日期不能大于今天', status_code=400)
|
||||
return v
|
||||
|
||||
class PowerDetailBulkUpdate(BaseModel):
|
||||
detail_list: List[PowerDetailUpdate]
|
||||
Reference in New Issue
Block a user