去年帮朋友改造他家祖传的私房菜馆时,我深刻体会到传统菜谱管理的痛点:手写笔记容易丢失、纸质菜谱难以分享、口味调整记录混乱。这促使我设计了一套基于SpringBoot+Vue的Android端家庭菜谱系统,它完美解决了以下问题:
这个系统特别适合:
后端选型上,SpringBoot提供了现成的解决方案:
前端采用Vue+Android混合方案:
菜谱主体的ER图包含这些关键字段:
java复制@Entity
public class Recipe {
@Id @GeneratedValue
private Long id;
private String title;
@Lob private String steps; // 带步骤图片的富文本
@ManyToMany(cascade=CascadeType.PERSIST)
private Set<Ingredient> ingredients;
@Enumerated(EnumType.STRING)
private DifficultyLevel level; // 难易度枚举
@ElementCollection
private Map<String, Boolean> tags; // 快手菜/低脂等标签
}
特别设计了"智能换算"功能:
使用Android的SpeechRecognizer实现:
kotlin复制val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, "zh-CN")
putExtra(RecognizerIntent.EXTRA_PROMPT, "说'下一步'继续")
}
startActivityForResult(intent, VOICE_REQUEST_CODE)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == VOICE_REQUEST_CODE && resultCode == RESULT_OK) {
data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)?.let { results ->
if (results.any { it.contains("下一步") }) flipToNextStep()
}
}
}
实际测试发现的问题:抽油烟机噪音会导致误识别,最终增加降噪预处理和二次确认机制
整合百度AI的食材识别接口:
优化点:
采用组合式分享:
防爬虫措施:
使用WebSocket实现:
java复制@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/recipe-chat")
.setAllowedOrigins("*")
.withSockJS();
}
}
前端处理断线重连:
javascript复制this.stompClient = Stomp.over(new SockJS('/recipe-chat'))
this.stompClient.connect({}, () => {
this.stompClient.subscribe('/topic/comments/' + recipeId, comment => {
this.comments.push(JSON.parse(comment.body))
})
}, () => setTimeout(this.connect, 5000)) // 5秒后重连
采用三级缓存策略:
通过Glide配置:
kotlin复制GlideApp.with(this)
.load(imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.loading_food)
.error(R.drawable.error_food)
.into(imageView)
典型慢查询:首页推荐菜谱(需综合评分、收藏数、作者等级)
解决方案:
sql复制CREATE MATERIALIZED VIEW recommended_recipes AS
SELECT r.* FROM recipe r
JOIN user u ON r.author_id = u.id
WHERE r.status = 'PUBLISHED'
ORDER BY
(r.average_rating * 0.6 +
LOG(2, r.favorite_count + 1) * 0.3 +
u.level * 0.1) DESC
LIMIT 100;
-- 每天凌晨刷新物化视图
REFRESH MATERIALIZED VIEW recommended_recipes;
初期遇到的两个极端:
最终方案:
验证有效的三种方法:
数据表现:
正在开发中的功能:
待解决的技术债:
这个项目给我的最大启示是:技术要为真实场景服务。比如最初设计的"精确到克"的称重功能,实际发现家庭烹饪更依赖"适量"、"少许"这样的模糊描述,后来改为可选模式才真正被用户接受。