'app_user_cookbook', //食谱表 // 'cookbook_label' => 'app_user_cookbook_label', //食谱标签表 // 'cookbook_food_relation' => 'app_user_cookbook_food_relation', //食谱跟食材关系表 'foodlist2' => 'app_z_national_standard_food_type_2_multilingual', //食材标签表2 'foodlist3' => 'app_z_national_standard_food_type_3_multilingual', //食材表 'kcal_log' => 'app_user_kcal_log_multilingual', //用户饮食记录表记录用户吃了什么食材 'search_history' => 'app_user_search_history_multilingual', //用户搜索记录表,记录用户搜索过什么内容 // 'tag_preference' => 'app_user_tag_preference', //用户标签偏好表 'recommend_cache' => 'app_recommend_cache_multilingual' //智能推荐缓存表 ]; protected $config = [ 'tag_limit' => 2, 'item_limit' => 12, 'cache_time' => 3600 ]; protected $translations = [ 'zh' => '搜索最多的食材', 'en' => 'Most Searched Ingredients', 'jp' => '最も検索された食材', 'fra' => 'Ingrédients les Plus Recherchés', 'de' => 'Am Häufigsten Gesuchte Lebensmittel', 'kor' => '가장 많이 검색된 재료', 'ru' => 'Самые Искомые Ингредиенты', 'pt' => 'Ingredientes Mais Pesquisados', 'spa' => 'Ingredientes Más Buscados', 'ara' => 'المكونات الأكثر بحثًا' ]; /** * 猜你喜欢主接口 */ public function getGuessYouLike($user_id = 1, $type = 'food', $limit = null,$language = 'zh') { try { $cfc = Db::connect('cfc_db'); // dump(1); // 设置限制数量 $tag_limit = $limit ? intval($limit) : $this->config['tag_limit']; $item_limit = $this->config['item_limit']; // 检查缓存 // $cache_key = $user_id . ':' . $type; // $cache_result = $this->getCache($cfc, $cache_key); // // die; // if ($cache_result !== null) { // return $cache_result; // } // dump($language); // 判断用户是否有历史数据 $has_history = $this->checkUserHistory($cfc, $user_id); if (!$has_history) { // 新用户,返回最火信息(仅一个标签) $result = $this->getPopularRecommendations($cfc, $type, 1, $item_limit,$language); } else { // 老用户,根据类型返回个性化推荐 if ($type === 'cookbook') { // $result = $this->getCookbookRecommendations($cfc, $user_id, $tag_limit, $item_limit,$language); } else { $result = $this->getFoodRecommendations($cfc, $user_id, $tag_limit, $item_limit,$language); } } // 确保返回格式正确 if (!is_array($result)) { $result = []; } // 更新缓存 // $this->updateCache($cfc, $cache_key, $user_id, $type, $result); return $result; } catch (\Exception $e) { // 记录错误日志 \think\Log::error('猜你喜欢功能错误: ' . $e->getMessage()); return []; } } /** * 检查用户是否有历史数据 */ private function checkUserHistory($db, $user_id) { try { // 检查饮食记录 $kcal_result = $db->query(" SELECT COUNT(*) as count FROM {$this->kitchenscale_db_msg['kcal_log']} WHERE aud_id = ? AND is_del = 0 ", [$user_id]); $kcal_count = $kcal_result[0]['count'] ?? 0; // 检查搜索记录 $search_result = $db->query(" SELECT COUNT(*) as count FROM {$this->kitchenscale_db_msg['search_history']} WHERE user_id = ? AND is_del = 0 ", [$user_id]); $search_count = $search_result[0]['count'] ?? 0; return ($kcal_count > 0 || $search_count > 0); } catch (\Exception $e) { return false; } } /** * 获取缓存数据 */ private function getCache($db, $cache_key) { try { $cache_result = $db->query(" SELECT id, cache_key, user_id, keyword, recommend_data, hit_count, last_hit, is_del, create_time FROM {$this->kitchenscale_db_msg['recommend_cache']} WHERE cache_key = ? AND is_del = 0 ", [$cache_key]); if (!empty($cache_result)) { $cache = $cache_result[0]; $last_hit_timestamp = strtotime($cache['last_hit']); if (time() - $last_hit_timestamp < $this->config['cache_time']) { // 更新命中次数和时间 $db->execute(" UPDATE {$this->kitchenscale_db_msg['recommend_cache']} SET hit_count = hit_count + 1, last_hit = GETDATE() WHERE id = ? ", [$cache['id']]); $data = json_decode($cache['recommend_data'], true); return is_array($data) ? $data : null; } } } catch (\Exception $e) { // 忽略缓存错误,继续执行 } return null; } /** * 更新缓存 */ private function updateCache($db, $cache_key, $user_id, $keyword, $data) { try { $current_time = date('Y-m-d H:i:s'); $recommend_data = json_encode($data, JSON_UNESCAPED_UNICODE); // 检查是否存在缓存 $existing_result = $db->query(" SELECT id FROM {$this->kitchenscale_db_msg['recommend_cache']} WHERE cache_key = ? AND is_del = 0 ", [$cache_key]); if (!empty($existing_result)) { // 更新现有缓存 $db->execute(" UPDATE {$this->kitchenscale_db_msg['recommend_cache']} SET user_id = ?, keyword = ?, recommend_data = ?, hit_count = 1, last_hit = ?, create_time = ? WHERE cache_key = ? AND is_del = 0 ", [$user_id, $keyword, $recommend_data, $current_time, $current_time, $cache_key]); } else { // 插入新缓存 $db->execute(" INSERT INTO {$this->kitchenscale_db_msg['recommend_cache']} (cache_key, user_id, keyword, recommend_data, hit_count, last_hit, create_time, is_del) VALUES (?, ?, ?, ?, 1, ?, ?, 0) ", [$cache_key, $user_id, $keyword, $recommend_data, $current_time, $current_time]); } } catch (\Exception $e) { // 忽略缓存更新错误 } } /** * 获取热门推荐(新用户) */ private function getPopularRecommendations($db, $type, $tag_limit, $item_limit,$language) { // dump($type); if ($type === 'cookbook') { return $this->getPopularCookbooks($db, $tag_limit, $item_limit); } else { // dump(111); return $this->getPopularFoods($db, $tag_limit, $item_limit,$language); } } /** * 获取热门食谱(新用户) */ private function getPopularCookbooks($db, $tag_limit, $item_limit) { try { // 简化查询,避免复杂关联导致的错误 $popular_cookbooks = $db->query(" SELECT TOP {$item_limit} id, title as name FROM {$this->kitchenscale_db_msg['cookbook']} WHERE is_del = 0 ORDER BY likes_num DESC, read_it DESC, create_time DESC "); // dump('sp'); // dump($popular_cookbooks); $result = []; $label_data = []; foreach ($popular_cookbooks as $cookbook) { $label_data[] = [ 'name' => $cookbook['name'] ?? '未知食谱', 'id' => $cookbook['id'] ?? 0, 'type' => 'cookbook' ]; } if (!empty($label_data)) { $result['最火食谱搜索'] = $label_data; } return $result; } catch (\Exception $e) { return []; } } /** * 获取热门食材(新用户) */ private function getPopularFoods($db, $tag_limit, $item_limit,$language) { try { // dump(2222); // // 简化查询,避免复杂关联导致的错误 $popular_foods = $db->query(" SELECT TOP {$item_limit} id, keyword as name, COUNT(*) as num FROM {$this->kitchenscale_db_msg['search_history']} WHERE is_del = 0 AND type = 'food' GROUP BY id, keyword ORDER BY num DESC "); // dump('sc'); // dump($popular_foods); $popular_foods_2 = []; if(count($popular_foods) < $item_limit){ $num = $item_limit - count($popular_foods); $popular_foods_2 = $db->query(" SELECT TOP {$num} id, food_name_".$language." as name FROM {$this->kitchenscale_db_msg['foodlist3']} WHERE is_del = 0 ORDER BY is_popular DESC, food_name_".$language." ASC "); } foreach ($popular_foods_2 as $key => $value) { $popular_foods[] = $value; } $result = []; $label_data = []; foreach ($popular_foods as $food) { $label_data[] = [ 'name' => $food['name'] ?? '????', 'id' => $food['id'] ?? 0, 'type' => 'food' ]; } if (!empty($label_data)) { $result[$this->translations[$language]] = $label_data; } return $result; } catch (\Exception $e) { return []; } } /** * 获取个性化食谱推荐(老用户) */ private function getCookbookRecommendations($db, $user_id, $tag_limit, $item_limit) { try { // 获取用户最常吃的食材 $user_top_foods = $db->query(" SELECT TOP 10 food_id, COUNT(*) as eat_count FROM {$this->kitchenscale_db_msg['kcal_log']} WHERE aud_id = ? AND is_del = 0 GROUP BY food_id ORDER BY eat_count DESC ", [$user_id]); if (empty($user_top_foods)) { return $this->getPopularCookbooks($db, $tag_limit, $item_limit); } $food_ids = array_column($user_top_foods, 'food_id'); if (empty($food_ids)) { return $this->getPopularCookbooks($db, $tag_limit, $item_limit); } $food_ids_str = implode(',', $food_ids); // 获取包含这些食材的食谱标签 $preferred_labels = $db->query(" SELECT TOP {$tag_limit} lbl.id, lbl.name, COUNT(DISTINCT cb.id) as match_count FROM {$this->kitchenscale_db_msg['cookbook_label']} lbl INNER JOIN {$this->kitchenscale_db_msg['cookbook']} cb ON lbl.id = cb.cook_label AND cb.is_del = 0 INNER JOIN {$this->kitchenscale_db_msg['cookbook_food_relation']} cfr ON cb.id = cfr.cookbook_id WHERE lbl.is_del = 0 AND cfr.food_id IN ({$food_ids_str}) GROUP BY lbl.id, lbl.name ORDER BY match_count DESC "); $result = []; foreach ($preferred_labels as $label) { // 使用子查询避免GROUP BY复杂性问题 $cookbooks = $db->query(" SELECT TOP {$item_limit} cb.id, cb.title as name FROM {$this->kitchenscale_db_msg['cookbook']} cb WHERE cb.id IN ( SELECT DISTINCT cfr.cookbook_id FROM {$this->kitchenscale_db_msg['cookbook_food_relation']} cfr WHERE cfr.food_id IN ({$food_ids_str}) ) AND cb.cook_label = ? AND cb.is_del = 0 ORDER BY cb.likes_num DESC, cb.read_it DESC ", [$label['id']]); $label_data = []; foreach ($cookbooks as $cookbook) { $label_data[] = [ 'name' => $cookbook['name'] ?? '未知食谱', 'id' => $cookbook['id'] ?? 0, 'type' => 'cookbook' ]; } if (!empty($label_data)) { $result[$label['name'] ?? '未知标签'] = $label_data; } } return $result; } catch (\Exception $e) { return $this->getPopularCookbooks($db, $tag_limit, $item_limit); } } /** * 获取个性化食材推荐(老用户) */ private function getFoodRecommendations($db, $user_id, $tag_limit, $item_limit,$language) { try { // 获取用户最常吃的食材 $user_top_foods = $db->query(" SELECT TOP 10 food_id, COUNT(*) as eat_count FROM {$this->kitchenscale_db_msg['kcal_log']} WHERE aud_id = ? AND is_del = 0 GROUP BY food_id ORDER BY eat_count DESC ", [$user_id]); // dump($user_id); // dump($language); // dump($user_top_foods); if (empty($user_top_foods)) { return $this->getPopularFoods($db, $tag_limit, $item_limit,$language); } $food_ids = array_column($user_top_foods, 'food_id'); if (empty($food_ids)) { return $this->getPopularFoods($db, $tag_limit, $item_limit,$language); } $food_ids_str = implode(',', $food_ids); // 获取用户偏好食材的分类 $preferred_categories = $db->query(" SELECT TOP {$tag_limit} f2.id, f2.name, COUNT(DISTINCT f3.id) as food_count FROM {$this->kitchenscale_db_msg['foodlist2']} f2 INNER JOIN {$this->kitchenscale_db_msg['foodlist3']} f3 ON f2.id = f3.two_id WHERE f3.id IN ({$food_ids_str}) AND f2.is_del = 0 AND f3.is_del = 0 GROUP BY f2.id, f2.name ORDER BY food_count DESC "); $result = []; foreach ($preferred_categories as $category) { // 获取该分类下的其他食材 $foods = $db->query(" SELECT TOP {$item_limit} id, food_name_".$language." as name FROM {$this->kitchenscale_db_msg['foodlist3']} WHERE two_id = ? AND is_del = 0 AND id NOT IN ({$food_ids_str}) ORDER BY is_popular DESC, food_name_".$language." ASC ", [$category['id']]); $category_data = []; foreach ($foods as $food) { $category_data[] = [ 'name' => $food['name'] ?? '????', 'id' => $food['id'] ?? 0, 'type' => 'food' ]; } if (!empty($category_data)) { $result[$category['name'] ?? '????'] = $category_data; } } return $result; } catch (\Exception $e) { return $this->getPopularFoods($db, $tag_limit, $item_limit,$language); } } }