1. 项目背景与核心需求
这个甜品线上点单配送系统源于我去年为本地一家甜品店做的实际项目。店主王姐经营着一家手工甜品店,随着外卖订单量从每天20单激增到100多单,原有的微信接单方式开始出现漏单、错单、配送混乱等问题。最严重的一次,因为手工记录出错,导致同一时段接了超过产能的订单,最后不得不退款道歉。
经过深入沟通,我们梳理出几个核心痛点:
- 订单高峰期人工记录容易出错
- 无法实时掌握库存变化
- 配送路线全靠司机经验
- 缺乏会员管理和营销工具
系统需要实现四大核心模块:
- 用户端:在线浏览菜单、下单支付
- 商家端:订单处理、库存管理
- 配送端:智能路线规划
- 后台管理:数据统计、会员运营
关键设计原则:操作界面必须足够简单,让50岁的王姐能独立使用;系统响应速度要快,高峰期不能卡顿;所有数据必须本地化存储,避免云服务中断影响营业。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术栈选型与架构设计
2.1 为什么选择Python
经过对比Java和PHP,最终选择Python主要基于:
- 开发效率:Django框架自带Admin后台,快速实现CRUD
- 生态丰富:有成熟的GUI库和地图API接口
- 学习成本:店员后续维护门槛低
- 实测数据:用Locust压测,单机Django在4核8G服务器上能稳定处理300+并发请求
2.2 整体架构
采用经典的三层架构:
code复制表示层:PyQt5 + QSS美化
业务层:Django REST Framework
数据层:MySQL + Redis缓存
特别增加了两个设计:
- 离线模式:使用SQLite做本地缓存,网络中断时仍可接单
- 双工通信:WebSocket实时推送订单状态变化
2.3 数据库设计要点
甜品行业特有的数据特点:
- 产品属性复杂(甜度、冰度等定制选项)
- 季节性商品频繁上下架
- 原料库存需要精确到克
最终设计的核心表结构:
python复制class Dessert(models.Model):
name = models.CharField(max_length=100)
base_price = models.DecimalField(max_digits=6, decimal_places=2)
# 特殊字段
is_seasonal = models.BooleanField(default=False)
sugar_level_choices = [(i, f"{i}%") for i in range(0, 110, 10)]
sugar_level = models.IntegerField(choices=sugar_level_choices)
class Ingredient(models.Model):
dessert = models.ForeignKey(Dessert, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
# 精确到0.1克
usage_per_serve = models.DecimalField(max_digits=6, decimal_places=1)
3. 关键功能实现细节
3.1 实时库存扣减方案
传统电商的库存扣减模式不适合甜品店场景:
- 同一原料用于多款商品(如奶油)
- 制作过程中可能有损耗
- 需要预留安全库存
最终实现的库存管理逻辑:
python复制def place_order(order):
with transaction.atomic():
# 第一步:预占库存
for item in order.items.all():
ingredients = item.dessert.ingredient_set.all()
for ing in ingredients:
stock = Stock.objects.select_for_update().get(
ingredient=ing.name)
if stock.quantity < ing.usage_per_serve * item.quantity:
raise InsufficientStockError
stock.reserved += ing.usage_per_serve * item.quantity
stock.save()
# 第二步:支付成功后实际扣减
if order.payment_status == 'PAID':
for item in order.items.all():
ingredients = item.dessert.ingredient_set.all()
for ing in ingredients:
stock = Stock.objects.get(ingredient=ing.name)
stock.quantity -= ing.usage_per_serve * item.quantity
stock.reserved -= ing.usage_per_serve * item.quantity
stock.save()
3.2 配送路径优化算法
结合高德地图API实现的智能配送:
- 基于历史订单数据建立热力图
- 实时交通状况权重调整
- 电动车续航约束条件
核心算法片段:
python复制def optimize_route(orders):
locations = [o.address for o in orders]
# 使用Dijkstra算法变种
route = []
current = store_location
while locations:
nearest = min(locations,
key=lambda x: get_driving_time(current, x))
route.append(nearest)
current = nearest
locations.remove(nearest)
return route
4. GUI设计实战技巧
4.1 PyQt5性能优化
在低配收银机上的优化经验:
- 避免使用QML,纯代码编写UI
- 使用QSS代替图片资源
- 列表控件采用分页加载
关键代码示例:
python复制# 分页加载商品列表
class ProductList(QListWidget):
def __init__(self):
super().__init__()
self.current_page = 0
self.setVerticalScrollMode(QListWidget.ScrollPerPixel)
self.verticalScrollBar().valueChanged.connect(self.load_more)
def load_more(self, value):
if value == self.verticalScrollBar().maximum():
self.current_page += 1
# 异步加载下一页
QTimer.singleShot(0, self._fetch_page)
def _fetch_page(self):
products = get_products(page=self.current_page)
for p in products:
item = QListWidgetItem(p.name)
self.addItem(item)
4.2 高交互性设计
针对触摸屏优化的操作流:
- 增大点击区域(至少50×50像素)
- 添加触觉反馈(使用QPropertyAnimation)
- 关键操作二次确认
python复制# 触觉反馈实现
def animate_button(btn):
anim = QPropertyAnimation(btn, b"geometry")
anim.setDuration(100)
anim.setStartValue(btn.geometry())
anim.setEndValue(btn.geometry().adjusted(-2, -2, 2, 2))
anim.setEasingCurve(QEasingCurve.OutElastic)
anim.start()
5. 部署与运维实战
5.1 Windows服务器部署
甜品店常用老旧Windows电脑做服务器,需特殊处理:
- 使用NSSM将服务注册为系统服务
- 配置开机自启动
- 自动清理日志脚本
batch复制:: 启动服务的bat脚本
@echo off
set PYTHONPATH=C:\app\venv\Scripts\python.exe
start /B nssm start DessertOrderSystem
5.2 数据备份方案
设计的双重备份机制:
- 实时备份:MySQL主从复制到备用机
- 每日全量:自动导出SQL并上传到USB硬盘
- 应急恢复:保留最近7天的SQL文件
python复制# 备份脚本核心逻辑
def backup_database():
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
filename = f"backup_{timestamp}.sql"
cmd = f"mysqldump -u{user} -p{password} {dbname} > {backup_dir}/{filename}"
subprocess.run(cmd, shell=True)
# 自动删除旧备份
backups = sorted(Path(backup_dir).glob("backup_*.sql"))
if len(backups) > 7:
for old in backups[:-7]:
old.unlink()
6. 实际运营中的调优
系统上线后持续优化的几个关键点:
- 订单打印速度优化:
- 从直接调用Windows API改为使用ESC/POS指令集
- 打印耗时从3秒降至0.8秒
- 高峰期响应优化:
- 引入Redis缓存热门商品数据
- 数据库查询量减少60%
- 智能预警系统:
python复制# 库存预警规则
def check_inventory():
for ingredient in Ingredient.objects.all():
usage_rate = get_weekly_usage(ingredient)
current_stock = ingredient.stock.quantity
if current_stock < usage_rate * 2: # 低于两周用量
send_alert(f"{ingredient.name}库存不足,剩余:{current_stock}g")
这个项目给我最深的体会是:好的系统不是功能越多越好,而是要在稳定可靠的基础上,用最简单的操作解决最痛的问题。现在王姐的店已经用这个系统处理了超过2万笔订单,最忙的时候一天能接200多单,再没出现过漏单错单的情况。
