'=', 'status' => '=', 'order_no' => 'like', 'order_date' => 'betweenDate', ]; /** 订单列表(支持按包含的商品名称搜索 ?product_name=) */ #[GetRoute(authorize: 'query')] public function query(Request $request): JsonResponse { $params = $request->all(); $pageSize = $params['pageSize'] ?? 10; $query = StoreOrderModel::query()->with([ 'store:id,name,address,contact,phone', 'items:id,order_id,product_id,product_name,product_spec,unit,price,quantity,amount,image_ids', 'purchase:id,purchase_no,purchase_date,status' ]); // 按包含的商品名称搜索:任一明细品名包含关键字即命中 $productName = trim((string) ($params['product_name'] ?? '')); if ($productName !== '') { $keyword = '%' . str_replace('%', '\%', $productName) . '%'; $query->whereHas('items', static function ($itemQuery) use ($keyword) { $itemQuery->where('product_name', 'like', $keyword); }); } $data = $this->buildSearch($params, $query) ->orderBy('order_date', 'desc') ->orderBy('id', 'desc') ->paginate($pageSize) ->toArray(); // Paginator::toArray() 的 data 仍为模型,先显式转数组(明细/关系一并转换) $data['data'] = array_map( static fn ($order) => is_object($order) ? $order->toArray() : $order, $data['data'] ); // 明细封面图:从快照 image_ids 批量解析(不再关联商品档案表) foreach ($data['data'] as &$order) { app(ItemImageResolver::class)->resolve($order['items']); } unset($order); return $this->success($data); } /** 订单详情 */ #[GetRoute(route: '/{id}', authorize: 'query', where: ['id' => '[0-9]+'])] public function detail(int $id): JsonResponse { $order = StoreOrderModel::with([ 'store:id,name,address,contact,phone', 'items' => static fn ($query) => $query->with('supplier:id,name'), ])->find($id); if (empty($order)) { throw new RepositoryException('订单不存在'); } // 成本价默认对序列化隐藏(防泄漏到小程序端),后台恢复可见 $order->items->each->makeVisible('cost_price'); $data = $order->toArray(); // 明细首图 app(ItemImageResolver::class)->resolve($data['items']); return $this->success($data); } /** * 状态流转 */ #[PutRoute(route: '/{id}/status', authorize: 'update', where: ['id' => '[0-9]+'])] public function status(int $id, Request $request): JsonResponse { $data = $request->validate([ 'status' => 'required|integer|in:1,3,4,9', ], [ 'status.required' => '目标状态不能为空', 'status.in' => '目标状态值不正确', ]); $order = StoreOrderModel::find($id); if (empty($order)) { throw new RepositoryException('订单不存在'); } $target = (int) $data['status']; if (! in_array($target, $this->allowedTransitions($order->status), true)) { throw new RepositoryException( '订单当前状态为「' . (StoreOrderModel::STATUS_NAMES[$order->status] ?? $order->status) . '」,不允许该操作' ); } $order->status = $target; $order->save(); $this->notifyStore($order); return $this->success(); } /** * 批量状态流转 */ #[PutRoute(route: '/batchStatus', authorize: 'update')] public function batchStatus(Request $request): JsonResponse { $data = $request->validate([ 'ids' => 'required|array|min:1', 'ids.*' => 'integer|exists:store_order,id', 'status' => 'required|integer|in:1,3,4,9', ], [ 'ids.required' => '请选择要流转的订单', 'ids.min' => '请选择要流转的订单', 'ids.*.exists' => '订单不存在', 'status.required' => '目标状态不能为空', 'status.in' => '目标状态值不正确', ]); $target = (int) $data['status']; $orders = StoreOrderModel::query()->whereIn('id', $data['ids'])->get(); // 全量预校验:任一订单不满足流转条件,整批中止,不做任何修改 $blocked = []; foreach ($orders as $order) { if (! in_array($target, $this->allowedTransitions($order->status), true)) { $blocked[] = $order->order_no . '(' . (StoreOrderModel::STATUS_NAMES[$order->status] ?? $order->status) . ')'; } } if (! empty($blocked)) { throw new RepositoryException( '以下订单不允许流转为「' . (StoreOrderModel::STATUS_NAMES[$target] ?? $target) . '」,批量操作已中止:' . implode('、', $blocked) ); } DB::transaction(function () use ($orders, $target) { foreach ($orders as $order) { $order->status = $target; $order->save(); $this->notifyStore($order); } }); return $this->success(['success' => $orders->count()]); } /** * 删除订单(软删除) */ #[DeleteRoute(route: '/{id}', authorize: 'delete', where: ['id' => '[0-9]+'])] public function delete(int $id): JsonResponse { $order = StoreOrderModel::find($id); if (empty($order)) { throw new RepositoryException('订单不存在'); } if ($order->status !== StoreOrderModel::STATUS_CANCELLED) { throw new RepositoryException( '订单当前状态为「' . (StoreOrderModel::STATUS_NAMES[$order->status] ?? $order->status) . '」,仅已取消订单可删除' ); } $order->delete(); return $this->success(); } /** * 状态流转合法路径 * * @return int[] */ private function allowedTransitions(int $status): array { return match ($status) { StoreOrderModel::STATUS_PENDING => [ StoreOrderModel::STATUS_SUMMARIZED, StoreOrderModel::STATUS_CANCELLED, ], StoreOrderModel::STATUS_DELIVERING => [ StoreOrderModel::STATUS_DISTRIBUTION, StoreOrderModel::STATUS_COMPLETED, ], StoreOrderModel::STATUS_DISTRIBUTION => [StoreOrderModel::STATUS_COMPLETED], default => [], }; } /** * 状态流转后通知门店用户 */ private function notifyStore(StoreOrderModel $order): void { $userIds = UserModel::query() ->where('store_id', $order->store_id) ->where('status', UserModel::STATUS_NORMAL) ->pluck('id'); $statusName = StoreOrderModel::STATUS_NAMES[$order->status] ?? (string) $order->status; foreach ($userIds as $userId) { NoticeModel::create([ 'user_id' => $userId, 'type' => NoticeModel::TYPE_ORDER, 'title' => '订单状态更新', 'content' => mb_substr("您的订单 {$order->order_no} 状态已更新为「{$statusName}」", 0, 500), 'data' => ['order_id' => $order->id], 'is_read' => NoticeModel::UNREAD, ]); } } }