海南作为国内热门旅游目的地,其独特的美食文化一直是吸引游客的重要因素。但在实际旅行中,游客常常面临"不知道吃什么"、"找不到地道美食"、"被网红店坑"等痛点。传统的美食APP又存在需要下载安装、占用手机空间等问题。
这个基于微信小程序的海南美食推荐系统,正是为了解决这些实际问题而设计的。它不需要下载安装,通过微信就能直接使用,为游客提供实时、精准的海南美食推荐服务。我在开发过程中特别注重三个核心价值点:
提示:选择微信小程序作为载体,不仅因为其免安装特性,更看重微信在旅游场景中的高渗透率 - 据统计,90%的国内游客都会使用微信进行旅游相关的沟通和支付。
前端采用微信小程序原生开发框架,这是经过多方面考量后的决定:
具体实现上:
javascript复制// 典型页面结构示例
Page({
data: {
dishes: [],
loading: true
},
onLoad() {
this.loadRecommendations()
},
async loadRecommendations() {
const res = await wx.cloud.callFunction({
name: 'getRecommendations',
data: { userId: getApp().globalData.userId }
})
this.setData({ dishes: res.result, loading: false })
}
})
后端采用Node.js + Express的组合,主要基于以下考虑:
核心接口设计遵循RESTful规范,主要接口包括:
javascript复制// 典型后端接口示例
router.get('/dishes', async (req, res) => {
try {
const { category, region } = req.query
const dishes = await Dish.find({
category,
region
}).limit(20)
res.json(dishes)
} catch (err) {
res.status(500).json({ error: err.message })
}
})
选用MySQL作为主数据库,主要表结构设计如下:
用户表(users)
| 字段 | 类型 | 描述 |
|---|---|---|
| id | INT | 主键 |
| openid | VARCHAR | 微信唯一标识 |
| nickname | VARCHAR | 微信昵称 |
| avatar | VARCHAR | 头像URL |
| preferences | TEXT | 饮食偏好(JSON) |
美食表(dishes)
| 字段 | 类型 | 描述 |
|---|---|---|
| id | INT | 主键 |
| name | VARCHAR | 美食名称 |
| description | TEXT | 详细介绍 |
| price | DECIMAL | 参考价格 |
| location | VARCHAR | 所在区域 |
| images | TEXT | 图片URL(JSON) |
| tags | TEXT | 标签(JSON) |
注意:tags字段存储如["海鲜","辣","特色"]等标签,用于后续的推荐算法
采用微信授权登录方案,流程如下:
javascript复制// 登录处理逻辑
app.post('/api/login', async (req, res) => {
const { code } = req.body
const wxRes = await axios.get(
`https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=${code}&grant_type=authorization_code`
)
const { openid } = wxRes.data
let user = await User.findOne({ openid })
if (!user) {
user = new User({ openid })
await user.save()
}
res.json({ userId: user.id })
})
实现基于协同过滤的混合推荐策略:
算法实现关键点:
javascript复制// 推荐算法核心逻辑
function generateRecommendations(user) {
// 获取相似用户
const similarUsers = findSimilarUsers(user.id)
// 收集候选菜品
const candidates = []
similarUsers.forEach(u => {
u.likedDishes.forEach(dish => {
if (!user.dislikedDishes.includes(dish.id)) {
candidates.push(dish)
}
})
})
// 加入基于内容的推荐
user.preferences.forEach(pref => {
const matched = Dish.find({ tags: pref })
candidates.push(...matched)
})
// 去重排序
return rankAndDeduplicate(candidates)
}
针对小程序特点做了多项优化:
javascript复制// 分页加载实现
let currentPage = 1
const loadMore = async () => {
if (this.data.loading || this.data.noMore) return
this.setData({ loading: true })
const res = await wx.request({
url: '/api/dishes',
data: { page: currentPage }
})
this.setData({
dishes: [...this.data.dishes, ...res.data],
loading: false
})
if (res.data.length < 20) {
this.setData({ noMore: true })
}
currentPage++
}
域名配置:必须在小程序后台配置合法域名,否则无法请求API
用户授权:getUserInfo接口调整后需要按钮触发
xml复制<button open-type="getUserInfo" bindgetuserinfo="onGetUserInfo">
授权登录
</button>
样式隔离:组件样式默认隔离,如需修改全局样式需设置
json复制{
"styleIsolation": "apply-shared"
}
索引优化:
连接池配置:
javascript复制const pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: 'password',
database: 'hainan_food'
})
慢查询监控:
sql复制SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
HTTPS必备:微信小程序要求所有接口必须使用HTTPS
CDN加速:静态资源建议使用CDN
监控报警:
bash复制# PM2启动命令
pm2 start app.js --name hainan-food --watch
在实际开发过程中,最大的体会是一定要平衡功能丰富性和用户体验。小程序的核心优势就是轻量化,过度追求功能全面反而会丧失这一优势。我们的做法是先把核心的推荐功能做到极致,再逐步添加辅助功能。