null]; if ($id !== 0) { $where['id'] = $id; } if ($platform == 'preview') { $decorate = self::where($where)->order('id asc')->find(); } else { $where['status'] = 'normal'; $where['type'] = 'shop'; $decorate = self::where('find_in_set(:platform,platform)', ['platform' => $platform])->where($where)->order('id asc')->find(); } if (!$decorate) { new Exception('未找到模板'); } $template = self::getDecorateContent($decorate); return $template; } public static function getCustomDecorate($id) { $decorateContent = DecorateContent::all(['decorate_id' => $id]); foreach ($decorateContent as &$v) { $v['content'] = self::getDecorateContentByType($v); } return $decorateContent; } public static function asyncDecorateScreenShot($params) { $decorate = self::get($params['shop_id']); if ($decorate) { $decorate->image = $params['image']; $decorate->save(); } } public static function getDecorateContent($decorate) { $template['home'] = []; //首页 $template['user'] = []; //个人中心 $template['tabbar'] = []; //底部菜单 $template['popup'] = []; //弹出提醒 $template['float-button'] = []; //悬浮按钮 $decorateContent = DecorateContent::all(['decorate_id' => $decorate->id]); foreach ($decorateContent as $k => $v) { $v['content'] = self::getDecorateContentByType($v); $template[$v->category][] = $v; } if (!empty($template['home'])) { $template['home'] = self::enrichHomeGoodsBlocks($template['home']); } return $template; } /** * 首页「精选好酒」模块:API 侧补齐 goods_ids,避免小程序仅带 category_id 时少显示装修选中的 hidden 商品。 * * @return array [category_id => goods_ids] */ public static function resolveHomeGoodsListContext($platform = '') { try { $template = self::getCurrentPlatformDecorate($platform ?: 'wxMiniProgram'); } catch (\Throwable $e) { return ['category_id' => 0, 'goods_ids' => '']; } $home = $template['home'] ?? []; if (!$home) { return ['category_id' => 0, 'goods_ids' => '']; } $ctx = self::extractHomeGoodsContextFromBlocks($home); return [ 'category_id' => (int) ($ctx['category_id'] ?? 0), 'goods_ids' => (string) ($ctx['goods_ids'] ?? ''), ]; } /** * 与首页 sh-hot-goods 请求对齐:无筛选时用装修商品;首页分类与全部分类列表一致。 */ public static function normalizeHomeGoodsListParams(array $params, $platform = '') { $keywords = trim((string) ($params['keywords'] ?? '')); if ($keywords !== '') { return $params; } $ctx = self::resolveHomeGoodsListContext($platform); $homeIds = trim((string) ($ctx['goods_ids'] ?? '')); $homeCat = (int) ($ctx['category_id'] ?? 0); $categoryId = isset($params['category_id']) ? (int) $params['category_id'] : 0; $isHomeScope = $categoryId <= 0 || ($homeCat > 0 && $categoryId === $homeCat); if (!$isHomeScope) { return $params; } // 首页:请求里已有 goods_ids(装修 ids)时也合并全店上架/隐藏,避免只返回 2 个 $mergedIds = self::mergeGoodsIdStrings( $params['goods_ids'] ?? '', $homeIds, self::allShelfGoodsIds() ); if ($mergedIds === '') { return $params; } $params['goods_ids'] = $mergedIds; unset($params['category_id']); return $params; } /** * 商城当前可售(上架+隐藏)商品,按 sort/weigh 排序 */ public static function allShelfGoodsIds() { $ids = Goods::where('status', 'in', ['up', 'hidden']) ->order('weigh asc, id desc') ->column('id'); return self::normalizeGoodsIdsString(implode(',', $ids)); } protected static function mergeGoodsIdStrings(...$parts) { $idMap = []; foreach ($parts as $part) { $part = trim((string) $part); if ($part === '') { continue; } foreach (explode(',', $part) as $id) { $id = (int) $id; if ($id > 0) { $idMap[$id] = $id; } } } if (!$idMap) { return ''; } $ordered = Goods::where('id', 'in', array_values($idMap)) ->where('status', 'in', ['up', 'hidden']) ->order('weigh asc, id desc') ->column('id'); return self::normalizeGoodsIdsString(implode(',', $ordered)); } protected static function enrichHomeGoodsBlocks(array $home) { $ctx = self::extractHomeGoodsContextFromBlocks($home); if (($ctx['goods_ids'] ?? '') === '') { return $home; } $targetIdx = null; foreach ($home as $idx => $block) { if (in_array($block['type'] ?? '', ['goods-list', 'goods-group'], true)) { $targetIdx = $idx; break; } } if ($targetIdx === null) { $targetIdx = self::findLastCategoryTabsIndex($home); } if ($targetIdx === null || $targetIdx < 0) { return $home; } $content = $home[$targetIdx]['content'] ?? []; $content['ids'] = $ctx['goods_ids']; unset($content['id']); $home[$targetIdx]['content'] = $content; return $home; } protected static function findLastCategoryTabsIndex(array $home) { for ($i = count($home) - 1; $i >= 0; $i--) { if (($home[$i]['type'] ?? '') === 'category-tabs') { return $i; } } return -1; } protected static function extractHomeGoodsContextFromBlocks(array $home) { $categoryId = 0; foreach ($home as $block) { $type = $block['type'] ?? ''; if (!in_array($type, ['goods-list', 'goods-group'], true)) { continue; } $content = $block['content'] ?? []; $ctx = self::buildHomeGoodsContextFromContent($content); if ($ctx['goods_ids'] !== '') { return $ctx; } } for ($i = count($home) - 1; $i >= 0; $i--) { if (($home[$i]['type'] ?? '') !== 'category-tabs') { continue; } $content = $home[$i]['content'] ?? []; $ctx = self::buildHomeGoodsContextFromContent($content); if ($ctx['goods_ids'] !== '' || $ctx['category_id'] > 0) { return $ctx; } break; } return ['category_id' => 0, 'goods_ids' => '']; } protected static function buildHomeGoodsContextFromContent(array $content) { $categoryId = (int) ($content['id'] ?? ($content['category_arr'][0]['id'] ?? 0)); $ids = self::mergeGoodsIdStrings( !empty($content['ids']) ? self::normalizeGoodsIdsString($content['ids']) : '', self::extractGoodsIdsFromTimeData($content['timeData'] ?? null), $categoryId > 0 ? self::goodsIdsFromCategoryId($categoryId) : '' ); return [ 'category_id' => $categoryId, 'goods_ids' => $ids, ]; } protected static function extractGoodsIdsFromTimeData($timeData) { if ($timeData === null || $timeData === '') { return ''; } if (is_string($timeData)) { $decoded = json_decode($timeData, true); if (is_array($decoded)) { $timeData = $decoded; } else { return ''; } } if (!is_array($timeData)) { return ''; } $ids = []; foreach ($timeData as $row) { if (is_array($row) && !empty($row['id'])) { $ids[] = (int) $row['id']; } elseif (is_numeric($row)) { $ids[] = (int) $row; } } return self::normalizeGoodsIdsString(implode(',', $ids)); } protected static function normalizeGoodsIdsString($ids) { $ids = is_array($ids) ? implode(',', $ids) : (string) $ids; $parts = array_filter(array_map('intval', explode(',', $ids))); return implode(',', array_unique($parts)); } protected static function goodsIdsFromCategoryId($categoryId) { $categoryId = (int) $categoryId; if ($categoryId <= 0) { return ''; } $categoryIds = Category::getCategoryIds($categoryId); if (!$categoryIds) { return ''; } $query = Goods::where('status', 'in', ['up', 'hidden']); $query->where(function ($q) use ($categoryIds) { foreach ($categoryIds as $cid) { $q->whereOrRaw("find_in_set($cid, category_ids)"); } }); $ids = $query->order('weigh asc, id desc')->column('id'); return self::normalizeGoodsIdsString(implode(',', $ids)); } public static function getDecorateContentByType($templateValue) { $content = json_decode($templateValue->content, true); if (!is_array($content)) { $content = []; } switch ($templateValue['type']) { case 'search': case 'coupons': case 'live': case 'seckill': case 'groupon': case 'wallet-card': case 'order-card': case 'rich-text': case 'nav-bg': break; case 'banner': case 'menu': case 'adv': case 'grid-list': case 'popup': case 'nav-list': if (!empty($content['list']) && is_array($content['list'])) { foreach ($content['list'] as &$l) { $l['image'] && $l['image'] = cdnurl($l['image'], true); } unset($l); $content['list'] = array_values(array_filter($content['list'], function ($item) { return !self::isHiddenMenuItem($item); })); } else { $content['list'] = []; } break; case 'user': case 'title-block': case 'goods-list': case 'goods-group': case 'image-goods': $content['image'] && $content['image'] = cdnurl($content['image'], true); break; case 'tabbar': if (isset($content['style']) && $content['style'] < 3 && !empty($content['list']) && is_array($content['list'])) { foreach ($content['list'] as &$l) { $l['image'] && $l['image'] = cdnurl($l['image'], true); $l['activeImage'] && $l['activeImage'] = cdnurl($l['activeImage'], true); } unset($l); } elseif (!isset($content['list']) || !is_array($content['list'])) { $content['list'] = []; } break; case 'float-button': $content['image'] = cdnurl($content['image'], true); if (!empty($content['list']) && is_array($content['list'])) { foreach ($content['list'] as &$l) { if ($l['style'] == 1) { $l['image'] && $l['image'] = cdnurl($l['image'], true); } $l['btnimage'] && $l['btnimage'] = cdnurl($l['btnimage'], true); } unset($l); } else { $content['list'] = []; } break; } return $content; } /** * 已关闭的功能入口,不在前端展示 */ protected static function isHiddenMenuItem($item) { if (!is_array($item)) { return false; } $name = isset($item['name']) ? (string) $item['name'] : ''; $path = isset($item['path']) ? (string) $item['path'] : ''; $hiddenNames = ['我的报修', '门店管理', '我的拼团', '今日必拼']; if (in_array($name, $hiddenNames, true)) { return true; } $hiddenPaths = ['/pages/activity/groupon', '/pages/app/merchant', '/pages/order/after-sale/list']; foreach ($hiddenPaths as $hiddenPath) { if ($path && strpos($path, $hiddenPath) !== false) { return true; } } return false; } }