/** * 搜索产品(支持分词,结果缓存) * * @param string $keyword 搜索关键词(如 "obj class") * @param array $filters 过滤条件 * @param int $page 页码 * @param int $pageSize 每页数量 * @param bool $includeAggregation 是否包含聚合结果 * @param string $orderBy 排序方式 * @return array */ public function searchProducts($keyword = '', $filters = [], $page = 1, $pageSize = 20, $includeAggregation = false, $orderBy = '') { // 生成缓存键(包含站点前缀) $cacheKey = $this->buildCacheKey(md5($keyword . json_encode($filters) . $page . $pageSize . $orderBy)); // 尝试从缓存获取 $cachedResult = \Yii::$app->cache->get($cacheKey); if ($cachedResult !== false) { return $cachedResult; } $from = ($page - 1) * $pageSize; // 构建查询 - 按照新的权重配置 if (!empty($keyword)) { $searchBody = [ 'query' => [ 'bool' => [ 'should' => [ // 最高权重(标题和货号) // 1. Name完全匹配 - boost: 200 [ 'match_phrase' => [ 'name' => [ 'query' => $keyword, 'boost' => 200 ] ] ], // 1.1 Name不分词匹配(针对短横杠) - boost: 190 [ 'match' => [ 'name.no_hyphen' => [ 'query' => $keyword, 'boost' => 190 ] ] ], // 2. SKU完全匹配 - boost: 180 [ 'constant_score' => [ 'filter' => [ 'match_phrase' => [ 'skustr' => $keyword ] ], 'boost' => 180 ] ], // 2.1 SKU不分词匹配(针对短横杠) - boost: 170 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'skustr.no_hyphen' => $keyword ] ], 'boost' => 170 ] ], // 3. Name 包含所有词 - boost: 100 [ 'match' => [ 'name' => [ 'query' => $keyword, 'operator' => 'and', 'boost' => 100 ] ] ], // ========== 新增模糊匹配分支 start ========== // name 模糊容错 [ 'match' => [ 'name' => [ 'query' => $keyword, 'operator' => 'and', 'fuzziness' => 'AUTO', 'prefix_length' => 3, 'boost' => 40 ] ] ], // name.no_hyphen 模糊容错(兼容横杠) [ 'match' => [ 'name.no_hyphen' => [ 'query' => $keyword, 'operator' => 'and', 'fuzziness' => 'AUTO', 'prefix_length' => 3, 'boost' => 38 ] ] ], // skustr 模糊容错 [ 'match' => [ 'skustr' => [ 'query' => $keyword, 'operator' => 'and', 'fuzziness' => 'AUTO', 'prefix_length' => 3, 'boost' => 35 ] ] ], // skustr.no_hyphen 模糊容错(兼容横杠) [ 'match' => [ 'skustr.no_hyphen' => [ 'query' => $keyword, 'operator' => 'and', 'fuzziness' => 'AUTO', 'prefix_length' => 3, 'boost' => 33 ] ] ], // ========== 新增模糊匹配分支 end ========== // 4. SkuStr 包含任意词 - boost: 80 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'skustr' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 80 ] ], // 5. Other Catno 包含任意词 - boost: 80 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'other_catno' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 80 ] ], // 中等权重(关键词和SEO描述) // 6. Keywords 包含所有词 - boost: 60 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'keywords' => [ 'query' => $keyword, 'operator' => 'and' ] ] ], 'boost' => 60 ] ], // 6. DescriptionFormat 包含所有词 - boost: 60 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'description_format' => [ 'query' => $keyword, 'operator' => 'and' ] ] ], 'boost' => 60 ] ], // 7. SeoDescription 包含所有词 - boost: 50 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'seo_description' => [ 'query' => $keyword, 'operator' => 'and' ] ] ], 'boost' => 50 ] ], // 8. Description 包含所有词 - boost: 50 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'description' => [ 'query' => $keyword, 'operator' => 'and' ] ] ], 'boost' => 50 ] ], // 低权重 // 9. Keywords 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'keywords' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 10. Content 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 11. Content1 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content1' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 12. Content2 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content2' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 13. content_h5 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content_h5' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 14. content1_h5 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content1_h5' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 15. content2_h5 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content2_h5' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 16. intro_h5 包含任意词 - boost: 3 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'intro_h5' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 3 ] ], // 17. Intro 包含任意词 - boost: 3 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'intro' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 3 ] ] ], 'minimum_should_match' => 1 ] ] ]; } else{ // 无关键词:使用 match_all,但保持bool结构以便后续添加筛选条件 $searchBody = [ 'query' => [ 'bool' => [ 'must' => [ ['match_all' => (object)[]] ] ] ] ]; } // 添加筛选条件 if (!empty($filters)) { // 初始化filter数组(现在所有查询都使用bool结构) if (!isset($searchBody['query']['bool']['filter'])) { $searchBody['query']['bool']['filter'] = []; } if (!empty($filters['cate'])) { // 支持多个分类ID(逗号分隔),求并集(OR逻辑) $cateIds = explode(',', $filters['cate']); $allCateIds = []; foreach ($cateIds as $cateId) { $cateId = (int)trim($cateId); if ($cateId <= 0) { continue; } // 添加当前分类ID $allCateIds[] = $cateId; // 获取该分类及其所有子分类ID $descendants = \addons\InvShop\common\models\product\Cate::getDescendants($cateId); if (!empty($descendants)) { $descendantIds = \yii\helpers\ArrayHelper::getColumn($descendants, 'id'); if (!empty($descendantIds)) { $allCateIds = array_merge($allCateIds, (array)$descendantIds); } } } // 去重并重新索引 $allCateIds = array_values(array_unique($allCateIds)); // 使用terms查询匹配任何一个分类ID(OR逻辑) if (!empty($allCateIds)) { $searchBody['query']['bool']['filter'][] = ['terms' => ['cate_id' => $allCateIds]]; } } // 处理规格筛选(prop_开头的参数) // URL中的值就是base_spec_value_id,ES索引中存储的也是base_spec_value_id foreach ($filters as $key => $value) { if (substr($key, 0, 5) === 'prop_' && !empty($value)) { $valueIds = array_map('intval', explode(',', $value)); if (count($valueIds) === 1) { // 单个值使用term查询 $searchBody['query']['bool']['filter'][] = [ 'term' => ['spec_values' => $valueIds[0]] ]; } else { // 多个值使用terms查询(OR逻辑) $searchBody['query']['bool']['filter'][] = [ 'terms' => ['spec_values' => $valueIds] ]; } } } } // 添加排序 $searchBody['sort'] = []; if (!empty($orderBy)) { switch ($orderBy) { case 'price_asc': $searchBody['sort'][] = ['price' => ['order' => 'asc']]; $searchBody['sort'][] = ['id' => ['order' => 'desc']]; // 次要排序 break; case 'price_desc': $searchBody['sort'][] = ['price' => ['order' => 'desc']]; $searchBody['sort'][] = ['id' => ['order' => 'desc']]; // 次要排序 break; case 'sales_desc': $searchBody['sort'][] = ['sales' => ['order' => 'desc']]; $searchBody['sort'][] = ['id' => ['order' => 'desc']]; // 次要排序 break; case 'view_desc': $searchBody['sort'][] = ['view' => ['order' => 'desc']]; $searchBody['sort'][] = ['id' => ['order' => 'desc']]; // 次要排序 break; } } else { // 默认按相关度排序,不添加次要排序,保持纯粹的相关度排序 $searchBody['sort'][] = ['_score' => ['order' => 'desc']]; } // 调试日志已移除,避免文件写入权限问题 // 如果需要聚合结果,添加聚合配置 if ($includeAggregation) { $searchBody['aggs'] = [ 'spec_values_stats' => [ 'terms' => [ 'field' => 'spec_values.keyword', 'size' => 10000 ] ] ]; // 调试:记录包含聚合的完整查询体 \Yii::info('=== ES聚合查询体 ===', 'elasticsearch'); \Yii::info('完整查询体: ' . json_encode($searchBody, JSON_UNESCAPED_UNICODE), 'elasticsearch'); } // 执行搜索 try { $searchResult = $this->esService->advancedSearch($this->indexName, $searchBody, $from, $pageSize); // 调试:记录搜索结果到日志 if (YII_DEBUG && !empty($filters) && array_filter($filters, function($key) { return substr($key, 0, 5) === 'prop_'; }, ARRAY_FILTER_USE_KEY)) { \Yii::info('ES搜索结果 - 总数: ' . ($searchResult['total'] ?? 0) . ', 返回数据数量: ' . count($searchResult['data'] ?? []), 'elasticsearch'); } } catch (\Exception $e) { \Yii::error('ES搜索失败: ' . $e->getMessage(), 'elasticsearch'); return [ 'total' => 0, 'data' => [], 'page' => $page, 'pageSize' => $pageSize, 'totalPages' => 0, 'error' => $e->getMessage(), 'raw_response' => null, // 确保有raw_response字段 ]; } // 格式化结果 $data = []; foreach ($searchResult['data'] as $hit) { $product = $hit['_source']; $product['id'] = $hit['_id']; $product['_score'] = $hit['_score'] ?? 0; $data[] = $product; } $total = $searchResult['total']; $result = [ 'total' => $total, 'data' => $data, 'page' => $page, 'pageSize' => $pageSize, 'totalPages' => ceil($total / $pageSize), ]; // 如果包含聚合,添加原始响应 if ($includeAggregation) { $result['raw_response'] = $searchResult; } // 缓存结果(5分钟) \Yii::$app->cache->set($cacheKey, $result, self::CACHE_DURATION); return $result; } /** * 搜索产品(支持分词,结果缓存) * * @param string $keyword 搜索关键词(如 "obj class") * @param array $filters 过滤条件 * @param int $page 页码 * @param int $pageSize 每页数量 * @param bool $includeAggregation 是否包含聚合结果 * @param string $orderBy 排序方式 * @return array */ public function searchProducts($keyword = '', $filters = [], $page = 1, $pageSize = 20, $includeAggregation = false, $orderBy = '') { // 生成缓存键(包含站点前缀) $cacheKey = $this->buildCacheKey(md5($keyword . json_encode($filters) . $page . $pageSize . $orderBy)); // 尝试从缓存获取 $cachedResult = \Yii::$app->cache->get($cacheKey); if ($cachedResult !== false) { return $cachedResult; } $from = ($page - 1) * $pageSize; // 构建查询 - 按照新的权重配置 if (!empty($keyword)) { $searchBody = [ 'query' => [ 'bool' => [ 'should' => [ // 最高权重(标题和货号) // 1. Name完全匹配 - boost: 200 [ 'match_phrase' => [ 'name' => [ 'query' => $keyword, 'boost' => 200 ] ] ], // 1.1 Name不分词匹配(针对短横杠) - boost: 190 [ 'match' => [ 'name.no_hyphen' => [ 'query' => $keyword, 'boost' => 190 ] ] ], // 2. SKU完全匹配 - boost: 180 [ 'constant_score' => [ 'filter' => [ 'match_phrase' => [ 'skustr' => $keyword ] ], 'boost' => 180 ] ], // 2.1 SKU不分词匹配(针对短横杠) - boost: 170 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'skustr.no_hyphen' => $keyword ] ], 'boost' => 170 ] ], // 3. Name 包含所有词 - boost: 100 [ 'match' => [ 'name' => [ 'query' => $keyword, 'operator' => 'and', 'boost' => 100 ] ] ], // ========== 新增模糊匹配分支 start ========== // name 模糊容错 [ 'match' => [ 'name' => [ 'query' => $keyword, 'operator' => 'and', 'fuzziness' => 'AUTO', 'prefix_length' => 3, 'boost' => 40 ] ] ], // name.no_hyphen 模糊容错(兼容横杠) [ 'match' => [ 'name.no_hyphen' => [ 'query' => $keyword, 'operator' => 'and', 'fuzziness' => 'AUTO', 'prefix_length' => 3, 'boost' => 38 ] ] ], // skustr 模糊容错 [ 'match' => [ 'skustr' => [ 'query' => $keyword, 'operator' => 'and', 'fuzziness' => 'AUTO', 'prefix_length' => 3, 'boost' => 35 ] ] ], // skustr.no_hyphen 模糊容错(兼容横杠) [ 'match' => [ 'skustr.no_hyphen' => [ 'query' => $keyword, 'operator' => 'and', 'fuzziness' => 'AUTO', 'prefix_length' => 3, 'boost' => 33 ] ] ], // ========== 新增模糊匹配分支 end ========== // 4. SkuStr 包含任意词 - boost: 80 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'skustr' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 80 ] ], // 5. Other Catno 包含任意词 - boost: 80 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'other_catno' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 80 ] ], // 中等权重(关键词和SEO描述) // 6. Keywords 包含所有词 - boost: 60 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'keywords' => [ 'query' => $keyword, 'operator' => 'and' ] ] ], 'boost' => 60 ] ], // 6. DescriptionFormat 包含所有词 - boost: 60 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'description_format' => [ 'query' => $keyword, 'operator' => 'and' ] ] ], 'boost' => 60 ] ], // 7. SeoDescription 包含所有词 - boost: 50 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'seo_description' => [ 'query' => $keyword, 'operator' => 'and' ] ] ], 'boost' => 50 ] ], // 8. Description 包含所有词 - boost: 50 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'description' => [ 'query' => $keyword, 'operator' => 'and' ] ] ], 'boost' => 50 ] ], // 低权重 // 9. Keywords 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'keywords' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 10. Content 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 11. Content1 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content1' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 12. Content2 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content2' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 13. content_h5 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content_h5' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 14. content1_h5 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content1_h5' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 15. content2_h5 包含任意词 - boost: 10 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'content2_h5' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 10 ] ], // 16. intro_h5 包含任意词 - boost: 3 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'intro_h5' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 3 ] ], // 17. Intro 包含任意词 - boost: 3 [ 'constant_score' => [ 'filter' => [ 'match' => [ 'intro' => [ 'query' => $keyword, 'operator' => 'or' ] ] ], 'boost' => 3 ] ] ], 'minimum_should_match' => 1 ] ] ]; } else{ // 无关键词:使用 match_all,但保持bool结构以便后续添加筛选条件 $searchBody = [ 'query' => [ 'bool' => [ 'must' => [ ['match_all' => (object)[]] ] ] ] ]; } // 添加筛选条件 if (!empty($filters)) { // 初始化filter数组(现在所有查询都使用bool结构) if (!isset($searchBody['query']['bool']['filter'])) { $searchBody['query']['bool']['filter'] = []; } if (!empty($filters['cate'])) { // 支持多个分类ID(逗号分隔),求并集(OR逻辑) $cateIds = explode(',', $filters['cate']); $allCateIds = []; foreach ($cateIds as $cateId) { $cateId = (int)trim($cateId); if ($cateId <= 0) { continue; } // 添加当前分类ID $allCateIds[] = $cateId; // 获取该分类及其所有子分类ID $descendants = \addons\InvShop\common\models\product\Cate::getDescendants($cateId); if (!empty($descendants)) { $descendantIds = \yii\helpers\ArrayHelper::getColumn($descendants, 'id'); if (!empty($descendantIds)) { $allCateIds = array_merge($allCateIds, (array)$descendantIds); } } } // 去重并重新索引 $allCateIds = array_values(array_unique($allCateIds)); // 使用terms查询匹配任何一个分类ID(OR逻辑) if (!empty($allCateIds)) { $searchBody['query']['bool']['filter'][] = ['terms' => ['cate_id' => $allCateIds]]; } } // 处理规格筛选(prop_开头的参数) // URL中的值就是base_spec_value_id,ES索引中存储的也是base_spec_value_id foreach ($filters as $key => $value) { if (substr($key, 0, 5) === 'prop_' && !empty($value)) { $valueIds = array_map('intval', explode(',', $value)); if (count($valueIds) === 1) { // 单个值使用term查询 $searchBody['query']['bool']['filter'][] = [ 'term' => ['spec_values' => $valueIds[0]] ]; } else { // 多个值使用terms查询(OR逻辑) $searchBody['query']['bool']['filter'][] = [ 'terms' => ['spec_values' => $valueIds] ]; } } } } // 添加排序 $searchBody['sort'] = []; if (!empty($orderBy)) { switch ($orderBy) { case 'price_asc': $searchBody['sort'][] = ['price' => ['order' => 'asc']]; $searchBody['sort'][] = ['id' => ['order' => 'desc']]; // 次要排序 break; case 'price_desc': $searchBody['sort'][] = ['price' => ['order' => 'desc']]; $searchBody['sort'][] = ['id' => ['order' => 'desc']]; // 次要排序 break; case 'sales_desc': $searchBody['sort'][] = ['sales' => ['order' => 'desc']]; $searchBody['sort'][] = ['id' => ['order' => 'desc']]; // 次要排序 break; case 'view_desc': $searchBody['sort'][] = ['view' => ['order' => 'desc']]; $searchBody['sort'][] = ['id' => ['order' => 'desc']]; // 次要排序 break; } } else { // 默认按相关度排序,不添加次要排序,保持纯粹的相关度排序 $searchBody['sort'][] = ['_score' => ['order' => 'desc']]; } // 调试日志已移除,避免文件写入权限问题 // 如果需要聚合结果,添加聚合配置 if ($includeAggregation) { $searchBody['aggs'] = [ 'spec_values_stats' => [ 'terms' => [ 'field' => 'spec_values.keyword', 'size' => 10000 ] ] ]; // 调试:记录包含聚合的完整查询体 \Yii::info('=== ES聚合查询体 ===', 'elasticsearch'); \Yii::info('完整查询体: ' . json_encode($searchBody, JSON_UNESCAPED_UNICODE), 'elasticsearch'); } // 执行搜索 try { $searchResult = $this->esService->advancedSearch($this->indexName, $searchBody, $from, $pageSize); // 调试:记录搜索结果到日志 if (YII_DEBUG && !empty($filters) && array_filter($filters, function($key) { return substr($key, 0, 5) === 'prop_'; }, ARRAY_FILTER_USE_KEY)) { \Yii::info('ES搜索结果 - 总数: ' . ($searchResult['total'] ?? 0) . ', 返回数据数量: ' . count($searchResult['data'] ?? []), 'elasticsearch'); } } catch (\Exception $e) { \Yii::error('ES搜索失败: ' . $e->getMessage(), 'elasticsearch'); return [ 'total' => 0, 'data' => [], 'page' => $page, 'pageSize' => $pageSize, 'totalPages' => 0, 'error' => $e->getMessage(), 'raw_response' => null, // 确保有raw_response字段 ]; } // 格式化结果 $data = []; foreach ($searchResult['data'] as $hit) { $product = $hit['_source']; $product['id'] = $hit['_id']; $product['_score'] = $hit['_score'] ?? 0; $data[] = $product; } $total = $searchResult['total']; $result = [ 'total' => $total, 'data' => $data, 'page' => $page, 'pageSize' => $pageSize, 'totalPages' => ceil($total / $pageSize), ]; // 如果包含聚合,添加原始响应 if ($includeAggregation) { $result['raw_response'] = $searchResult; } // 缓存结果(5分钟) \Yii::$app->cache->set($cacheKey, $result, self::CACHE_DURATION); return $result; }
An Error occurred while handling another error:
yii\web\HeadersAlreadySentException: Headers already sent in /www/wwwroot/enkilife.cn/addons/InvShop/common/services/ProductElasticsearchService.php on line 1. in /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/web/Response.php:368
Stack trace:
#0 /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/web/Response.php(341): yii\web\Response->sendHeaders()
#1 /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/web/ErrorHandler.php(136): yii\web\Response->send()
#2 /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/base/ErrorHandler.php(152): yii\web\ErrorHandler->renderException()
#3 [internal function]: yii\base\ErrorHandler->handleException()
#4 {main}
Previous exception:
yii\base\UnknownClassException: Unable to find 'addons\InvShop\common\services\ProductElasticsearchService' in file: /www/wwwroot/enkilife.cn/addons/InvShop/common/services/ProductElasticsearchService.php. Namespace missing? in /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/BaseYii.php:296
Stack trace:
#0 [internal function]: yii\BaseYii::autoload()
#1 /www/wwwroot/enkilife.cn/frontend/controllers/ProductController.php(579): spl_autoload_call()
#2 [internal function]: frontend\controllers\ProductController->actionIndex()
#3 /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array()
#4 /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/base/Controller.php(178): yii\base\InlineAction->runWithParams()
#5 /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/base/Module.php(552): yii\base\Controller->runAction()
#6 /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/web/Application.php(103): yii\base\Module->runAction()
#7 /www/wwwroot/enkilife.cn/vendor/yiisoft/yii2/base/Application.php(384): yii\web\Application->handleRequest()
#8 /www/wwwroot/enkilife.cn/web/index.php(48): yii\base\Application->run()
#9 {main}