数据采集与融合技术大作业
食鲜配·智厨
| 这个项目属于哪个课程 | https://edu.cnblogs.com/campus/fzu/2025DataCollectionandFusiontechnology |
|---|---|
| 组名 | 风雨无组 |
| 项目名字及简介 | 食鲜配·智厨是一个基于Python 3.7+和Django 2.0.7的智能电商菜谱一体化平台,实现了"购物-菜谱-健康"的完整闭环,为用户提供"买什么-怎么做-吃得健康"一站式解决方案。 |
| 项目logo | ![]() |
| 团队成员学号 | 102302101、102302102、102302103、102302104、102302105、102302106、102302109、102302110 |
| 项目目标 | 实现网页端多源异构数据采集,构建用户画像并完成食材与菜谱的智能推荐,实现 AI 分析与 Web 应用展示,形成完整的数据采集与融合闭环。 |
| 其他参考文献 | https://github.com/CRIPAC-DIG/SR-GNN 、https://docs.djangoproject.com/ |
| gitee链接 | https://gitee.com/ding41/buy-menu |
系统架构设计
系统整体采用 B/S 架构,构建如下闭环流程:
多源数据采集 → 数据分析与融合 → 推荐算法 → Web系统展示 → 用户交互 → 行为反馈
技术栈
- 后端: Django 2.7, Python 3.11
- 数据库: SQLite
- 前端: HTML, CSS, JavaScript, jQuery
- 其他: Pillow (图片处理), django-haystack (搜索)
核心功能设计与实现
后台管理系统
- 商品全生命周期管理
- 菜谱管理与食材绑定
- 用户行为数据可视化
前台核心功能
商品浏览与购物车
- 分类浏览、关键词搜索
- 分量与处理方式选择
- 购物车实时价格更新
菜谱推荐与双向推荐算法 - 商品 → 菜谱推荐:基于商品标签匹配菜谱食材
- 菜谱 → 商品推荐:基于菜谱所需食材匹配商品
- 支持“一键加购”
AI 智能分析与个性化模块 - 食材搭配合理性分析
- 健康饮食偏好设置(低糖/低盐等)
- AI 助手提供烹饪与处理建议
个人工作(核心功能实现)
1. 用户系统后端
支持用户注册、登录、个人信息管理。使用了Django的认证系统,并添加了自定义装饰器来保护用户相关页面。
用户注册代码实现
def register_handle(request):
username = request.POST.get('user_name')
password = request.POST.get('pwd')
confirm_pwd = request.POST.get('confirm_pwd')
email = request.POST.get('email')
# 判断两次密码一致性
if password != confirm_pwd:
return redirect('/user/register/')
# 密码加密
s1 = sha1()
s1.update(password.encode('utf8'))
encrypted_pwd = s1.hexdigest()
# 创建对象
UserInfo.objects.create(uname=username, upwd=encrypted_pwd, uemail=email)
# 注册成功
context = {
'title': '用户登陆',
'username': username,
}
return render(request, 'df_user/login.html', context)
def register_exist(request):
username = request.GET.get('uname')
count = UserInfo.objects.filter(uname=username).count()
return JsonResponse({'count': count})
用户登录代码实现
def login_handle(request):
uname = request.POST.get('username')
upwd = request.POST.get('pwd')
jizhu = request.POST.get('jizhu', 0)
users = UserInfo.objects.filter(uname=uname)
if len(users) == 1:
s1 = sha1()
s1.update(upwd.encode('utf8'))
if s1.hexdigest() == users[0].upwd:
url = request.COOKIES.get('url', '/')
red = HttpResponseRedirect(url)
# 是否勾选记住用户名,设置cookie
if jizhu != 0:
red.set_cookie('uname', uname)
else:
red.set_cookie('uname', '', max_age=-1)
request.session['user_id'] = users[0].id
request.session['user_name'] = uname
return red
else:
# 密码错误处理
context = {
'title': '用户名登陆',
'error_name': 0,
'error_pwd': 1,
'uname': uname,
'upwd': upwd,
}
return render(request, 'df_user/login.html', context)
else:
# 用户名不存在处理
context = {
'title': '用户名登陆',
'error_name': 1,
'error_pwd': 0,
'uname': uname,
'upwd': upwd,
}
return render(request, 'df_user/login.html', context)
登录装饰器
def login(func):
def login_fun(request, *args, **kwargs):
if request.session.has_key('user_id'):
return func(request, *args, **kwargs)
else:
red = HttpResponseRedirect('/user/login/')
red.set_cookie('url', request.get_full_path())
return red
return login_fun
展示
登录界面

注册界面

2.AI食材搭配分析功能
项目集成了先进的AI功能,通过调用Deepseek大语言模型API来分析用户购物车中食材的搭配合理性,为用户提供智能化的营养建议和健康指导。
实现原理和核心代码
AI分析模块的核心位于 apps/utils/ai_analyzer.py
- FoodAnalyzer类:核心分析器类
class FoodAnalyzer: def __init__(self, api_key: str): self.api_key = api_key self.api_url = "https://api.deepseek.com/v1/chat/completions" self.headers = { "Content-Type": "application/json", "Authorization": f"Bearer {api_key}" }
__init__方法初始化分析器实例,设置API端点URL为Deepseek的chat completions接口,配置HTTP请求头,包含JSON内容类型和Bearer认证
-
API通信方法:
def _call_api(self, messages: List[Dict[str, str]], model: str = "deepseek-chat") -> str: try: data = { "model": model, "messages": messages, "temperature": AI_ANALYSIS_CONFIG['temperature'], "max_tokens": AI_ANALYSIS_CONFIG['max_tokens'] } response = requests.post(self.api_url, headers=self.headers, json=data, timeout=API_TIMEOUT) response.raise_for_status() result = response.json() return result["choices"][0]["message"]["content"] except requests.exceptions.RequestException as e: logger.error(f"API调用失败: {e}") return "抱歉,AI分析服务暂时不可用,请稍后再试。" except KeyError as e: logger.error(f"API响应格式错误: {e}") return "抱歉,AI分析服务出现异常,请稍后再试。" except Exception as e: logger.error(f"未知错误: {e}") return "抱歉,分析过程中出现错误,请稍后再试。"- 构建API请求数据,包含模型参数和对话消息
- 使用requests库发送POST请求,设置超时时间
- 解析JSON响应,提取AI的回复内容
- 多层异常处理:网络错误、响应格式错误、未知异常
- 返回用户友好的错误提示信息
-
数据预处理:
# 在analyze_food_combination方法中 food_list = [] for item in cart_items: food_name = item.get('goods_name', '') quantity = item.get('count', 1) category = item.get('category', '') if food_name: food_list.append(f"{food_name}({quantity}个) - {category}") food_str = "\n".join(food_list)
通过遍历购物车商品列表,提取商品名称、数量和类别,然后格式化每个商品为"名称(数量个) - 类别"的字符串
通过使用换行符连接所有商品,形成完整的食材清单
API调用流程
完整的API调用流程如下:
- 数据准备阶段:
if not cart_items: return {"success": False, "message": "购物车中没有商品", "analysis": None} # 提取商品信息...
首先检查购物车是否为空,返回错误信息,然后提取每个商品的名称、数量、类别信息
- 提示词构建:
定义系统角色为营养师专家,用户消息包含具体的分析要求和食材清单prompt = f""" 作为一名专业的营养师和食品安全专家,请分析以下购物车中的食材搭配是否合理: 购物车商品清单: {food_str} 请从以下几个维度进行分析: 1. 营养搭配分析... 2. 食品安全评估... 3. 适宜人群分析... 4. 健康建议... """ messages = [ {"role": "system", "content": "你是一位专业的营养师和食品安全专家,擅长分析食材搭配的营养价值和安全性。"}, {"role": "user", "content": prompt} ] - API请求执行:
调用内部的_call_api方法执行API请求,返回结构化的结果字典,包含成功状态、分析内容和统计信息analysis_result = self._call_api(messages) return { "success": True, "message": "分析完成", "analysis": analysis_result, "food_count": len(cart_items), "food_list": food_list }
分析维度详解
AI分析涵盖四个主要维度,每个维度都有具体的分析内容:
-
营养搭配分析:
- 评估宏营养素(蛋白质、碳水化合物、脂肪)比例
- 检查微营养素(维生素、矿物质)是否充足
- 分析膳食纤维和抗氧化物质含量
- 识别营养过剩或缺乏的风险
-
食品安全评估:
- 检查食材间的生化反应(如维生素C与铁的协同作用)
- 识别潜在的食物相克(如某些中药材与食物)
- 评估储存安全(如易腐烂食材的保存建议)
- 提供烹饪安全指导(如高温烹调的注意事项)
-
适宜人群分析:
- 识别适合的消费人群(如孕妇、儿童、老年人)
- 指出不适宜人群(如糖尿病患者、肾病患者)
- 考虑特殊饮食需求(如素食、宗教饮食限制)
-
健康建议:
- 推荐补充食材以完善营养结构
- 建议调整食材比例或替换选项
- 提供烹饪方法和食用建议
- 生成1-2个简单的食谱推荐
个性化建议功能
get_personalized_advice() 方法提供基于用户档案的定制化建议:
-
用户档案:
从前端GET参数获取用户档案信息,将数据库中的健康偏好强制添加到用户档案中user_profile = request.GET.dict() if health_pref: user_profile['health_preference'] = health_pref -
针对性分析:
检查用户是否有特殊的健康关注点,在提示词中添加重要标记,要求AI重点关注这些健康问题health_pref_str = "" if user_profile: pref = user_profile.get('health_preference', '') if pref: health_pref_str = f"\n🔴 【重要】用户特别健康关注:{pref} (请在分析时重点针对这些点进行警告或推荐)" -
定制化输出:
prompt = f""" 基于以下购物车内容和用户信息,提供个性化的营养建议: 购物车商品: {food_str} 用户信息: 年龄:{user_profile.get('age', '未知')} 性别:{user_profile.get('gender', '未知')} 健康状况:{user_profile.get('health_status', '无特殊疾病')} {health_pref_str} """
配置和调试模式
-
配置文件 (
config.py):AI_ANALYSIS_CONFIG = { 'max_food_items': 20, # 最多分析商品数量 'temperature': 0.7, # AI创造性程度 (0-1) 'max_tokens': 2000, # 最大回复长度 } DEBUG_AI = False # 调试模式开关 -
调试模式:
def get_analyzer() -> FoodAnalyzer: if DEBUG_AI or not DEEPSEEK_API_KEY or DEEPSEEK_API_KEY == "sk-your-deepseek-api-key-here": return MockFoodAnalyzer() return FoodAnalyzer(DEEPSEEK_API_KEY)
先检查调试模式或API密钥是否有效,如果条件满足,返回模拟分析器,否则返回真实的AI分析器实例
- 错误处理:
- API调用失败时返回友好提示
- 记录详细错误日志便于调试
- 保证系统在API不可用时仍能正常运行
展示

菜谱与食材的联动
数据模型设计
菜谱与食材的联动首先需要良好的数据模型设计。我通过三个核心模型来实现这一功能:
1. Recipe模型 - 食谱信息

class Recipe(models.Model):
"""食谱"""
cid = models.ForeignKey(RecipeType, on_delete=models.CASCADE, verbose_name="分类", db_column='cid')
title = models.CharField(max_length=100, verbose_name="食谱名称")
thumb = models.CharField(max_length=100, blank=True, null=True, verbose_name="缩略图")
videourl = models.CharField(max_length=200, blank=True, null=True, verbose_name="视频链接")
desc = models.CharField(max_length=500, blank=True, null=True, verbose_name="描述")
yl = models.CharField(max_length=500, blank=True, null=True, verbose_name="用料")
fl = models.CharField(max_length=500, blank=True, null=True, verbose_name="分量")
steptext = models.TextField(blank=True, null=True, verbose_name="步骤文字")
steppic = models.TextField(blank=True, null=True, verbose_name="步骤图片")
grade = models.FloatField(default=0, verbose_name="评分")
class Meta:
db_table = 'df_recipe'
verbose_name = "食谱"
verbose_name_plural = verbose_name
def __str__(self):
return self.title
2. GoodsInfo模型 - 商品(食材)信息

class GoodsInfo(models.Model):
# 具体商品信息
isDelete = models.BooleanField(default=False) # 逻辑删除
gtitle = models.CharField(max_length=20, verbose_name="商品名称", unique=True)
gpic = models.ImageField(verbose_name='商品图片', upload_to='df_goods/image/%Y/%m', null=True, blank=True)
gprice = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="商品价格")
gunit = models.CharField(max_length=20, default='500g', verbose_name="单位重量")
gclick = models.IntegerField(verbose_name="点击量", default=0, null=False)
gjianjie = models.CharField(max_length=200, verbose_name="简介")
gkucun = models.IntegerField(verbose_name="库存", default=0)
gcontent = HTMLField(max_length=200, verbose_name="详情")
gtype = models.ForeignKey(TypeInfo, on_delete=models.CASCADE, verbose_name="分类")
class Meta:
verbose_name = "商品"
verbose_name_plural = verbose_name
def __str__(self):
return self.gtitle
3. RecipeGoods模型 - 关联表

这是实现联动功能的核心模型:
class RecipeGoods(models.Model):
"""食谱与商品关联表"""
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, verbose_name="食谱", related_name='recipe_goods')
goods = models.ForeignKey(GoodsInfo, on_delete=models.CASCADE, verbose_name="商品", related_name='goods_recipes')
amount = models.CharField(max_length=50, blank=True, null=True, verbose_name="用量说明")
class Meta:
db_table = 'df_recipe_goods'
verbose_name = "食谱食材"
verbose_name_plural = verbose_name
unique_together = ('recipe', 'goods') # 防止重复关联
def __str__(self):
return f"{self.recipe.title} - {self.goods.gtitle}"
通过RecipeGoods模型,我建立了菜谱和食材之间的多对多关系,并记录了每个食材在菜谱中的用量。
后端视图实现
1. 商品详情页视图
在商品详情页,我需要显示与该食材相关的所有菜谱:
def detail(request, gid):
"""商品详情页视图"""
try:
goods = GoodsInfo.objects.get(pk=int(gid))
except GoodsInfo.DoesNotExist:
from django.shortcuts import redirect
return redirect('df_goods:index')
goods.gclick += 1
goods.save()
news = goods.gtype.goodsinfo_set.order_by('-id')[0:2]
# 获取与该商品相关的所有食谱
recipe_goods = RecipeGoods.objects.filter(goods_id=gid).select_related('recipe')
recipes = [rg.recipe for rg in recipe_goods]
context = {
'title': goods.gtype.ttitle,
'guest_cart': 1 if 'user_id' in request.session else 0,
'cart_num': cart_count(request),
'goods': goods,
'news': news,
'id': gid,
'recipes': recipes, # 传递食谱列表到模板
}
response = render(request, 'df_goods/detail.html', context)
# ... 处理浏览记录的代码
return response
2. 食谱详情API
提供获取食谱详细信息和关联食材的API接口:
def recipe_detail(request, recipe_id):
"""获取食谱详情和关联商品的API"""
try:
recipe = Recipe.objects.get(pk=recipe_id)
# 获取食谱关联的所有商品
recipe_goods = RecipeGoods.objects.filter(recipe_id=recipe_id).select_related('goods')
goods_list = []
for rg in recipe_goods:
goods_list.append({
'id': rg.goods.id,
'title': rg.goods.gtitle,
'price': str(rg.goods.gprice),
'pic': str(rg.goods.gpic) if rg.goods.gpic else '',
'unit': rg.goods.gunit,
'kucun': rg.goods.gkucun,
'amount': rg.amount or '', # 用量说明
})
# 解析步骤
steps = []
if recipe.steptext:
step_texts = recipe.steptext.split('#')
step_pics = recipe.steppic.split('#') if recipe.steppic else []
for i, text in enumerate(step_texts):
if text.strip():
steps.append({
'text': text.strip(),
'pic': step_pics[i] if i < len(step_pics) else ''
})
# 解析用料
ingredients = []
if recipe.yl and recipe.fl:
yl_list = recipe.yl.split('#')
fl_list = recipe.fl.split('#')
for i, yl in enumerate(yl_list):
if yl.strip():
ingredients.append({
'name': yl.strip(),
'amount': fl_list[i].strip() if i < len(fl_list) else ''
})
data = {
'success': True,
'recipe': {
'id': recipe.id,
'title': recipe.title,
'thumb': recipe.thumb or '',
'desc': recipe.desc or '',
'videourl': recipe.videourl or '',
'grade': recipe.grade,
'ingredients': ingredients,
'steps': steps,
},
'goods': goods_list,
}
return JsonResponse(data)
except Recipe.DoesNotExist:
return JsonResponse({'success': False, 'message': '食谱不存在'})
except Exception as e:
return JsonResponse({'success': False, 'message': str(e)})
URL配置
在urls.py中配置相关的路由:
urlpatterns = [
url('^$', views.index, name="index"),
url('^list(\d+)_(\d+)_(\d+)/$', views.good_list, name="good_list"),
url('^(\d+)/$', views.detail, name="detail"),
url(r'^search/', views.ordinary_search, name="ordinary_search"),
url(r'^recipe/(\d+)/$', views.recipe_detail, name="recipe_detail"), # 食谱详情API
]
前端实现
1. 商品详情页模板
在商品详情页左侧添加食谱推荐区域:
<!-- 食谱推荐 -->
{% if recipes %}
<div class="recipe_recommend">
<h3>食谱推荐</h3>
<ul class="recipe_list">
{% for recipe in recipes %}
<li class="recipe_item" data-recipe-id="{{ recipe.id }}">
<div class="recipe_thumb">
<img src="/{{ recipe.thumb }}" alt="{{ recipe.title }}">
</div>
<div class="recipe_info">
<h4>{{ recipe.title }}</h4>
<span class="recipe_grade">评分: {{ recipe.grade }}</span>
</div>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
2. 动态标签页
在右侧添加用于显示食谱详情的标签页:
<div class="tab_content" id="tabContent">
<!-- 商品介绍 -->
<div class="tab_pane active" id="goodsPane">
<dl>
<dt>商品详情:</dt>
<dd>{{ goods.gcontent|safe }}</dd>
</dl>
</div>
<!-- 食谱介绍 -->
<div class="tab_pane" id="recipePane" style="display:none;">
<div id="recipeContent">
<p class="recipe_placeholder">请从左侧选择一个食谱查看详情</p>
</div>
</div>
<!-- 食材用量 -->
<div class="tab_pane" id="ingredientPane" style="display:none;">
<div id="ingredientContent">
<p class="ingredient_placeholder">请从左侧选择一个食谱查看食材</p>
</div>
</div>
</div>
JavaScript交互实现
1. 食谱点击事件
$(document).ready(function(){
// ... 其他初始化代码
// 点击食谱
$('.recipe_item').click(function() {
var recipeId = $(this).data('recipe-id');
$('.recipe_item').removeClass('active');
$(this).addClass('active');
loadRecipeDetail(recipeId);
});
// Tab切换
$('#detailTabs li').click(function() {
var tab = $(this).data('tab');
$('#detailTabs li').removeClass('active');
$(this).addClass('active');
$('.tab_pane').hide();
if (tab === 'goods') {
$('#goodsPane').show();
} else if (tab === 'comment') {
$('#commentPane').show();
} else if (tab === 'recipe') {
$('#recipePane').show();
} else if (tab === 'ingredient') {
$('#ingredientPane').show();
}
});
});
2. 加载食谱详情
// 加载食谱详情
function loadRecipeDetail(recipeId) {
$.ajax({
url: '/recipe/' + recipeId + '/',
type: 'GET',
dataType: 'json',
success: function(data) {
if (data.success) {
currentRecipeData = data;
renderRecipeContent(data);
// 切换到食谱介绍tab
updateTabsForRecipe();
$('#detailTabs li[data-tab="recipe"]').click();
} else {
alert(data.message || '加载失败');
}
},
error: function() {
alert('网络错误');
}
});
}
// 更新tabs显示食谱相关选项
function updateTabsForRecipe() {
var tabs = $('#detailTabs');
if (tabs.find('[data-tab="recipe"]').length === 0) {
tabs.append('<li data-tab="recipe">食谱介绍</li>');
tabs.append('<li data-tab="ingredient">食材用量</li>');
// 重新绑定点击事件
tabs.find('li').off('click').on('click', function() {
var tab = $(this).data('tab');
$('#detailTabs li').removeClass('active');
$(this).addClass('active');
$('.tab_pane').hide();
$('#' + tab + 'Pane').show();
});
}
}
3. 渲染食谱内容
// 渲染食谱内容
function renderRecipeContent(data) {
var recipe = data.recipe;
var goods = data.goods;
// 渲染食谱介绍
var recipeHtml = '<div class="recipe_detail">';
recipeHtml += '<h3>' + recipe.title + '</h3>';
if (recipe.thumb) {
recipeHtml += '<div class="recipe_main_img"><img src="/' + recipe.thumb + '" alt=""></div>';
}
if (recipe.desc) {
recipeHtml += '<p class="recipe_desc">' + recipe.desc + '</p>';
}
if (recipe.videourl) {
recipeHtml += '<p><a href="' + recipe.videourl + '" target="_blank" class="video_link">📺 观看视频教程</a></p>';
}
// 步骤
if (recipe.steps && recipe.steps.length > 0) {
recipeHtml += '<div class="recipe_steps"><h4>制作步骤</h4><ol>';
recipe.steps.forEach(function(step, index) {
recipeHtml += '<li class="step_item">';
recipeHtml += '<div class="step_text">' + step.text + '</div>';
if (step.pic) {
recipeHtml += '<div class="step_pic"><img src="/' + step.pic + '" alt="步骤' + (index+1) + '"></div>';
}
recipeHtml += '</li>';
});
recipeHtml += '</ol></div>';
}
recipeHtml += '</div>';
$('#recipeContent').html(recipeHtml);
// 渲染食材用量
var ingredientHtml = '<div class="ingredient_detail">';
ingredientHtml += '<h3>食材用量</h3>';
// 关联商品
if (goods && goods.length > 0) {
ingredientHtml += '<div class="related_goods"><h4>可购买食材</h4><ul class="goods_grid">';
goods.forEach(function(g) {
ingredientHtml += '<li class="goods_card" data-goods=\'' + JSON.stringify(g) + '\'>';
ingredientHtml += '<div class="goods_img"><img src="/media/' + g.pic + '" alt=""></div>';
ingredientHtml += '<div class="goods_name">' + g.title + '</div>';
ingredientHtml += '<div class="goods_price">¥' + g.price + '/' + g.unit + '</div>';
if (g.amount) {
ingredientHtml += '<div class="goods_amount">建议用量: ' + g.amount + '</div>';
}
ingredientHtml += '<button class="add_to_cart_btn">加入购物车</button>';
ingredientHtml += '</li>';
});
ingredientHtml += '</ul></div>';
}
ingredientHtml += '</div>';
$('#ingredientContent').html(ingredientHtml);
// 绑定商品点击事件
$('.goods_card .add_to_cart_btn').click(function(e) {
e.stopPropagation();
var goodsData = $(this).closest('.goods_card').data('goods');
showIngredientModal(goodsData);
});
}
展示


个性化界面
用户数据模型设计

个性化功能的核心在于用户数据的收集和分析。我们通过扩展Django的用户模型来实现个性化数据存储:
1. 扩展用户模型
class UserInfo(models.Model):
uname = models.CharField(max_length=20, verbose_name="用户名", unique=True)
upwd = models.CharField(max_length=40, verbose_name="用户密码", blank=False)
uemail = models.EmailField(verbose_name="邮箱", unique=True)
ushou = models.CharField(max_length=20, default="", verbose_name="收货地址")
uaddress = models.CharField(max_length=100, default="", verbose_name="地址")
uyoubian = models.CharField(max_length=6, default="", verbose_name="邮编")
uphone = models.CharField(max_length=11, default="", verbose_name="手机号")
# === 个性化字段 ===
# 用于存储健康偏好,例如 "低糖,低胆固醇"
health_preference = models.CharField(max_length=255, default='', verbose_name='健康偏好', blank=True)
class Meta:
verbose_name = "用户信息"
verbose_name_plural = verbose_name
def __str__(self):
return self.uname
2. 用户浏览记录模型
class GoodsBrowser(models.Model):
user = models.ForeignKey(UserInfo, on_delete=models.CASCADE, verbose_name="用户ID")
good = models.ForeignKey(GoodsInfo, on_delete=models.CASCADE, verbose_name="商品ID")
browser_time = models.DateTimeField(default=datetime.now, verbose_name="浏览时间")
class Meta:
verbose_name = "用户浏览记录"
verbose_name_plural = verbose_name
def __str__(self):
return "{0}浏览记录{1}".format(self.user.uname, self.good.gtitle)
健康偏好设置功能
1. 偏好设置界面设计
健康偏好是用户个性化体验的核心。我们设计了一个友好的弹窗界面,让用户轻松设置自己的健康关注点:
<!-- 健康偏好弹窗组件 -->
<div id="preference_modal" class="pref-modal" style="display: none;">
<div class="pref-content">
<div class="pref-header">
<h3>🍅 个性化饮食设置</h3>
<p>请问您关注食物的什么方面?AI将为您提供定制建议。</p>
</div>
<div class="pref-body">
<div class="tag-container">
<span class="pref-tag" data-value="低糖">🍬 低糖</span>
<span class="pref-tag" data-value="低胆固醇">🥩 低胆固醇</span>
<span class="pref-tag" data-value="低盐">🧂 低盐</span>
<span class="pref-tag" data-value="高蛋白">💪 高蛋白</span>
<span class="pref-tag" data-value="低卡路里">🥗 低卡路里</span>
<span class="pref-tag" data-value="素食">🥬 素食</span>
<span class="pref-tag" data-value="无麸质">🌾 无麸质</span>
</div>
<input type="hidden" id="selected_prefs" value="">
</div>
<div class="pref-footer">
<button onclick="savePreferences()" class="save-btn">确认并保存</button>
<a href="javascript:closeModal()" class="skip-link">暂时跳过</a>
</div>
</div>
</div>
2. 弹窗样式设计
.pref-modal {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.6); z-index: 9999;
display: flex; justify-content: center; align-items: center;
backdrop-filter: blur(2px);
}
.pref-content {
background: white; width: 420px; border-radius: 16px; padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2); text-align: center;
animation: modalFadeIn 0.3s ease;
}
.pref-tag {
padding: 8px 18px; border: 1px solid #eee; border-radius: 25px; cursor: pointer;
color: #555; transition: all 0.2s; background: #f9f9f9; font-size: 14px;
}
.pref-tag.active {
background: linear-gradient(135deg, #ff8800 0%, #ff5500 100%);
color: white; border-color: transparent;
box-shadow: 0 4px 10px rgba(255, 85, 0, 0.3);
}
3. 偏好设置后端逻辑
@user_decorator.login
def save_health_preference(request):
"""保存用户的健康偏好"""
if request.method == 'POST':
preferences = request.POST.get('preferences', '')
uid = request.session.get('user_id')
try:
user = UserInfo.objects.get(id=uid)
user.health_preference = preferences
user.save()
return JsonResponse({'success': True, 'message': '偏好设置成功'})
except Exception as e:
return JsonResponse({'success': False, 'message': str(e)})
return JsonResponse({'success': False, 'message': '请求方法错误'})
4. 弹窗交互逻辑
$(function() {
// 检查是否已设置偏好
var currentPref = "{{ user_info.health_preference|default:'' }}";
// 如果偏好为空,显示弹窗
if (currentPref === "") {
setTimeout(function(){
$('#preference_modal').fadeIn();
}, 500);
}
// 标签点击交互
$('.pref-tag').click(function() {
$(this).toggleClass('active');
updateSelected();
});
});
function updateSelected() {
var selected = [];
$('.pref-tag.active').each(function() {
selected.push($(this).data('value'));
});
$('#selected_prefs').val(selected.join(','));
}
function savePreferences() {
var prefs = $('#selected_prefs').val();
if(!prefs) {
alert("请至少选择一项,或者点击跳过");
return;
}
$.ajax({
url: '{% url "df_user:save_preference" %}',
type: 'POST',
data: {
'preferences': prefs,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(res) {
if(res.success) {
alert("✅ 设置成功!AI将为您关注这些健康指标。");
closeModal();
setTimeout(function(){ location.reload(); }, 500);
} else {
alert("保存失败:" + res.message);
}
},
error: function() {
alert("网络请求错误,请稍后再试");
}
});
}

5部署项目到华为云服务器
连接华为云服务器

安装宝塔linux面板
wget -O install.sh http://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh
安装成功后会出现一下信息:可以看到外网和内网的访问面板,我们在Windows端使用外网面板地址即可访问,然后输入对应的username和password。

根据终端的提示放通华为云的端口

安装项目对应的python版本
sudo apt update
sudo apt install software-properties-common
# 使用Apt-Get安装Python 3.11
# 打开系统上的终端,然后为系统配置Deadsnakes PPA。
sudo add-apt-repository ppa:deadsnakes/ppa
# 在Ubuntu系统上添加ppa后,更新apt缓存并在Ubuntu上安装Python 3.11
sudo apt update
sudo apt install python3.11
# 等待安装完成。通过执行以下命令检查Python版本:
python3.11 -V
# 至此,Python 3.11已安装在Ubuntu系统上并可以使用。
创建软链接:
# 重新指定python为python3的软连接
sudo ln -s /usr/bin/python3.11 /usr/bin/python
查看最后是否安装成功:
pyhton -V

安装虚拟环境(防止意外发生导致系统奔溃):
apt-get update
apt-get upgrade
pip install virtualenv
cd ~
cd /home
virtualenv django #在django文件夹下创建虚拟环境
cd django
source bin/activate #激活虚拟环境
如下图左端有(django)标识,则表示创建成功

将Django项目文件上传至华为云服务器

修改上传到服务器的django文件中的seeting.py文件
使得其他ip也能够访问Django项目

安装Django项目的依赖库
将项目的依赖库整理成如下文档

读取安装依赖库
pip install -Ur requirements.txt

启动Django项目
前台运行(关闭远程后程序会结束运行)
python manage.py runserver 0.0.0.0:8080
后台运行(关闭远程后程序不会结束运行)
nohup python manage.py runserver 0.0.0.0:8080 &
运行结果如下

查看服务器IP


运行对应的url即可
结果如下

注意事项
关闭防火墙
记得进入宝塔界面,把服务器防火墙关掉,这样外界才可以访问这个端口的数据

个人心得
- 后端开发能力的深化:通过主导用户系统和AI食材搭配分析功能的开发,我对Django框架的理解从基础应用提升到了实战深度。例如在用户认证模块中,不仅熟练运用了Django自带的认证系统,还通过自定义装饰器实现了页面权限控制,深入理解了session与cookie的协同工作机制,解决了用户登录状态保持、登录后跳转原页面等实际问题。
- AI接口集成与异常处理思维的建立:在对接Deepseek大语言模型API时,我学会了规范的接口调用流程,包括请求头配置、参数构造、响应解析等。更重要的是,通过设计多层异常处理机制,覆盖了网络错误、响应格式错误、未知异常等场景,确保了系统在AI服务不稳定时仍能正常运行,培养了“防御性编程”的思维。
- 明确分工与责任意识的强化:作为团队的一员,我负责核心功能的后端实现与部分前端交互逻辑开发。在项目推进过程中,通过与前端成员明确数据交互格式,与其他后端成员协调数据库模型设计,确保了各模块的兼容对接。这让我深刻认识到,清晰的分工和强烈的责任意识是团队项目顺利推进的关键。


浙公网安备 33010602011771号