feat: 订单模块 v0.1 实现与样板二维平面图全量采集
- 后端分层架构: api(Controller)/services(业务)/repositories(数据)/models(ORM)
- 订单模块: 创建(契约对齐目标站{contract_id,drawing_data,pic_url})、搜索、
详情、更新、删除、状态机(意向单→正式单→生产中→完工/取消,非法迁移拒绝)、
7节点生产进度、审计日志
- 认证模块: JWT签发(HS256/24h)、PBKDF2密码、注册登录
- 样板库API: 分类树/模板搜索/模板详情, drawing_data 与采集样本同构
- 254张样板二维平面图全部采集并静态挂载 /static/windoor-images/
- 种子脚本: 管理员+分类树+254套样板入库
- 统一响应envelope与付费墙(code=10086)/权限(code=210)语义
- 端到端冒烟测试7/7通过, 更新API.md §12自研接口规范
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""订单业务逻辑层:状态机、进度初始化、审计日志。"""
|
||||
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} 契约。"""
|
||||
order = Order(
|
||||
order_no=await self.repo.next_order_no(user.company_id),
|
||||
contract_id=payload.contract_id,
|
||||
drawing_data=payload.drawing_data,
|
||||
pic_url=payload.pic_url,
|
||||
customer_id=payload.customer_id,
|
||||
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"]
|
||||
Reference in New Issue
Block a user