Files
menchuang/src/huamenchuang/services/order_service.py
T
HHY b2730af80b feat: v0.7 全功能复刻与前端适配
后端:
- 产品价格本模块(型材/玻璃/五金/配件, 供报价取价)
- 报价单模块(BJ单号, 手动报价/样板算料/转订单)
- 售后单(问题描述必填, 对齐原站校验)与状态流转
- 量尺单(洞口尺寸清单)与阳光房订单(order_type=1)
- 个人中心: 资料/密码修改/邀请返利(HMC邀请码)
前端:
- 新增8页面: 产品/报价单/售后/量尺/阳光房/商城/使用指南/个人中心
- 登录页注册双Tab, 响应式侧边栏(<900px自动折叠)
- Element Plus 图标全局注册修复
- 共15个页面覆盖原站核心业务路由
2026-09-20 11:53:31 -07:00

105 lines
5.0 KiB
Python

"""订单业务逻辑层:状态机、进度初始化、审计日志。"""
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"]