【免费】LLM大模型 微信小程序AI智能扫码点餐(订餐)系统(LangChain+FastAPI+Vue3) 适合毕业设计,课程设计,锋哥原创出品,必属精品
大家好,我是Java1234_小锋老师,分享一套锋哥原创的LLM大模型 微信小程序AI智能扫码点餐(订餐)系统(LangChain+FastAPI+Vue3)。

项目介绍
餐饮门店的点餐方式正在从纸质菜单和口头报菜转向扫码自助。顾客坐下后希望尽快看到菜品、价格和口味,也希望在不熟悉菜单时得到靠谱的推荐。普通点餐小程序能够完成浏览和下单,但很难回答“有没有不辣的下饭菜”“营业到几点”“花生过敏能不能吃宫保鸡丁”这类问题。若直接把问题交给通用大模型,模型又可能编造店里没有的菜或说错价格。
针对上述问题,本文设计并实现了一套基于LLM大模型的微信小程序AI智能扫码点餐(订餐)系统。系统以Python和FastAPI构建后端,以微信小程序作为顾客端,以Vue3和Element Plus构建管理后台。业务数据存放在MySQL数据库db_diancan中,菜品图片、桌台二维码和向量库存放在本地目录。智能点餐部分采用LangChain组织检索增强生成流程:把上架菜品和门店知识写入Chroma,顾客提问时先检索相关资料,再交给通义千问生成回答,并按SSE流式返回。推荐具体菜品时,回答中带有菜品编号标记,便于前端定位菜品。
系统覆盖扫码选桌、菜品浏览、购物车下单、模拟微信支付、收银台结账、订单查询、账号注册与个人中心,以及管理端的数据概览、菜品维护、桌号二维码和知识库管理。订单金额由服务端按当前单价计算,避免顾客篡改价格。向量库同步失败时只影响助手检索,不阻断正常点餐。测试表明,主要业务流程能够按设计完成,助手回答能够落在已录入的菜品和门店规则之内。
源码下载
链接: https://pan.baidu.com/s/1yZ4-4y_MH6s9fkl3NMHzqQ?pwd=1234
提取码: 1234
系统展示









核心代码
"""后台首页统计。"""
from datetime import date, datetime, time, timedelta
from decimal import Decimal
from sqlalchemy import text
from sqlalchemy.orm import Session
from app.models import DineOrder, Dish, User
class DashboardService:
"""卡片、近 7 天销售额、类目占比、菜品排行和订单状态。"""
def stat(self, db: Session) -> dict:
"""汇总首页图表需要的数据。"""
today = date.today()
start_of_day = datetime.combine(today, time.min)
start7 = datetime.combine(today - timedelta(days=6), time.min)
today_order = db.query(DineOrder).filter(DineOrder.order_time >= start_of_day).count()
today_success = (
db.query(DineOrder)
.filter(DineOrder.transac_status == "success", DineOrder.order_time >= start_of_day)
.all()
)
today_sales = Decimal("0")
for order in today_success:
if order.sett_amount is not None:
today_sales += order.sett_amount
dish_count = db.query(Dish).filter(Dish.on_sale == 1).count()
user_count = db.query(User).count()
pending = db.query(DineOrder).filter(DineOrder.transac_status == "unsettled").count()
sales_rows = db.execute(
text(
"""
SELECT DATE_FORMAT(order_time, '%Y-%m-%d') AS name, IFNULL(SUM(sett_amount), 0) AS value
FROM t_order
WHERE transac_status = 'success' AND order_time >= :start
GROUP BY DATE_FORMAT(order_time, '%Y-%m-%d')
ORDER BY name
"""
),
{"start": start7},
).mappings().all()
category_rows = db.execute(
text(
"""
SELECT c.name AS name, IFNULL(SUM(d.total_price), 0) AS value
FROM t_order_detail d
JOIN t_order o ON o.id = d.order_id
JOIN t_dish dish ON dish.id = d.dish_id
JOIN t_category c ON c.id = dish.category_id
WHERE o.transac_status = 'success'
GROUP BY c.id, c.name
ORDER BY value DESC
"""
)
).mappings().all()
top_rows = db.execute(
text(
"""
SELECT d.name AS name, IFNULL(SUM(d.quantity), 0) AS value
FROM t_order_detail d
JOIN t_order o ON o.id = d.order_id
WHERE o.transac_status = 'success'
GROUP BY d.name
ORDER BY value DESC
LIMIT 10
"""
)
).mappings().all()
status_row = db.execute(
text(
"""
SELECT
SUM(CASE WHEN transac_status = 'unsettled' AND order_receiving = 'mis_orders' THEN 1 ELSE 0 END) AS waitReceive,
SUM(CASE WHEN transac_status = 'unsettled' AND order_receiving = 'rec_order' THEN 1 ELSE 0 END) AS waitPay,
SUM(CASE WHEN transac_status = 'success' THEN 1 ELSE 0 END) AS finished
FROM t_order
"""
)
).mappings().first()
return {
"cards": {
"todayOrder": today_order or 0,
"todaySales": float(today_sales),
"dishCount": dish_count or 0,
"userCount": user_count or 0,
"pendingOrder": pending or 0,
},
"sales": self._fill_days(start7.date(), today, sales_rows),
"category": self._pairs(category_rows),
"topDishes": self._pairs(top_rows),
"status": self._status(status_row),
}
def _fill_days(self, start: date, end: date, raw) -> list[dict]:
"""没有订单的日期补 0,方便折线图连续。"""
index = {}
for row in raw or []:
index[str(row["name"])] = float(row["value"] or 0)
result = []
day = start
while day <= end:
name = day.strftime("%Y-%m-%d")
result.append({"name": name, "value": index.get(name, 0)})
day += timedelta(days=1)
return result
def _pairs(self, raw) -> list[dict]:
"""转成图表的名称和数值。"""
return [{"name": row["name"], "value": float(row["value"] or 0)} for row in raw or []]
def _status(self, raw) -> list[dict]:
"""待接单、待结账、已结账。"""
raw = raw or {}
return [
{"name": "待接单", "value": int(raw.get("waitReceive") or 0)},
{"name": "待结账", "value": int(raw.get("waitPay") or 0)},
{"name": "已结账", "value": int(raw.get("finished") or 0)},
]
dashboard_service = DashboardService()
<template>
<div class="page">
<div class="page-head">
<div><h2>菜品类目</h2><p>类目下还有菜品时不能删除</p></div>
<el-button type="primary" @click="open()">新增类目</el-button>
</div>
<div class="panel">
<el-table class="full-table" :data="records">
<el-table-column prop="name" label="名称" min-width="160" />
<el-table-column prop="sortNum" label="排序" min-width="100" />
<el-table-column label="创建时间" min-width="180">
<template #default="{ row }">{{ formatDateTime(row.createTime) }}</template>
</el-table-column>
<el-table-column label="操作" min-width="160">
<template #default="{ row }">
<el-button size="small" @click="open(row)">编辑</el-button>
<el-button size="small" type="danger" @click="remove(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pager">
<el-pagination background layout="prev, pager, next, total" :total="total" :page-size="query.pageSize" v-model:current-page="query.pageNum" @current-change="load" />
</div>
</div>
<el-dialog v-model="visible" title="类目" width="420px">
<el-form label-width="70px">
<el-form-item label="名称"><el-input v-model="form.name" /></el-form-item>
<el-form-item label="排序"><el-input-number v-model="form.sortNum" :min="0" /></el-form-item>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="save">保存</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import http from '@/utils/http'
import { formatDateTime } from '@/utils/format'
const records = ref([])
const total = ref(0)
const visible = ref(false)
const query = reactive({ pageNum: 1, pageSize: 10, query: '' })
const form = reactive({ id: null, name: '', sortNum: 0 })
/** 分页加载类目 */
async function load() {
const res = await http.post('/admin/category/list', query)
records.value = res.data.records || []
total.value = res.data.total || 0
}
function open(row) {
Object.assign(form, row ? { ...row } : { id: null, name: '', sortNum: 0 })
visible.value = true
}
async function save() {
await http.post('/admin/category/save', form)
ElMessage.success('已保存')
visible.value = false
load()
}
async function remove(row) {
await ElMessageBox.confirm('确定删除该类目吗?', '提示', { type: 'warning' })
await http.get('/admin/category/delete', { params: { id: row.id } })
load()
}
onMounted(load)
</script>
更多推荐

所有评论(0)