From 8b7d1379d1ee49c15e3096220ad26b1b6df458e2 Mon Sep 17 00:00:00 2001 From: wsb1224 Date: Thu, 14 May 2026 14:24:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=89=93=E9=80=9A=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E5=91=98=E7=AB=AF=E5=88=9B=E5=BB=BA=E8=AE=A2=E5=8D=95=E9=93=BE?= =?UTF-8?q?=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/schemas/orders.py | 24 +- backend/app/services/order_service.py | 8 + frontend/web-sales/src/mockApi.js | 7 + .../web-sales/src/views/OrderFormPage.vue | 266 +++++++++++++++--- 开发细节/任务状态跟踪.md | 1 + 5 files changed, 252 insertions(+), 54 deletions(-) diff --git a/backend/app/schemas/orders.py b/backend/app/schemas/orders.py index 06659cc..7a4c302 100644 --- a/backend/app/schemas/orders.py +++ b/backend/app/schemas/orders.py @@ -19,13 +19,13 @@ class OrderItemPayload(BaseModel): product_name: str specification: str unit: str - quantity: float - sale_price: float - cost_price: float - rebate_amount: float = 0 - freight_amount: float = 0 - tax_amount: float = 0 - other_fee_amount: float = 0 + quantity: float = Field(gt=0) + sale_price: float = Field(ge=0) + cost_price: float = Field(ge=0) + rebate_amount: float = Field(default=0, ge=0) + freight_amount: float = Field(default=0, ge=0) + tax_amount: float = Field(default=0, ge=0) + other_fee_amount: float = Field(default=0, ge=0) remark: str | None = None @@ -36,11 +36,11 @@ class CreateOrderRequest(BaseModel): order_source: str | None = None delivery_type: str | None = None factory_id: int | None = None - commission_amount: float = 0 - rebate_total: float = 0 - freight_total: float = 0 - tax_total: float = 0 - other_fee_total: float = 0 + commission_amount: float = Field(default=0, ge=0) + rebate_total: float = Field(default=0, ge=0) + freight_total: float = Field(default=0, ge=0) + tax_total: float = Field(default=0, ge=0) + other_fee_total: float = Field(default=0, ge=0) remark: str | None = None items: list[OrderItemPayload] = Field(default_factory=list) diff --git a/backend/app/services/order_service.py b/backend/app/services/order_service.py index 9f53e40..1118ca7 100644 --- a/backend/app/services/order_service.py +++ b/backend/app/services/order_service.py @@ -346,6 +346,11 @@ class OrderService: def create_order(self, payload: dict, session: Session | None = None) -> dict: if session is not None: try: + if not payload["customer_name"].strip(): + raise AppException(code=ErrorCode.PARAM_ERROR, message="客户姓名不能为空", status_code=400) + if not payload["customer_mobile"].strip(): + raise AppException(code=ErrorCode.PARAM_ERROR, message="客户手机号不能为空", status_code=400) + customer = self.customer_repository.find_by_name_and_mobile( session, payload["customer_name"], @@ -368,6 +373,9 @@ class OrderService: ) items = payload["items"] + if not items: + raise AppException(code=ErrorCode.PARAM_ERROR, message="订单明细不能为空", status_code=400) + sale_total = sum(item["quantity"] * item["sale_price"] for item in items) cost_total = sum(item["quantity"] * item["cost_price"] for item in items) profit_total = ( diff --git a/frontend/web-sales/src/mockApi.js b/frontend/web-sales/src/mockApi.js index 98788a9..1575236 100644 --- a/frontend/web-sales/src/mockApi.js +++ b/frontend/web-sales/src/mockApi.js @@ -92,6 +92,13 @@ export function fetchOrderForm() { return wait({ sections: orderFormSections }); } +export async function createOrder(payload) { + return request("/api/orders", { + method: "POST", + body: JSON.stringify(payload), + }); +} + export async function fetchOrderDetail(orderId) { try { const data = await request(`/api/orders/${orderId}`); diff --git a/frontend/web-sales/src/views/OrderFormPage.vue b/frontend/web-sales/src/views/OrderFormPage.vue index e3f3e04..e9354e9 100644 --- a/frontend/web-sales/src/views/OrderFormPage.vue +++ b/frontend/web-sales/src/views/OrderFormPage.vue @@ -1,63 +1,245 @@ diff --git a/开发细节/任务状态跟踪.md b/开发细节/任务状态跟踪.md index c6837ff..bd52539 100644 --- a/开发细节/任务状态跟踪.md +++ b/开发细节/任务状态跟踪.md @@ -95,3 +95,4 @@ | 2026-05-14 | 已继续推进审计查询主线:`/api/audit-logs` 已支持按 `biz_type/biz_id/operate_type/start_time/end_time/page_no/page_size` 查询,并接入真实订单审批日志数据源。 | | 2026-05-14 | 已开始补前端真实联调:`frontend/web-sales` 的订单列表与订单详情已改为优先请求真实后端接口,失败时再回退 mock;同时清理了该模块关键页面中的乱码展示文本。 | | 2026-05-14 | 已继续按前后端同步方式推进:后端订单接口已清理真实中文提示语;前端 `web-sales` 订单列表页与详情页已接入真实 `提交审核/取消订单` 动作,并补充加载态、禁用态与操作结果提示。 | +| 2026-05-14 | 已继续按前后端同步方式推进创建订单链路:后端补充创建订单参数校验;前端 `web-sales` 新建订单页已接入真实 `/api/orders` 创建接口,并支持最小可用单明细录入。 |