猜你喜欢
This commit is contained in:
parent
27dd992508
commit
db30092871
|
|
@ -635,13 +635,13 @@ class Countfood extends Base{
|
||||||
$bz['text'] = '超标';
|
$bz['text'] = '超标';
|
||||||
$bz['color'] = '#FF0000';
|
$bz['color'] = '#FF0000';
|
||||||
}
|
}
|
||||||
array_push($return_data,['time'=>$key,'val'=>$value,'unit'=>'kcal','describe'=>$bz['text'],'color'=>$bz['color']]);
|
array_push($return_data,['time'=>$key,'title'=>'摄入卡路里','val'=>$value,'unit'=>'kcal','describe'=>$bz['text'],'color'=>$bz['color']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump($return_data);
|
// dump($return_data);
|
||||||
return $this->msg([
|
return $this->msg([
|
||||||
// 'page_now'=>$data['page'],
|
'page_now'=>$data['page'],
|
||||||
// 'page_total'=>$page_total,
|
'page_total'=>$page_total,
|
||||||
'content_list'=>$return_data
|
'content_list'=>$return_data
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,728 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace app\KitchenScale\controller\app;
|
||||||
|
|
||||||
|
use think\Db;
|
||||||
|
use think\Controller;
|
||||||
|
|
||||||
|
class Guessyoulike extends Controller {
|
||||||
|
|
||||||
|
protected $kitchenscale_db_msg = [
|
||||||
|
'cookbook' => 'app_user_cookbook',
|
||||||
|
'cookbook_label' => 'app_user_cookbook_label',
|
||||||
|
'foodlist2' => 'app_z_national_standard_food_type_2',
|
||||||
|
'foodlist3' => 'app_z_national_standard_food_type_3',
|
||||||
|
'kcal_log' => 'app_user_kcal_log',
|
||||||
|
'search_log' => 'app_user_search_log',
|
||||||
|
'search_history' => 'app_user_search_history',
|
||||||
|
'tag_preference' => 'app_user_tag_preference',
|
||||||
|
'recommend_cache' => 'app_recommend_cache'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $config = [
|
||||||
|
'tag_limit' => 5,
|
||||||
|
'item_limit' => 8,
|
||||||
|
'cache_time' => 3600
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 猜你喜欢主接口
|
||||||
|
*/
|
||||||
|
public function getGuessYouLike($user_id, $input_tags = 'cookbook', $limit = null) {
|
||||||
|
try {
|
||||||
|
if (empty($user_id)) {
|
||||||
|
return ['code' => 0, 'msg' => '用户ID不能为空', 'data' => []];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($input_tags, ['cookbook', 'food'])) {
|
||||||
|
return ['code' => 0, 'msg' => '类型参数错误,只能是cookbook或food', 'data' => []];
|
||||||
|
}
|
||||||
|
|
||||||
|
$tag_limit = $limit ?: $this->config['tag_limit'];
|
||||||
|
$item_limit = $this->config['item_limit'];
|
||||||
|
|
||||||
|
$cache_key = $this->generateCacheKey($user_id, $input_tags, $tag_limit);
|
||||||
|
$cached_result = $this->getCachedRecommendation($cache_key);
|
||||||
|
|
||||||
|
if ($cached_result) {
|
||||||
|
return $cached_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_new_user = $this->isNewUser($user_id);
|
||||||
|
|
||||||
|
if ($is_new_user) {
|
||||||
|
$result = $this->getHotRecommendationsForNewUser($input_tags, $item_limit);
|
||||||
|
} else {
|
||||||
|
if ($input_tags === 'cookbook') {
|
||||||
|
$result = $this->getCookbookRecommendations($user_id, $tag_limit, $item_limit);
|
||||||
|
} else {
|
||||||
|
$result = $this->getFoodRecommendations($user_id, $tag_limit, $item_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cacheRecommendation($cache_key, $result, $user_id, $input_tags);
|
||||||
|
|
||||||
|
return ['code' => 1, 'msg' => '获取成功', 'data' => $result];
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return ['code' => 0, 'msg' => '推荐系统异常: ' . $e->getMessage(), 'data' => []];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否是新用户
|
||||||
|
*/
|
||||||
|
private function isNewUser($user_id) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
$has_search_history = $cfc->table($this->kitchenscale_db_msg['search_history'])
|
||||||
|
->where('user_id', $user_id)
|
||||||
|
->where('is_del', 0)
|
||||||
|
->count();
|
||||||
|
|
||||||
|
$has_diet_history = $cfc->table($this->kitchenscale_db_msg['kcal_log'])
|
||||||
|
->where('aud_id', $user_id)
|
||||||
|
->where('is_del', 0)
|
||||||
|
->count();
|
||||||
|
|
||||||
|
return ($has_search_history + $has_diet_history) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为新用户获取热门推荐(只有一个"热门搜索"标签)
|
||||||
|
*/
|
||||||
|
private function getHotRecommendationsForNewUser($input_tags, $item_limit) {
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
if ($input_tags === 'cookbook') {
|
||||||
|
$hot_recipes = $this->getHotRecipes($item_limit);
|
||||||
|
$result['热门搜索'] = $hot_recipes;
|
||||||
|
} else {
|
||||||
|
$hot_foods = $this->getHotFoods($item_limit);
|
||||||
|
$result['热门搜索'] = $hot_foods;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取热门菜谱(基于所有用户的搜索记录和饮食记录)
|
||||||
|
*/
|
||||||
|
private function getHotRecipes($limit) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
// 方法1:从搜索记录中获取热门菜谱关键词
|
||||||
|
$hot_search_keywords = $cfc->table($this->kitchenscale_db_msg['search_history'])
|
||||||
|
->where('is_del', 0)
|
||||||
|
->group('keyword')
|
||||||
|
->order('SUM(search_count) DESC')
|
||||||
|
->limit($limit * 2)
|
||||||
|
->field('keyword, SUM(search_count) as total_searches')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
$recipes = [];
|
||||||
|
foreach ($hot_search_keywords as $search_item) {
|
||||||
|
$keyword = $search_item['keyword'];
|
||||||
|
|
||||||
|
// 查找匹配的菜谱
|
||||||
|
$recipe = $cfc->table($this->kitchenscale_db_msg['cookbook'])
|
||||||
|
->where("(title LIKE '%".$keyword."%' OR describe_data LIKE '%".$keyword."%') and is_del = 0")
|
||||||
|
->field('id, title as name, "cookbook" as type')
|
||||||
|
->order('create_time DESC')
|
||||||
|
->find();
|
||||||
|
|
||||||
|
if ($recipe && !in_array($recipe['id'], array_column($recipes, 'id'))) {
|
||||||
|
$recipes[] = $recipe;
|
||||||
|
if (count($recipes) >= $limit) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法2:从饮食记录中分析热门菜谱(通过食材关联)
|
||||||
|
if (count($recipes) < $limit) {
|
||||||
|
$remaining = $limit - count($recipes);
|
||||||
|
|
||||||
|
// 获取热门食材
|
||||||
|
$hot_foods = $cfc->table($this->kitchenscale_db_msg['kcal_log'] . ' kcal')
|
||||||
|
->join($this->kitchenscale_db_msg['foodlist3'] . ' f3', 'kcal.food_id = f3.id')
|
||||||
|
->where('kcal.is_del', 0)
|
||||||
|
->group('f3.id, f3.name')
|
||||||
|
->order('COUNT(*) DESC')
|
||||||
|
->limit(10)
|
||||||
|
->field('f3.id, f3.name')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
foreach ($hot_foods as $food) {
|
||||||
|
// 查找包含这些食材的菜谱
|
||||||
|
$related_recipes = $cfc->table($this->kitchenscale_db_msg['cookbook'])
|
||||||
|
->where("(title LIKE '%".$food['name']."%' OR describe_data LIKE '%".$food['name']."%') and is_del = 0")
|
||||||
|
->whereNotIn('id', array_column($recipes, 'id'))
|
||||||
|
->field('id, title as name, "cookbook" as type')
|
||||||
|
->order('create_time DESC')
|
||||||
|
->limit($remaining)
|
||||||
|
->select();
|
||||||
|
|
||||||
|
if ($related_recipes) {
|
||||||
|
$recipes = array_merge($recipes, $related_recipes);
|
||||||
|
if (count($recipes) >= $limit) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法3:如果还不够,从菜谱表中按创建时间获取最新的
|
||||||
|
if (count($recipes) < $limit) {
|
||||||
|
$remaining = $limit - count($recipes);
|
||||||
|
$more_recipes = $cfc->table($this->kitchenscale_db_msg['cookbook'])
|
||||||
|
->where('is_del', 0)
|
||||||
|
->whereNotIn('id', array_column($recipes, 'id'))
|
||||||
|
->field('id, title as name, "cookbook" as type')
|
||||||
|
->order('create_time DESC')
|
||||||
|
->limit($remaining)
|
||||||
|
->select();
|
||||||
|
|
||||||
|
$recipes = array_merge($recipes, $more_recipes ?: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_slice($recipes, 0, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取热门食材(基于所有用户的饮食记录和搜索记录)
|
||||||
|
*/
|
||||||
|
private function getHotFoods($limit) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
$foods = [];
|
||||||
|
|
||||||
|
// 方法1:从饮食记录中获取热门食材
|
||||||
|
$hot_from_diet = $cfc->table($this->kitchenscale_db_msg['kcal_log'] . ' kcal')
|
||||||
|
->join($this->kitchenscale_db_msg['foodlist3'] . ' f3', 'kcal.food_id = f3.id')
|
||||||
|
->where('kcal.is_del', 0)
|
||||||
|
->where('f3.is_del', 0)
|
||||||
|
->group('f3.id, f3.name')
|
||||||
|
->order('COUNT(*) DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->field('f3.id, f3.name, "food" as type, COUNT(*) as eat_count')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
if ($hot_from_diet) {
|
||||||
|
$foods = array_merge($foods, $hot_from_diet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法2:从搜索记录中获取食材相关关键词
|
||||||
|
if (count($foods) < $limit) {
|
||||||
|
$remaining = $limit - count($foods);
|
||||||
|
|
||||||
|
$food_keywords = $cfc->table($this->kitchenscale_db_msg['search_history'])
|
||||||
|
->where('is_del', 0)
|
||||||
|
->group('keyword')
|
||||||
|
->order('SUM(search_count) DESC')
|
||||||
|
->limit($remaining * 2)
|
||||||
|
->field('keyword, SUM(search_count) as total_searches')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
foreach ($food_keywords as $keyword_item) {
|
||||||
|
$keyword = $keyword_item['keyword'];
|
||||||
|
|
||||||
|
// 查找匹配的食材
|
||||||
|
$food = $cfc->table($this->kitchenscale_db_msg['foodlist3'])
|
||||||
|
->where('name', 'like', '%' . $keyword . '%')
|
||||||
|
->where('is_del', 0)
|
||||||
|
->whereNotIn('id', array_column($foods, 'id'))
|
||||||
|
->field('id, name, "food" as type')
|
||||||
|
->find();
|
||||||
|
|
||||||
|
if ($food) {
|
||||||
|
$foods[] = $food;
|
||||||
|
if (count($foods) >= $limit) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法3:如果还不够,从食材表中获取常用食材
|
||||||
|
if (count($foods) < $limit) {
|
||||||
|
$remaining = $limit - count($foods);
|
||||||
|
$more_foods = $cfc->table($this->kitchenscale_db_msg['foodlist3'])
|
||||||
|
->where('is_del', 0)
|
||||||
|
->whereNotIn('id', array_column($foods, 'id'))
|
||||||
|
->field('id, name, "food" as type')
|
||||||
|
->order('id DESC')
|
||||||
|
->limit($remaining)
|
||||||
|
->select();
|
||||||
|
|
||||||
|
$foods = array_merge($foods, $more_foods ?: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_slice($foods, 0, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取食谱推荐(老用户)
|
||||||
|
*/
|
||||||
|
private function getCookbookRecommendations($user_id, $tag_limit, $item_limit) {
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
$preferred_tags = $this->getUserCookbookTags($user_id, $tag_limit);
|
||||||
|
|
||||||
|
if (empty($preferred_tags)) {
|
||||||
|
$preferred_tags = $this->getHotCookbookTags($tag_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($preferred_tags as $tag_info) {
|
||||||
|
$tag_name = $tag_info['tag_name'];
|
||||||
|
$recipes = $this->getRecipesByTag($tag_name, $item_limit);
|
||||||
|
|
||||||
|
if (!empty($recipes)) {
|
||||||
|
$result[$tag_name] = $recipes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户食谱标签(基于用户搜索记录和饮食记录)
|
||||||
|
*/
|
||||||
|
private function getUserCookbookTags($user_id, $limit) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
$tags = [];
|
||||||
|
|
||||||
|
// 从用户搜索记录中获取标签
|
||||||
|
$user_search_tags = $cfc->table($this->kitchenscale_db_msg['search_history'])
|
||||||
|
->where('user_id', $user_id)
|
||||||
|
->where('is_del', 0)
|
||||||
|
->order('search_count DESC, last_searched_at DESC')
|
||||||
|
->limit($limit * 2)
|
||||||
|
->field('keyword, search_count')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
foreach ($user_search_tags as $search_item) {
|
||||||
|
$keyword = $search_item['keyword'];
|
||||||
|
|
||||||
|
// 验证关键词是否对应有效的菜谱标签
|
||||||
|
$valid_tag = $cfc->table($this->kitchenscale_db_msg['cookbook_label'])
|
||||||
|
->where('label_name', 'like', '%' . $keyword . '%')
|
||||||
|
->where('is_del', 0)
|
||||||
|
->find();
|
||||||
|
|
||||||
|
if ($valid_tag) {
|
||||||
|
$tags[] = [
|
||||||
|
'tag_name' => $valid_tag['label_name'],
|
||||||
|
'tag_id' => $valid_tag['id'],
|
||||||
|
'source' => 'search',
|
||||||
|
'weight' => $search_item['search_count']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($tags) >= $limit) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从用户饮食记录中分析标签(通过食材关联菜谱标签)
|
||||||
|
if (count($tags) < $limit) {
|
||||||
|
$user_diet_foods = $cfc->table($this->kitchenscale_db_msg['kcal_log'] . ' kcal')
|
||||||
|
->join($this->kitchenscale_db_msg['foodlist3'] . ' f3', 'kcal.food_id = f3.id')
|
||||||
|
->where('kcal.aud_id', $user_id)
|
||||||
|
->where('kcal.is_del', 0)
|
||||||
|
->group('f3.id, f3.name')
|
||||||
|
->order('COUNT(*) DESC')
|
||||||
|
->limit(5)
|
||||||
|
->field('f3.id, f3.name, COUNT(*) as eat_count')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
foreach ($user_diet_foods as $food) {
|
||||||
|
// 查找包含这些食材的菜谱标签
|
||||||
|
$related_tags = $cfc->table($this->kitchenscale_db_msg['cookbook'] . ' c')
|
||||||
|
->join($this->kitchenscale_db_msg['cookbook_label'] . ' cl', 'FIND_IN_SET(cl.id, c.cook_label)')
|
||||||
|
->where('c.food_ids', 'like', '%"' . $food['id'] . '"%')
|
||||||
|
->where('c.is_del', 0)
|
||||||
|
->where('cl.is_del', 0)
|
||||||
|
->group('cl.id, cl.label_name')
|
||||||
|
->order('COUNT(*) DESC')
|
||||||
|
->limit(2)
|
||||||
|
->field('cl.id, cl.label_name, COUNT(*) as recipe_count')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
foreach ($related_tags as $tag) {
|
||||||
|
if (!in_array($tag['label_name'], array_column($tags, 'tag_name'))) {
|
||||||
|
$tags[] = [
|
||||||
|
'tag_name' => $tag['label_name'],
|
||||||
|
'tag_id' => $tag['id'],
|
||||||
|
'source' => 'diet',
|
||||||
|
'weight' => $food['eat_count']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($tags) >= $limit) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($tags) >= $limit) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按权重排序
|
||||||
|
usort($tags, function($a, $b) {
|
||||||
|
return $b['weight'] <=> $a['weight'];
|
||||||
|
});
|
||||||
|
|
||||||
|
return array_slice($tags, 0, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取热门食谱标签(基于所有用户数据)
|
||||||
|
*/
|
||||||
|
private function getHotCookbookTags($limit) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
// 从搜索记录中分析热门标签
|
||||||
|
$hot_search_tags = $cfc->table($this->kitchenscale_db_msg['search_history'])
|
||||||
|
->where('is_del', 0)
|
||||||
|
->group('keyword')
|
||||||
|
->order('SUM(search_count) DESC')
|
||||||
|
->limit($limit * 2)
|
||||||
|
->field('keyword, SUM(search_count) as total_searches')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
$tags = [];
|
||||||
|
foreach ($hot_search_tags as $search_item) {
|
||||||
|
$keyword = $search_item['keyword'];
|
||||||
|
|
||||||
|
$valid_tag = $cfc->table($this->kitchenscale_db_msg['cookbook_label'])
|
||||||
|
->where('label_name', 'like', '%' . $keyword . '%')
|
||||||
|
->where('is_del', 0)
|
||||||
|
->find();
|
||||||
|
|
||||||
|
if ($valid_tag && !in_array($valid_tag['label_name'], array_column($tags, 'tag_name'))) {
|
||||||
|
$tags[] = [
|
||||||
|
'tag_name' => $valid_tag['label_name'],
|
||||||
|
'tag_id' => $valid_tag['id'],
|
||||||
|
'search_count' => $search_item['total_searches']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($tags) >= $limit) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不够,从标签表中获取
|
||||||
|
if (count($tags) < $limit) {
|
||||||
|
$remaining = $limit - count($tags);
|
||||||
|
$more_tags = $cfc->table($this->kitchenscale_db_msg['cookbook_label'])
|
||||||
|
->where('is_del', 0)
|
||||||
|
->whereNotIn('id', array_column($tags, 'tag_id'))
|
||||||
|
->field('id, label_name as tag_name')
|
||||||
|
->order('id DESC')
|
||||||
|
->limit($remaining)
|
||||||
|
->select();
|
||||||
|
|
||||||
|
$tags = array_merge($tags, $more_tags ?: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_slice($tags, 0, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据标签获取食谱
|
||||||
|
*/
|
||||||
|
private function getRecipesByTag($tag_name, $limit) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
$recipes = $cfc->table($this->kitchenscale_db_msg['cookbook'] . ' c')
|
||||||
|
->join($this->kitchenscale_db_msg['cookbook_label'] . ' cl', 'FIND_IN_SET(cl.id, c.cook_label)')
|
||||||
|
->where('cl.label_name', 'like', '%' . $tag_name . '%')
|
||||||
|
->where('c.is_del', 0)
|
||||||
|
->where('cl.is_del', 0)
|
||||||
|
->field('c.id, c.title as name, "cookbook" as type')
|
||||||
|
->order('c.create_time DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->select();
|
||||||
|
|
||||||
|
return $recipes ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取食材推荐(老用户)
|
||||||
|
*/
|
||||||
|
private function getFoodRecommendations($user_id, $tag_limit, $item_limit) {
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
$preferred_categories = $this->getUserFoodCategories($user_id, $tag_limit);
|
||||||
|
|
||||||
|
if (empty($preferred_categories)) {
|
||||||
|
$preferred_categories = $this->getHotFoodCategories($tag_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($preferred_categories as $category_info) {
|
||||||
|
$category_name = $category_info['category_name'];
|
||||||
|
$foods = $this->getFoodsByCategory($category_info['category_id'], $item_limit);
|
||||||
|
|
||||||
|
if (!empty($foods)) {
|
||||||
|
$result[$category_name] = $foods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户食材分类(基于用户饮食记录)
|
||||||
|
*/
|
||||||
|
private function getUserFoodCategories($user_id, $limit) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
$categories = [];
|
||||||
|
|
||||||
|
$diet_categories = $cfc->table($this->kitchenscale_db_msg['kcal_log'] . ' kcal')
|
||||||
|
->join($this->kitchenscale_db_msg['foodlist3'] . ' f3', 'kcal.food_id = f3.id')
|
||||||
|
->join($this->kitchenscale_db_msg['foodlist2'] . ' f2', 'f3.two_id = f2.id')
|
||||||
|
->where('kcal.aud_id', $user_id)
|
||||||
|
->where('kcal.is_del', 0)
|
||||||
|
->group('f2.id, f2.name')
|
||||||
|
->order('COUNT(*) DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->field('f2.id as category_id, f2.name as category_name, COUNT(*) as eat_count')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
foreach ($diet_categories as $category) {
|
||||||
|
$categories[] = [
|
||||||
|
'category_id' => $category['category_id'],
|
||||||
|
'category_name' => $category['category_name'],
|
||||||
|
'eat_count' => $category['eat_count']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_slice($categories, 0, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取热门食材分类(基于所有用户数据)
|
||||||
|
*/
|
||||||
|
private function getHotFoodCategories($limit) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
// 从饮食记录中分析热门分类
|
||||||
|
$hot_categories = $cfc->table($this->kitchenscale_db_msg['kcal_log'] . ' kcal')
|
||||||
|
->join($this->kitchenscale_db_msg['foodlist3'] . ' f3', 'kcal.food_id = f3.id')
|
||||||
|
->join($this->kitchenscale_db_msg['foodlist2'] . ' f2', 'f3.two_id = f2.id')
|
||||||
|
->where('kcal.is_del', 0)
|
||||||
|
->group('f2.id, f2.name')
|
||||||
|
->order('COUNT(*) DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->field('f2.id as category_id, f2.name as category_name, COUNT(*) as total_eat_count')
|
||||||
|
->select();
|
||||||
|
|
||||||
|
if ($hot_categories && count($hot_categories) >= $limit) {
|
||||||
|
return $hot_categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不够,从分类表中获取
|
||||||
|
$categories = $hot_categories ?: [];
|
||||||
|
if (count($categories) < $limit) {
|
||||||
|
$remaining = $limit - count($categories);
|
||||||
|
$more_categories = $cfc->table($this->kitchenscale_db_msg['foodlist2'])
|
||||||
|
->where('is_del', 0)
|
||||||
|
->whereNotIn('id', array_column($categories, 'category_id'))
|
||||||
|
->field('id as category_id, name as category_name')
|
||||||
|
->order('id DESC')
|
||||||
|
->limit($remaining)
|
||||||
|
->select();
|
||||||
|
|
||||||
|
$categories = array_merge($categories, $more_categories ?: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_slice($categories, 0, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类获取食材
|
||||||
|
*/
|
||||||
|
private function getFoodsByCategory($category_id, $limit) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
$foods = $cfc->table($this->kitchenscale_db_msg['foodlist3'])
|
||||||
|
->where('two_id', $category_id)
|
||||||
|
->where('is_del', 0)
|
||||||
|
->field('id, name, "food" as type')
|
||||||
|
->order('id DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->select();
|
||||||
|
|
||||||
|
return $foods ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成缓存key
|
||||||
|
*/
|
||||||
|
private function generateCacheKey($user_id, $input_tags, $tag_limit) {
|
||||||
|
return $user_id . ':' . $input_tags . ':' . $tag_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存推荐
|
||||||
|
*/
|
||||||
|
private function getCachedRecommendation($cache_key) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
$cache = $cfc->table($this->kitchenscale_db_msg['recommend_cache'])
|
||||||
|
->where('cache_key', $cache_key)
|
||||||
|
->where('is_del', 0)
|
||||||
|
->where('last_hit', '>=', date('Y-m-d H:i:s', time() - $this->config['cache_time']))
|
||||||
|
->find();
|
||||||
|
|
||||||
|
if ($cache) {
|
||||||
|
$cfc->table($this->kitchenscale_db_msg['recommend_cache'])
|
||||||
|
->where('id', $cache['id'])
|
||||||
|
->update([
|
||||||
|
'hit_count' => $cache['hit_count'] + 1,
|
||||||
|
'last_hit' => date('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
|
||||||
|
return json_decode($cache['recommend_data'], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存推荐结果
|
||||||
|
*/
|
||||||
|
private function cacheRecommendation($cache_key, $data, $user_id, $input_tags) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
$cache_data = [
|
||||||
|
'cache_key' => $cache_key,
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'keyword' => $input_tags,
|
||||||
|
'recommend_data' => json_encode($data, JSON_UNESCAPED_UNICODE),
|
||||||
|
'hit_count' => 1,
|
||||||
|
'last_hit' => date('Y-m-d H:i:s'),
|
||||||
|
'create_time' => date('Y-m-d H:i:s')
|
||||||
|
];
|
||||||
|
|
||||||
|
$cfc->table($this->kitchenscale_db_msg['recommend_cache'])
|
||||||
|
->insert($cache_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录用户标签点击(用于更新偏好权重)
|
||||||
|
*/
|
||||||
|
public function recordTagClick($user_id, $tag_name, $tag_type) {
|
||||||
|
try {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
$preference = $cfc->table($this->kitchenscale_db_msg['tag_preference'])
|
||||||
|
->where('user_id', $user_id)
|
||||||
|
->where('tag_name', $tag_name)
|
||||||
|
->where('tag_type', $tag_type)
|
||||||
|
->where('is_del', 0)
|
||||||
|
->find();
|
||||||
|
|
||||||
|
if ($preference) {
|
||||||
|
$new_weight = min($preference['preference_weight'] + 1, 10);
|
||||||
|
$cfc->table($this->kitchenscale_db_msg['tag_preference'])
|
||||||
|
->where('id', $preference['id'])
|
||||||
|
->update([
|
||||||
|
'preference_weight' => $new_weight,
|
||||||
|
'last_updated' => date('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$cfc->table($this->kitchenscale_db_msg['tag_preference'])
|
||||||
|
->insert([
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'tag_type' => $tag_type,
|
||||||
|
'tag_name' => $tag_name,
|
||||||
|
'tag_id' => 0,
|
||||||
|
'preference_weight' => 1,
|
||||||
|
'create_time' => date('Y-m-d H:i:s'),
|
||||||
|
'last_updated' => date('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除用户推荐缓存
|
||||||
|
*/
|
||||||
|
public function clearUserCache($user_id) {
|
||||||
|
try {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
$cfc->table($this->kitchenscale_db_msg['recommend_cache'])
|
||||||
|
->where('user_id', $user_id)
|
||||||
|
->update(['is_del' => 1]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取推荐统计信息
|
||||||
|
*/
|
||||||
|
public function getRecommendStats($user_id) {
|
||||||
|
$cfc = Db::connect('cfc_db');
|
||||||
|
|
||||||
|
$cache_stats = $cfc->table($this->kitchenscale_db_msg['recommend_cache'])
|
||||||
|
->where('user_id', $user_id)
|
||||||
|
->where('is_del', 0)
|
||||||
|
->field('COUNT(*) as cache_count, SUM(hit_count) as total_hits')
|
||||||
|
->find();
|
||||||
|
|
||||||
|
$preference_stats = $cfc->table($this->kitchenscale_db_msg['tag_preference'])
|
||||||
|
->where('user_id', $user_id)
|
||||||
|
->where('is_del', 0)
|
||||||
|
->field('COUNT(*) as tag_count, AVG(preference_weight) as avg_weight')
|
||||||
|
->find();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'cache_count' => $cache_stats['cache_count'] ?? 0,
|
||||||
|
'total_hits' => $cache_stats['total_hits'] ?? 0,
|
||||||
|
'tag_count' => $preference_stats['tag_count'] ?? 0,
|
||||||
|
'avg_weight' => round($preference_stats['avg_weight'] ?? 0, 2)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试方法
|
||||||
|
*/
|
||||||
|
public function test() {
|
||||||
|
$user_id = 1;
|
||||||
|
|
||||||
|
// 测试新用户食谱推荐
|
||||||
|
$result1 = $this->getGuessYouLike($user_id, 'cookbook');
|
||||||
|
|
||||||
|
// 测试新用户食材推荐
|
||||||
|
$result2 = $this->getGuessYouLike($user_id, 'food');
|
||||||
|
|
||||||
|
// 自定义数量
|
||||||
|
$result3 = $this->getGuessYouLike($user_id, 'cookbook', 3);
|
||||||
|
|
||||||
|
// 记录标签点击
|
||||||
|
$this->recordTagClick($user_id, '家常菜', 'cookbook_label');
|
||||||
|
|
||||||
|
// 获取统计信息
|
||||||
|
$stats = $this->getRecommendStats($user_id);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'cookbook_result' => $result1,
|
||||||
|
'food_result' => $result2,
|
||||||
|
'custom_limit' => $result3,
|
||||||
|
'stats' => $stats
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功返回方法
|
||||||
|
*/
|
||||||
|
private function success($msg, $data = []) {
|
||||||
|
return ['code' => 1, 'msg' => $msg, 'data' => $data];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误返回方法
|
||||||
|
*/
|
||||||
|
private function error($msg) {
|
||||||
|
return ['code' => 0, 'msg' => $msg, 'data' => []];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,11 @@ class Index extends Base{
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected $reedaw_db_name = [
|
||||||
|
'banner'=>'admin_notice_banner',//菜谱表
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
// 加 bcadd(,,20)
|
// 加 bcadd(,,20)
|
||||||
// 减 bcsub(,,20)
|
// 减 bcsub(,,20)
|
||||||
|
|
@ -417,30 +422,37 @@ class Index extends Base{
|
||||||
$return_data['kcal_data']['kcal']['color'] = '#FF0000';
|
$return_data['kcal_data']['kcal']['color'] = '#FF0000';
|
||||||
}
|
}
|
||||||
// 处理计食器信息 end
|
// 处理计食器信息 end
|
||||||
// 处理banner信息 start
|
|
||||||
$collect_list = $cfc->table($this->kitchenscale_db_msg['collect_list'])->where(['token'=>$data['token'],'is_del'=>0])->column('cookbook_id');
|
|
||||||
foreach ($banner_list as $key => $value) {
|
|
||||||
if(array_key_exists($value['id'],$collect_list)){
|
|
||||||
$banner_list[$key]['is_me_like_it'] = 'yes';
|
|
||||||
}else{
|
|
||||||
$banner_list[$key]['is_me_like_it'] = 'no';
|
|
||||||
}
|
|
||||||
unset($banner_list[$key]['ROW_NUMBER']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理banner信息 end
|
|
||||||
// 处理搜索历史 start
|
// 处理搜索历史 start
|
||||||
$search_log = $cfc->table($this->kitchenscale_db_msg['search_log'])->where(["aud_id"=>$user['id'],'is_del'=>0])->order('id desc')->limit(30)->select();
|
$search_log = $cfc->table($this->kitchenscale_db_msg['search_log'])->where(["aud_id"=>$user['id'],'is_del'=>0])->order('id desc')->limit(30)->select();
|
||||||
|
|
||||||
$return_data['search_history'] = $search_log;
|
$return_data['search_history'] = $search_log;
|
||||||
// 处理搜索历史 end
|
// 处理搜索历史 end
|
||||||
}else{
|
}else{
|
||||||
foreach ($banner_list as $key => $value) {
|
// foreach ($banner_list as $key => $value) {
|
||||||
$banner_list[$key]['is_me_like_it'] = 'no';
|
// $banner_list[$key]['is_me_like_it'] = 'no';
|
||||||
unset($banner_list[$key]['ROW_NUMBER']);
|
// unset($banner_list[$key]['ROW_NUMBER']);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
// 处理banner信息 start
|
||||||
|
// $collect_list = $cfc->table($this->kitchenscale_db_msg['collect_list'])->where(['token'=>$data['token'],'is_del'=>0])->column('cookbook_id');
|
||||||
|
// foreach ($banner_list as $key => $value) {
|
||||||
|
// if(array_key_exists($value['id'],$collect_list)){
|
||||||
|
// $banner_list[$key]['is_me_like_it'] = 'yes';
|
||||||
|
// }else{
|
||||||
|
// $banner_list[$key]['is_me_like_it'] = 'no';
|
||||||
|
// }
|
||||||
|
// unset($banner_list[$key]['ROW_NUMBER']);
|
||||||
|
// }
|
||||||
|
$banner_list = Db::table($this->reedaw_db_name['banner'])->where(['scene_data' => '3','is_del'=>0])->cache(3600)->order('sort_num desc')->field('id,type,pic,jump_url,parameter_data,sort_num')->select();
|
||||||
|
for ($i=0; $i < count($banner_list); $i++) {
|
||||||
|
if($banner_list[$i]['type'] != 1){
|
||||||
|
$banner_list[$i]['parameter_data'] = '';
|
||||||
|
}
|
||||||
|
unset($banner_list[$i]['sort_num']);
|
||||||
|
unset($banner_list[$i]['ROW_NUMBER']);
|
||||||
|
}
|
||||||
|
// 处理banner信息 end
|
||||||
$return_data['banner_data'] = $banner_list;
|
$return_data['banner_data'] = $banner_list;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue