"""订单业务逻辑层:状态机、进度初始化、审计日志。""" from sqlalchemy.ext.asyncio import AsyncSession from huamenchuang.core.deps import ModuleLockedError, NotAuthedError from huamenchuang.models.order import DEFAULT_PROCESSES, Order, OrderProcess from huamenchuang.models.user import User from huamenchuang.repositories.order_repo import OrderRepository from huamenchuang.schemas.order import OrderCreate, OrderProcessUpdate, OrderSearch, OrderUpdate class OrderService: def __init__(self, db: AsyncSession) -> None: self.db = db self.repo = OrderRepository(db) async def create_order(self, user: User, payload: OrderCreate) -> Order: """创建订单:对齐目标站 {contract_id, drawing_data, pic_url} 契约。 指定 contract_id 时自动快照合同客户信息。""" customer_id = payload.customer_id customer_name = phone = address = "" if payload.contract_id: from huamenchuang.repositories.contract_repo import ContractRepository contract = await ContractRepository(self.db).get(payload.contract_id, user.company_id) if contract is None: raise KeyError("关联合同不存在") customer_id = contract.customer_id customer_name, phone, address = contract.customer_name, contract.phone, contract.address order = Order( order_no=await self.repo.next_order_no(user.company_id), order_type=payload.order_type, contract_id=payload.contract_id, drawing_data=payload.drawing_data, pic_url=payload.pic_url, customer_id=customer_id, customer_name=customer_name, phone=phone, address=address, note=payload.note, money=payload.money, count=payload.count, area=payload.area, status=payload.status, company_id=user.company_id, user_id=user.id, ) order = await self.repo.create(order) # 初始化默认生产进度节点(直接 add,避免触发关系惰性加载) for i, name in enumerate(DEFAULT_PROCESSES): self.db.add(OrderProcess(order_id=order.id, name=name, status=0, sort=i)) await self.repo.add_log(order.id, user.id, "create", f"创建订单 {order.order_no}") return order async def search(self, user: User, q: OrderSearch) -> tuple[list[Order], int]: return await self.repo.search( user.company_id, page=q.page, page_size=q.page_size, keyword=q.keyword, status=q.status, contract_id=q.contract_id, start_date=q.start_date, end_date=q.end_date, order_by=q.order_by, ) async def get_detail(self, user: User, order_id: int) -> Order: order = await self.repo.get(order_id, user.company_id) if order is None: raise KeyError("订单不存在") return order async def update_order(self, user: User, order_id: int, payload: OrderUpdate) -> Order: order = await self.get_detail(user, order_id) data = payload.model_dump(exclude_none=True, exclude={"action", "target_status"}) for k, v in data.items(): setattr(order, k, v) if payload.action == "change_status": self._transition(order, payload.target_status) await self.repo.add_log(order.id, user.id, "change_status", f"{order.STATUS_LABELS.get(order.status)}→{order.STATUS_LABELS.get(payload.target_status)}") else: await self.repo.add_log(order.id, user.id, "update", f"更新字段: {', '.join(data) or '-'}") return order @staticmethod def _transition(order: Order, target: int | None) -> None: """状态机校验:非法迁移直接拒绝。""" if target is None: raise ValueError("缺少 target_status") if target not in Order.TRANSITIONS.get(order.status, set()): raise ValueError(f"不允许的状态迁移: {order.status_label}→{order.STATUS_LABELS.get(target)}") order.status = target async def update_process(self, user: User, order_id: int, payload: OrderProcessUpdate) -> OrderProcess: order = await self.get_detail(user, order_id) proc = await self.repo.upsert_process(order.id, payload.name, payload.status) await self.repo.add_log(order.id, user.id, "up-order-process", f"{payload.name}={payload.status}") return proc # type: ignore[return-value] async def list_logs(self, user: User, order_id: int) -> list: await self.get_detail(user, order_id) return await self.repo.list_logs(order_id) async def delete_order(self, user: User, order_id: int) -> None: order = await self.get_detail(user, order_id) await self.repo.add_log(order.id, user.id, "delete", f"删除订单 {order.order_no}") await self.repo.delete(order) __all__ = ["OrderService", "ModuleLockedError", "NotAuthedError"]