feat: 产品接口暴露lifecycle_phase并做阶段感知状态校验
This commit is contained in:
@ -6,6 +6,13 @@ from sqlalchemy import select, or_, cast, String, delete, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.core.lifecycle import (
|
||||
ALL_OVERALL_STEPS,
|
||||
allowed_steps,
|
||||
is_step_allowed,
|
||||
phase_label,
|
||||
resolve_phase_for_step,
|
||||
)
|
||||
from app.models.product import Product
|
||||
from app.models.production_order import ProductionOrder
|
||||
from app.models.task import Task
|
||||
@ -279,6 +286,7 @@ async def get_product_by_serial(db: AsyncSession, serial_number: str) -> Product
|
||||
current_location_id=product.current_location_id,
|
||||
overall_status=product.overall_status,
|
||||
status=product.status,
|
||||
lifecycle_phase=product.lifecycle_phase,
|
||||
created_at=product.created_at,
|
||||
top_level_tasks=[
|
||||
TaskSummaryResponse.model_validate(t) for t in top_tasks
|
||||
@ -365,6 +373,7 @@ async def create_product(db: AsyncSession, data: ProductCreate, creator_username
|
||||
current_location_name=creator_display_name or None,
|
||||
overall_status=product.overall_status,
|
||||
status=product.status,
|
||||
lifecycle_phase=product.lifecycle_phase,
|
||||
created_at=product.created_at,
|
||||
)
|
||||
|
||||
@ -413,11 +422,14 @@ async def update_product(db: AsyncSession, product_id: uuid.UUID, data: ProductU
|
||||
current_location_id=product.current_location_id,
|
||||
overall_status=product.overall_status,
|
||||
status=product.status,
|
||||
lifecycle_phase=product.lifecycle_phase,
|
||||
created_at=product.created_at,
|
||||
)
|
||||
|
||||
|
||||
VALID_OVERALL_STATUS = {"备货", "生产", "测试", "维修", "在库", "待仓库收货", "已入库", "已出库"}
|
||||
# 全部合法宏观状态(两阶段并集)— 仅作"完全非法取值"的第一道粗筛;
|
||||
# 阶段内的细分校验(售后回流设备禁止改回「备货 / 生产」)见下方 is_step_allowed
|
||||
VALID_OVERALL_STATUS = ALL_OVERALL_STEPS
|
||||
|
||||
|
||||
async def update_overall_status(
|
||||
@ -434,7 +446,7 @@ async def update_overall_status(
|
||||
if status_value not in VALID_OVERALL_STATUS:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"无效状态: {status_value},合法值: {', '.join(sorted(VALID_OVERALL_STATUS))}",
|
||||
detail=f"无效状态: {status_value},合法值: {'、'.join(sorted(ALL_OVERALL_STEPS))}",
|
||||
)
|
||||
|
||||
result = await db.execute(
|
||||
@ -480,6 +492,19 @@ async def update_overall_status(
|
||||
detail="只有 SUPER_ADMIN 或当前操作该产品主线任务的人才能修改宏观状态",
|
||||
)
|
||||
|
||||
# ── 选项隔离:阶段感知校验 ──
|
||||
# 售后回流设备禁止被改回「备货 / 生产」;选定售后专属工序(发货测试 / 售后维修)
|
||||
# 则设备随即进入售后生命周期(无历史记录的老设备由此进入售后阶段)。
|
||||
phase = resolve_phase_for_step(product.lifecycle_phase, status_value)
|
||||
if not is_step_allowed(phase, status_value):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=(
|
||||
f"产品当前处于{phase_label(product.lifecycle_phase)},不允许改为「{status_value}」。"
|
||||
f"该阶段可选:{'、'.join(allowed_steps(product.lifecycle_phase))}"
|
||||
),
|
||||
)
|
||||
product.lifecycle_phase = phase
|
||||
product.overall_status = status_value
|
||||
# 同步 status 字段,保证与整体状态口径一致(修复"只改整体状态不改 status"的旧缺陷)
|
||||
_OVERALL_TO_STATUS = {
|
||||
@ -829,6 +854,7 @@ async def get_all_products(
|
||||
macro_status=_resolve_macro_status(p),
|
||||
overall_status=overall_names.get(p.id) or p.overall_status,
|
||||
status=p.status,
|
||||
lifecycle_phase=p.lifecycle_phase,
|
||||
created_at=p.created_at,
|
||||
latest_record_time=latest_record_map.get(p.id, (None, None, False, None))[0],
|
||||
latest_record_content=latest_record_map.get(p.id, (None, None, False, None))[1],
|
||||
|
||||
Reference in New Issue
Block a user