where('status', ProductModel::STATUS_ON) ->distinct() ->pluck('category_id') ->map(static fn ($id) => (int) $id) ->filter(static fn (int $id) => $id > 0); $categories = ProductCategoryModel::query() ->where('status', ProductCategoryModel::STATUS_NORMAL) ->get() ->keyBy('id'); // 保留有上架商品的分类 + 其全部祖先 $keep = []; foreach ($activeCategoryIds as $categoryId) { $cursor = $categoryId; $guard = 0; while ($cursor > 0 && $guard++ < 20 && $categories->has($cursor)) { $keep[$cursor] = true; $cursor = (int) $categories[$cursor]->parent_id; } } $filtered = array_values(array_filter( $categories->toArray(), static fn (array $item) => isset($keep[$item['id']]) )); return $this->success(ProductCategoryModel::buildTree($filtered)); } /** * 商品列表:价格取当前门店客户等级价; * 未绑定门店/门店未设客户等级 → 仍可浏览,price 为 null(不可见价格,加购/下单另由前置校验拦截); * ?category_id=&keyword=&page=&pageSize= */ #[GetRoute('/product/list', authorize: true)] public function products(Request $request): JsonResponse { $user = $this->currentUser($request); $levelId = $this->boundStore($user)?->level_id ?? 0; $query = ProductModel::query() ->where('status', ProductModel::STATUS_ON) ->with('category:id,name'); if ($levelId > 0) { $query->with(['prices' => static fn ($q) => $q->where('level_id', $levelId)]); } $categoryId = (int) $request->input('category_id', 0); if ($categoryId > 0) { $query->where('category_id', $categoryId); } $keyword = trim((string) $request->input('keyword', '')); if ($keyword !== '') { $query->where(static function ($q) use ($keyword) { $q->where('name', 'like', '%' . $keyword . '%') ->orWhere('spec', 'like', '%' . $keyword . '%'); }); } $pageSize = (int) $request->input('pageSize', 10); $data = $query->orderBy('sort') ->orderBy('id') ->paginate($pageSize) ->toArray(); // 扁平化价格:prices[0].actual_price → price(访问器经 toArray 自动输出换算后实际价; // 未绑定门店或未设等级价为 null——未加载 prices 时 ?? null 兜底) // unset prices 同时移除 price_type/percent,门店端无法反推成本;cost_price 已被 $hidden 过滤 foreach ($data['data'] as &$row) { $row['price'] = $row['prices'][0]['actual_price'] ?? null; unset($row['prices']); } return $this->success($data); } }