feat: ship end-to-end interior design MVP

This commit is contained in:
Codex
2026-08-02 08:57:10 +08:00
parent 4d97a30616
commit a96b1bf9f9
12 changed files with 2497 additions and 485 deletions
+41
View File
@@ -2,8 +2,11 @@ from copy import deepcopy
from datetime import UTC, datetime
from app.domain.models import (
CameraView,
CommandRequest,
DesignBrief,
ProjectSnapshot,
RoomProfile,
WorkflowCommand,
WorkflowDefinition,
WorkflowStage,
@@ -91,13 +94,51 @@ def apply_command(project: ProjectSnapshot, request: CommandRequest) -> ProjectS
updated.plan.ceiling_height_mm = int(
request.payload.get("ceiling_height_mm", updated.plan.ceiling_height_mm)
)
if request.payload.get("gross_area_sqm") is not None:
updated.plan.gross_area_sqm = float(request.payload["gross_area_sqm"])
if request.payload.get("room_names"):
existing = {room.name: room for room in updated.plan.structure.rooms}
updated.plan.structure.rooms = [
existing.get(name)
or RoomProfile(
id=f"room-{index + 1}",
name=name,
kind="other",
confidence=1,
notes="用户确认",
)
for index, raw_name in enumerate(request.payload["room_names"])
if (name := str(raw_name).strip())
]
for issue in updated.plan.issues:
if issue.kind in {"scale", "ocr"}:
issue.resolved = True
elif request.command == WorkflowCommand.BUILD_BLOCKOUT:
room_names = [room.name for room in updated.plan.structure.rooms]
focus = room_names[:3] or ["客餐厅", "主卧", "入口"]
updated.scene.cameras = [
CameraView(id=f"camera-{index + 1}", label=f"{room}主视角", room=room)
for index, room in enumerate(focus)
]
elif request.command == WorkflowCommand.SUBMIT_STYLE_BRIEF:
updated.brief = DesignBrief.model_validate({**updated.brief.model_dump(), **request.payload, "completed": True})
for field in ("concept", "lighting", "forms", "density"):
if field in request.payload:
setattr(updated.style, field, request.payload[field])
for field in ("keywords", "materials", "avoid", "locked_decisions"):
if field in request.payload:
setattr(updated.style, field, list(request.payload[field]))
elif request.command == WorkflowCommand.SELECT_DIRECTION:
direction_id = str(request.payload.get("direction_id", ""))
direction = next((item for item in updated.directions if item.id == direction_id), None)
if direction is None:
raise InvalidTransitionError(f"Direction '{direction_id}' does not exist in this project.")
updated.style.selected_direction_id = direction_id
updated.style.concept = direction.thesis
updated.style.keywords = direction.keywords
updated.style.palette = direction.palette
updated.style.materials = direction.materials
updated.style.lighting = direction.lighting
updated.available_commands = available_commands(updated.stage)
return updated