model = CatalogueModel::class; $this->selectField = ['id', 'title', 'category_id', 'cover', 'pdf', 'price', 'alias', 'identifier', 'status', 'created_at', 'updated_at']; $this->queryField = ['title' => 'like', 'alias' => 'like', 'identifier' => 'like', 'status' => '=']; $this->media = MediaUrlService::getInstance(); } /** * 列表 * * 两处沿用老行为: * 1. 按分类筛选时连同该分类的子分类一起查,否则选了父分类会一条都搜不到 * 2. 报价单里 6 个历史材质列绝大多数为空,只把有值的列名回给前端(show_field), * 前端据此决定表格显示哪几列 * @throws ContainerExceptionInterface * @throws NotFoundExceptionInterface */ public function list(): array { $this->with = ['category', 'priceSheet']; $categoryId = (int) request()->get('category_id', 0); if ($categoryId > 0) { $ids = CategoryModel::where('pid', $categoryId)->where('deleted_at', 0)->pluck('id')->all(); $ids[] = $categoryId; $this->whereIn = ['category_id', $ids]; } $result = $this->getPageList(); foreach ($result['items'] as &$item) { $item['category_name'] = $item['category']['name'] ?? ''; unset($item['category']); $item['cover'] = $this->media->toPublic($item['cover'] ?? ''); $item['pdf'] = $this->media->toPublic($item['pdf'] ?? ''); $item['show_field'] = $this->pickUsedMaterialFields($item['price_sheet'] ?? []); } unset($item); return $result; } public function option(): mixed { $this->optionField = ['id', 'title as name', 'identifier']; return $this->getOption(); } /** * 详情,带规格与两类相册 * @throws Exception */ public function detail($id): mixed { $info = $this->getDetail($id); $info->cover = $this->media->toPublic($info->cover); $info->pdf = $this->media->toPublic($info->pdf); $priceSheet = PriceSheetModel::where('catalogue_id', $id) ->where('deleted_at', 0) ->orderBy('id') ->get() ->toArray(); $info->show_field = $this->pickUsedMaterialFields($priceSheet); $info->price_sheet = $priceSheet; $info->render_images = $this->imagesOf($id, ImageModel::TYPE_RENDER); $info->physical_images = $this->imagesOf($id, ImageModel::TYPE_PHYSICAL); return $info; } public function create($params): mixed { $params = $this->normalize($params); $this->assertIdentifierUnique((string) ($params['identifier'] ?? '')); return $this->insert($params); } /** * @throws Exception */ public function update($id, $params): mixed { $params = $this->normalize($params); if (array_key_exists('identifier', $params)) { $this->assertIdentifierUnique((string) $params['identifier'], (int) $id); } return $this->save($id, $params); } /** * 删除商品:连带软删它的规格与相册,否则会留下一堆查不到主体的孤儿数据 * @throws Exception */ public function delete($id): mixed { $ids = array_values(array_filter(array_map('intval', is_array($id) ? $id : [$id]))); if (empty($ids)) { $this->utils->errorThrow('参数错误'); } $now = time(); PriceSheetModel::whereIn('catalogue_id', $ids)->where('deleted_at', 0) ->update(['deleted_at' => $now, 'updated_at' => $now]); ImageModel::whereIn('catalogue_id', $ids)->where('deleted_at', 0) ->update(['deleted_at' => $now, 'updated_at' => $now]); return $this->del($ids); } /** * @throws Exception */ public function status($id, $status): mixed { return $this->save($id, ['status' => (int) $status]); } /** * 报价单里真正有值的材质列 */ private function pickUsedMaterialFields(array $priceSheetRows): array { $used = []; foreach ($priceSheetRows as $row) { foreach (PriceSheetModel::MATERIAL_FIELDS as $field) { if (!empty($row[$field] ?? '')) { $used[$field] = true; } } } return array_keys($used); } private function imagesOf(int|string $catalogueId, int $type): array { $rows = ImageModel::where('catalogue_id', $catalogueId) ->where('type', $type) ->where('deleted_at', 0) ->orderBy('id') ->get(['id', 'url', 'type']) ->toArray(); $this->media->publicEach($rows, ['url']); return $rows; } private function normalize(array $params): array { foreach (['cover', 'pdf'] as $field) { if (array_key_exists($field, $params)) { $params[$field] = $this->media->firstOf($params[$field]); } } return $params; } /** * 商品编号是小程序搜索和线下对单的依据,重复了就没法定位货品 * @throws Exception */ private function assertIdentifierUnique(string $identifier, int $exceptId = 0): void { $identifier = trim($identifier); if ($identifier === '') { return; } $exists = CatalogueModel::where('identifier', $identifier) ->where('deleted_at', 0) ->when($exceptId > 0, fn ($q) => $q->where('id', '<>', $exceptId)) ->exists(); if ($exists) { $this->utils->errorThrow('商品编号已存在:' . $identifier); } } }