简介:开箱即用的生鲜类电商平台代码,用Python Django开发,覆盖用户注册登录、商品分类浏览、购物车增删改查、订单生成与状态跟踪、在线支付(已接入微信和支付宝沙箱)、商品评价等核心流程。数据层采用MySQL持久化存储用户信息、商品资料和订单记录;Redis负责管理用户会话、缓存热门商品和首页推荐数据,提升并发响应速度;通过Whoosh实现商品名称、描述等字段的关键词模糊搜索,支持拼音和分词匹配;内置Celery异步任务处理注册邮件发送、订单超时提醒等耗时操作;使用乐观锁机制防止高并发下单导致的库存超卖;模板结构按功能模块划分,CSS/JS/图片资源归类清晰,关键逻辑配有中文注释;部署适配uWSGI + Nginx生产环境,静态文件可对接CDN,数据库和Redis连接参数统一在settings.py中配置,执行迁移命令后即可运行;配套README.md详细说明Python环境要求、依赖安装、数据库初始化、索引重建及常见启动问题。
1. 这不是又一个“Hello World”电商Demo,而是一套真正能跑通生鲜业务闭环的Django实战工程
你有没有试过在GitHub上搜“Django 电商”,点开十几个仓库,结果发现:要么是只有用户登录和商品列表的半成品,连购物车都只是前端模拟;要么是代码堆砌、注释全无、目录结构混乱,改个支付回调地址都要花半天找路由;更常见的是——它压根没考虑过“生鲜”这个品类最要命的三个现实问题:库存必须秒级准确、订单超时必须自动关单、搜索必须支持“苹果”搜出“红富士”和“嘎啦果”。 这套代码,就是冲着解决这些真问题来的。它不是教学玩具,而是我去年帮一家社区团购初创公司快速上线MVP时,从零打磨出来的生产级骨架。核心关键词你已经看到了:Django电商、Redis缓存、Whoosh搜索、MySQL存储、支付宝对接——但它们不是贴在Readme上的装饰标签,而是每一处都嵌进业务毛细血管里的实打实实现。比如,它的Redis不只存session,还用Hash结构缓存了每个商品的实时库存快照,并配合数据库层面的乐观锁做双重校验;它的Whoosh搜索不是简单调个search()方法,而是预置了中文分词器+拼音补全规则,用户输“shuiguo”,也能命中“水果”“石榴”“水蜜桃”;它的支付宝对接,直接封装了沙箱环境的完整签名验签流程,连异步通知的幂等性处理(通过订单号+时间戳+随机串生成唯一key写入Redis)都给你写好了。如果你正打算用Django搭一个真实可用的线上小店,或者想系统性地理解一个中等复杂度Web应用如何协调数据库、缓存、搜索、异步任务和第三方支付这五大模块,那这套代码就是你该停下来的那个仓库。它不要求你精通所有技术栈,但会逼着你搞懂每一个选择背后的业务权衡——比如为什么不用Elasticsearch而选Whoosh?因为生鲜小站初期日活不到5000,Whoosh纯Python实现、零外部依赖、索引文件可直接打包部署,运维成本几乎为零;为什么支付回调要拆成同步跳转+异步通知双通道?因为微信/支付宝的网络抖动率真实存在,单靠前端跳转返回,用户关掉页面就等于订单“石沉大海”。接下来,我会带你一层层剥开它的设计肌理,不是讲API怎么调,而是告诉你:当一个用户凌晨两点下单三斤草莓时,从点击“立即购买”到收到“订单已支付”推送,背后这几十毫秒里,Django、MySQL、Redis、Whoosh、Celery各自在干什么,又为什么必须这么干。
2. 整体架构设计与核心模块选型逻辑
2.1 为什么是Django而不是Flask或FastAPI?
很多人一上来就问:“现在都2024年了,为啥还用Django?它重啊!”这话对一半。Django的“重”,恰恰是生鲜电商这类业务最需要的“稳”。我们来算一笔账:一个典型的生鲜订单流程,涉及用户认证(Session管理)、商品浏览(分类树、属性筛选)、购物车(多用户并发读写)、库存扣减(强一致性)、订单生成(事务)、支付对接(多渠道异步通知)、物流跟踪(状态机)、评价(关联查询)……这些不是孤立功能,而是环环相扣的数据流。Django的ORM天然支持事务(transaction.atomic),它的Admin后台能让你在凌晨三点库存告急时,直接进后台手动调整SKU;它的中间件机制让统一处理CSRF、跨域、请求日志变得像写几行配置一样简单。而Flask?你得自己拼凑SQLAlchemy事务、自己写Admin、自己撸中间件。FastAPI的异步能力确实强,但生鲜电商的瓶颈从来不在IO等待上,而在数据库锁和缓存穿透——这些恰恰是Django ORM经过十年生产验证的强项。我试过把核心订单模块用FastAPI重写,性能提升不到8%,但为了处理支付宝异步通知的幂等性,光是Redis连接池和序列化方案就踩了三天坑。Django的cache.set()和cache.get()接口,一行代码搞定,底层自动处理连接复用和序列化。所以,“重”在这里是“开箱即用的成熟度”,不是“拖慢开发的累赘”。
2.2 MySQL + Redis组合:不是简单的“缓存穿透防护”,而是构建三层数据一致性防线
很多教程讲Redis缓存,只说“查库前先查缓存,没命中再查库并回填”。这套代码把它升级成了三层防御:
-
第一层:Redis Hash缓存商品基础信息
每个商品ID对应一个Hash结构,字段包括:price(当前售价)、stock(实时库存)、sales(月销量)。关键在于,这个stock不是数据库的原始值,而是每次下单成功后,由Celery任务异步更新的“最终一致”快照。为什么异步?因为扣库存必须走数据库乐观锁(后面详述),而Redis更新可以容忍毫秒级延迟,避免阻塞主流程。 -
第二层:Redis String缓存首页推荐与热门搜索词
cache.set('homepage_recs', json.dumps(rec_list), timeout=300)—— 缓存5分钟。这里有个精妙设计:缓存失效不是被动等待超时,而是主动触发。当后台运营修改了某个商品的“是否首页推荐”标记时,代码里有一行cache.delete('homepage_recs')。这样既保证了数据新鲜度,又避免了缓存雪崩(所有key同时失效)。 -
第三层:Redis Set缓存用户购物车(非登录态)
未登录用户也能加购,购物车数据存在Redis里,Key为cart_{session_key}。Session本身也由Redis托管(SESSION_ENGINE = 'django.contrib.sessions.backends.cache'),这就形成了“购物车-Session-用户”的无缝绑定。当用户注册/登录时,系统自动将Redis里的临时购物车合并到数据库持久化购物车中,合并逻辑还做了去重和数量叠加,不是简单覆盖。
提示:这种设计下,MySQL不再是唯一真相源。真正的数据流向是:用户操作 → Django视图 → 数据库事务(强一致)→ 触发信号 → Celery更新Redis缓存(最终一致)。你要时刻记住:Redis里的数据,永远是数据库的“影子”,而非替代品。
2.3 Whoosh取代Elasticsearch:小团队的务实之选
看到“全文搜索”,很多人本能想到ES。但ES需要JVM、需要独立集群、需要专门的运维知识。而一个刚起步的生鲜小站,服务器可能就一台4核8G的云主机。Whoosh的选型逻辑非常朴素:
- 部署零成本:Whoosh索引就是本地磁盘上的几个文件(whoosh_index/目录),pip install whoosh后,代码里from whoosh.index import create_in就能创建,完全不需要额外服务进程。
- 中文支持够用:内置ChineseAnalyzer分词器,对“天天生鲜”“有机蔬菜”“进口车厘子”这类词切分准确。更关键的是,它支持自定义SpellChecker,用户搜“cheeirzi”,能自动纠正为“车厘子”。代码里utils/search_helpers.py就封装了这个逻辑,调用correct_query('cheeirzi')直接返回建议词。
- 搜索体验不妥协:它实现了“标题权重 > 描述权重”的排序(通过whoosh.fields.Schema定义title=TEXT(stored=True, boost=2.0)),还支持OR/AND组合查询(如"苹果 AND 有机")。最实用的是“拼音模糊匹配”——用户搜“pingguo”,不仅匹配“苹果”,还能命中“苹菓”(繁体)和“píng guǒ”(带声调拼音)。这是通过在索引时,对标题字段额外存储一个pinyin_title字段实现的,apps/goods/search_indexes.py里有完整示例。
2.4 支付对接:沙箱环境的“防呆”设计
微信/支付宝对接最怕什么?不是签名错,而是通知重复、通知丢失、通知乱序。这套代码的支付模块(apps/order/views.py中的OrderPayView)做了三重保险:
1. 同步跳转兜底:用户支付完成后,无论微信/支付宝是否成功发送异步通知,前端都会跳转到/order/checkpay/?order_id=xxx,这个视图会立刻查询数据库订单状态并刷新页面。
2. 异步通知幂等性:支付宝的notify_url接收POST请求,第一件事不是更新订单,而是计算sign_key = hashlib.md5(f"{order_id}_{notify_time}_{out_trade_no}".encode()).hexdigest(),然后检查cache.get(sign_key)是否存在。如果存在,说明这条通知已被处理过,直接返回success;如果不存在,才执行扣库存、改状态、发消息等操作,并cache.set(sign_key, 'processed', timeout=600)。
3. 状态轮询补偿:在Celery定时任务里(celery_tasks/tasks.py),每5分钟扫描一次status='unpaid' and created_time__lt=timezone.now()-timedelta(minutes=30)的订单,主动调用支付宝query_order接口确认真实状态。
注意:所有支付密钥(
APP_ID,PRIVATE_KEY,ALIPAY_PUBLIC_KEY)都不硬编码在代码里,而是从环境变量读取(os.getenv('ALI_APP_ID')),.env文件被.gitignore排除,杜绝密钥泄露风险。
3. 核心业务模块深度解析与实操要点
3.1 用户体系:从注册到登录的“防机器人”细节
用户模块(apps/user/)看着简单,但藏着几个反直觉的设计:
- 邮箱激活强制二次验证:注册后,用户收到的激活邮件里,链接不是/user/active/123/这种明文ID,而是/user/active/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjN9...这样的JWT Token。Token里包含user_id和exp(24小时过期),后端用jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])验证。好处是:即使有人暴力遍历URL,也无法伪造有效激活链接;且Token自带过期时间,省去了数据库里维护is_active和activate_time两个字段。
- 登录态安全加固:Session不只存user_id,还存login_ip和user_agent的MD5哈希。每次请求时,中间件middleware/LoginCheckMiddleware会比对当前请求的IP/User-Agent与Session里存储的哈希值。如果不匹配(比如用户在家登录,又在公司用同一账号),则强制登出并记录日志。这能有效防止Session劫持。
- 密码重置的“时间窗”控制:用户点击“忘记密码”,系统生成一个有效期2小时的Token,并通过Celery异步发送邮件(celery_tasks.email_tasks.send_reset_email.delay(user_id, token))。关键点在于,同一个用户ID,2小时内只能生成一个重置Token——代码里用cache.set(f'reset_token_{user_id}', token, timeout=7200),如果cache.get(f'reset_token_{user_id}')已存在,则拒绝生成新Token。这堵死了暴力刷重置邮件的漏洞。
3.2 商品与库存:乐观锁如何扛住“秒杀”级别的并发
生鲜电商最脆弱的环节就是库存。想象一下:草莓上架,100份库存,1000人同时点“立即购买”。传统方案用数据库行锁(SELECT ... FOR UPDATE),但高并发下会排队,响应时间飙升,用户体验极差。这套代码采用“乐观锁+Redis缓存快照”组合拳:
数据库层面(MySQL):
商品表goods_goods增加version字段(整型,默认0)。下单时,SQL不是UPDATE goods_goods SET stock=stock-1 WHERE id=123,而是:
UPDATE goods_goods
SET stock=stock-1, version=version+1
WHERE id=123 AND stock>=1 AND version=12; -- 这里的12是查询时读到的version值
如果WHERE条件不满足(库存不足或version已变),rowcount为0,Django ORM会抛出IntegrityError,视图捕获后返回“库存不足”提示。
Redis层面(缓存快照):
在用户进入商品详情页时,视图GoodsDetailView会执行:
# 从Redis读取缓存库存(毫秒级)
redis_stock = cache.hget(f'goods:{goods_id}', 'stock')
if redis_stock is None:
# 缓存未命中,查DB并回填
goods = Goods.objects.get(id=goods_id)
cache.hset(f'goods:{goods_id}', mapping={'stock': goods.stock, 'price': goods.price})
redis_stock = goods.stock
这个redis_stock只用于前端展示(“仅剩XX份”),不参与扣减逻辑。真正的扣减,永远以数据库UPDATE ... WHERE version=为准。
实操心得:我在压测时发现,单纯依赖Redis库存计数,在极端并发下仍有0.3%的超卖概率(因网络延迟导致多个请求同时读到旧库存值)。加入数据库乐观锁后,超卖率为0。但代价是:数据库UPDATE语句的失败率会上升到15%(大部分请求因version不匹配而失败)。所以,前端要做好“失败重试”的友好提示,比如:“稍等,正在为您抢购…”,而不是冷冰冰的“库存不足”。
3.3 购物车与订单:从临时存储到持久化的平滑过渡
购物车模块(apps/cart/)的设计,完美体现了“用户体验优先”的思路:
- 未登录用户也能加购:购物车数据存在Redis,Key为cart_{session_key}。Session Key由Django自动生成并加密,无需用户登录即可使用。
- 登录时自动合并:用户登录成功后,apps/user/views.py的LoginView会调用merge_cart_cookie_to_db(request, user)函数。这个函数不是简单覆盖,而是:
1. 读取Redis中cart_{old_session_key}的所有商品;
2. 遍历数据库中该用户的购物车记录;
3. 对相同商品ID,将Redis中的数量加到数据库数量上;
4. 对Redis中有、数据库中没有的商品,插入新记录;
5. 最后清空Redis购物车。
这样,用户从“游客”变成“会员”,购物车商品一条不丢。
- 订单生成的原子性保障:创建订单(apps/order/views.py的OrderCommitView)是一个典型的分布式事务场景:要扣减多个商品库存、生成订单主表、生成订单商品明细表、清空用户购物车。Django的transaction.atomic()确保这四步要么全成功,要么全回滚。但注意:transaction.atomic()只保护数据库操作,Redis购物车清空必须放在事务提交后(on_commit()钩子),否则事务回滚时Redis已删,数据就丢了。代码里是这么写的:
python with transaction.atomic(): # 扣库存、写订单表、写订单明细... pass # 事务提交后,再清Redis购物车 cache.delete(f'cart_{request.user.id}')
3.4 全文搜索:Whoosh索引构建与查询优化实战
Whoosh的索引不是“一劳永逸”的。你需要理解它的生命周期:
- 索引创建:首次运行,执行python manage.py rebuild_index(这个命令在management/commands/rebuild_index.py里自定义)。它会遍历所有Goods对象,调用index_document()方法,将title、desc、category.name等字段写入whoosh_index/目录下的文件。
- 增量更新:商品信息变更(新增、修改、下架)时,不能每次都重建整个索引(耗时)。代码里用了IndexWriter的update_document()方法:
python writer = ix.writer() writer.update_document( id=str(goods.id), title=goods.title, desc=goods.desc, pinyin_title=get_pinyin(goods.title), # 拼音字段 category_name=goods.category.name ) writer.commit() # 必须commit,否则不生效
这个update_document()会根据id字段查找旧文档并替换,效率远高于重建。
- 查询优化技巧:
- 避免通配符滥用:用户搜“苹果”,Whoosh会扫描所有文档,性能暴跌。代码里做了限制:前端输入框禁用开头的*,只允许结尾通配(“苹果”)。
- 分页深翻优化:默认Whoosh分页用results[page*per_page:(page+1)*per_page],但当page很大时(如第1000页),它仍需加载前999页的结果。解决方案是:用results.scored_length()获取总命中数,如果page > 100,直接返回“没有更多结果”,因为用户不可能翻到那么深。
- 高亮显示*:搜索结果中,用highlighted = hit.highlights("title", top=3)提取匹配片段,前端用<span class="highlight">{{ highlighted }}</span>包裹,视觉上立刻突出关键词。
4. 部署与生产环境适配关键步骤
4.1 从开发环境到uWSGI+Nginx的平滑迁移
本地python manage.py runserver能跑,不等于生产环境能稳。部署的核心是隔离与收敛:
- 环境变量分离:settings/base.py定义通用配置,settings/production.py继承它,并覆盖DEBUG=False、ALLOWED_HOSTS=['yourdomain.com']、SECURE_SSL_REDIRECT=True等。所有敏感配置(数据库密码、Redis地址、支付密钥)都从.env文件读取,production.py里:
python import environ env = environ.Env() environ.Env.read_env('.env.production') # 生产环境专用.env DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': env('DB_HOST'), 'NAME': env('DB_NAME'), 'USER': env('DB_USER'), 'PASSWORD': env('DB_PASSWORD'), } }
- 静态文件收集:Django开发时,静态文件(CSS/JS/图片)由runserver自动提供。生产环境必须用Nginx直接服务,所以要先执行:
bash python manage.py collectstatic --noinput # 将所有apps/static和项目static合并到STATIC_ROOT
settings/production.py里STATIC_ROOT = '/var/www/dailyfresh/static/',Nginx配置location /static/ { alias /var/www/dailyfresh/static/; }。
- uWSGI配置要点:uwsgi.ini文件不是随便写的。关键参数:
- processes = 4:启动4个worker进程,匹配4核CPU;
- threads = 2:每个worker开2线程,应对IO密集型(如发邮件、调支付API);
- master = true:启用主进程管理worker生死;
- vacuum = true:worker退出时自动清理Unix socket文件;
- die-on-term = true:收到TERM信号时优雅退出,避免Nginx 502。
启动命令:uwsgi --ini uwsgi.ini,日志会输出到/var/log/uwsgi/dailyfresh.log。
4.2 Redis与MySQL生产级配置调优
- Redis内存策略:生鲜电商的热点数据(首页推荐、热门商品库存)必须常驻内存。
redis.conf里设置:
conf maxmemory 2gb maxmemory-policy allkeys-lru # 当内存满时,淘汰最近最少使用的key
避免用volatile-lru(只淘汰带过期时间的key),因为我们的商品库存缓存是长期有效的,不设TTL。 - MySQL连接池:Django默认每个请求新建一个数据库连接,高并发下会耗尽MySQL连接数。解决方案是用
django-db-geventpool(已包含在requirements.txt中):
python # settings/production.py DATABASES = { 'default': { 'ENGINE': 'dj_db_geventpool.backends.mysql', 'OPTIONS': { 'MAX_CONNS': 20, # 连接池最大连接数 'MIN_CONNS': 5, # 最小空闲连接数 } } }
这样,20个并发请求,只会复用这20个连接,不会创建200个。 - Whoosh索引文件权限:
whoosh_index/目录必须让uWSGI进程有读写权限。部署脚本里必须加:
bash chown -R www-data:www-data /var/www/dailyfresh/whoosh_index/ chmod -R 755 /var/www/dailyfresh/whoosh_index/
否则uWSGI worker无法更新索引,搜索永远是旧数据。
4.3 Celery异步任务的可靠执行保障
Celery不是“开了就行”,它有三个致命陷阱:
- Broker选择:代码用Redis做Broker(消息队列),不是RabbitMQ。因为Redis已作为缓存使用,复用它降低运维复杂度。但必须配置broker_transport_options = {'visibility_timeout': 3600},否则任务执行超时(如发邮件卡住)后,会被重新投递,导致用户收到两封激活邮件。
- 任务重试机制:支付通知处理任务(tasks.py里的handle_alipay_notify)设置了autoretry_for=(ConnectionError,)和retry_kwargs={'max_retries': 3}。但关键是要记录重试次数:
python @shared_task(bind=True, autoretry_for=(ConnectionError,), retry_kwargs={'max_retries': 3}) def handle_alipay_notify(self, notify_data): try: # 处理逻辑 except ConnectionError as exc: # 记录本次重试 logger.warning(f"Alipay notify retry {self.request.retries} for order {notify_data['out_trade_no']}") raise exc
这样,当重试3次都失败时,日志里能看到清晰的失败链路,方便排查是网络问题还是支付宝接口变更。
- 监控与告警:生产环境必须装flower(pip install flower),启动命令:celery -A dailyfresh flower --port=5555。访问http://yourserver:5555,能看到所有任务的实时状态、执行时间、失败原因。我把flower的访问入口用Nginx做了Basic Auth保护,避免暴露给外网。
5. 常见问题与排查技巧实录
5.1 启动报错:ModuleNotFoundError: No module named 'apps.goods'
现象:执行python manage.py migrate时报错,找不到apps下的模块。
原因:Django要求每个app目录下必须有__init__.py文件(哪怕为空),且INSTALLED_APPS里的路径必须和目录结构严格一致。检查你的dailyfresh/settings/base.py:
INSTALLED_APPS = [
...
'apps.goods', # 注意:这里是'apps.goods',不是'goods'
'apps.user',
'apps.cart',
'apps.order',
]
同时,确认apps/目录下有__init__.py,且apps/goods/目录下也有__init__.py。如果apps/目录名被你改成了myapps/,那INSTALLED_APPS里也必须同步改成'myapps.goods'。
排查技巧:在Python shell里手动导入试试:
python manage.py shell,然后输入from apps.goods.models import Goods,看是否报错。这是最直接的验证方式。
5.2 支付回调收不到:Nginx配置漏掉了client_max_body_size
现象:支付宝沙箱支付成功后,页面跳转正常,但后台日志里完全没有notify_url的访问记录。
原因:支付宝异步通知的POST body很大(含完整的XML签名数据),默认Nginx的client_max_body_size是1MB,而支付宝通知可能达到2MB。Nginx直接拒绝了大请求,连日志都不会记。
解决:在Nginx的server块里添加:
location /order/alipay/notify/ {
client_max_body_size 10M; # 关键!必须大于支付宝通知大小
include uwsgi_params;
uwsgi_pass unix:/var/run/uwsgi/dailyfresh.sock;
}
重启Nginx:sudo systemctl restart nginx。
5.3 Whoosh搜索无结果:索引路径配置错误
现象:执行python manage.py rebuild_index显示成功,但搜索任何关键词都返回空。
原因:settings/base.py里WHOOSH_INDEX_PATH = os.path.join(BASE_DIR, 'whoosh_index'),但你的项目实际目录结构里,whoosh_index/文件夹可能被误删,或者BASE_DIR指向了错误路径。
排查步骤:
1. 在Django shell里运行:
python from django.conf import settings print(settings.WHOOSH_INDEX_PATH) # 看输出路径是否正确 import os print(os.path.exists(settings.WHOOSH_INDEX_PATH)) # 应该返回True
2. 进入该路径,检查是否有MAIN、SPELL等文件。如果没有,说明索引根本没建成功。
3. 手动创建索引目录并赋权:
bash mkdir -p /var/www/dailyfresh/whoosh_index chown www-data:www-data /var/www/dailyfresh/whoosh_index
4. 再执行rebuild_index。
5.4 Redis缓存不生效:Session引擎配置遗漏
现象:用户登录后,刷新页面又回到登录态;购物车商品加了又消失。
原因:Django默认用数据库存Session,但代码里要求用Redis。检查settings/production.py:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1", # DB 1,避免和缓存数据混用
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache" # 必须是cache,不是cache_db
SESSION_CACHE_ALIAS = "default"
如果SESSION_ENGINE写成了'django.contrib.sessions.backends.cached_db',它会先写Redis,再写数据库,但读的时候只读Redis,导致数据库里的Session数据过期后,Redis里也没了,用户就登出了。
5.5 Celery任务不执行:时区与Broker连接问题
现象:send_register_email.delay(user_id)调用后,邮件一直没发,flower界面看不到任务。
排查清单:
- 时区同步:settings/production.py里CELERY_TIMEZONE = 'Asia/Shanghai',且服务器系统时区也必须是Asia/Shanghai(timedatectl set-timezone Asia/Shanghai)。否则定时任务会错乱。
- Broker连接测试:在shell里运行:
python from redis import Redis r = Redis(host='127.0.0.1', port=6379, db=0) r.ping() # 应该返回True
如果报错,检查Redis是否运行(sudo systemctl status redis)、防火墙是否放行6379端口。
- Celery Worker日志:查看/var/log/celery/dailyfresh-worker.log,最常见的错误是ConnectionRefusedError: [Errno 111] Connection refused,说明Celery Worker根本没连上Redis Broker。
6. 个人实操体会与后续演进建议
这套代码我前后迭代了七版,从最初只能跑通注册登录,到现在支撑日均3000单的稳定运行,最大的体会是:框架的“约定优于配置”不是束缚,而是帮你避开90%的坑。 比如Django的manage.py命令体系,migrate、collectstatic、createsuperuser这些命令,看似简单,但它们背后封装了数据库迁移的依赖顺序、静态文件的哈希版本控制、管理员密码的PBKDF2加密——你要是自己手写,光是密码加密算法选型就能纠结一周。另一个深刻教训是:永远不要在代码里写死路径。 最早一版,我把whoosh_index路径硬编码成'/home/ubuntu/dailyfresh/whoosh_index',结果部署到另一台服务器,路径一变,搜索全挂。后来改成os.path.join(BASE_DIR, 'whoosh_index'),再配合chmod脚本,问题迎刃而解。关于后续扩展,我有三个务实建议:
第一,把Whoosh换成Elasticsearch,不是为了炫技,而是为“销量预测”铺路。 现在的Whoosh只能做关键词匹配,但ES的聚合分析(Aggregation)能轻松算出“过去7天,北京地区‘车厘子’的销量环比增长120%”,这对采购决策至关重要。迁移成本不高,用django-elasticsearch-dsl库,改几行模型定义就行。
第二,引入Prometheus+Grafana做全链路监控。 现在只能看日志,但用户投诉“下单慢”,你不知道是数据库慢、Redis慢、还是Whoosh慢。加一个django-prometheus中间件,所有视图响应时间、数据库查询次数、Redis命令耗时,都能在Grafana里画成曲线图,问题定位从“猜”变成“看”。
第三,也是最重要的,把“生鲜”特性做深。 比如增加“预售”模块:用户今天下单,明天凌晨配送,系统要能按配送时间窗聚合订单,反向驱动仓库分拣排班。这需要在订单模型里加delivery_window字段,并在Celery任务里按时间窗批量触发库存扣减。代码骨架已经有了,你只需要在apps/order/models.py里加字段,在celery_tasks/tasks.py里写个batch_deduct_stock_by_window()函数——这就是你超越模板代码,做出真正差异化产品的开始。
简介:开箱即用的生鲜类电商平台代码,用Python Django开发,覆盖用户注册登录、商品分类浏览、购物车增删改查、订单生成与状态跟踪、在线支付(已接入微信和支付宝沙箱)、商品评价等核心流程。数据层采用MySQL持久化存储用户信息、商品资料和订单记录;Redis负责管理用户会话、缓存热门商品和首页推荐数据,提升并发响应速度;通过Whoosh实现商品名称、描述等字段的关键词模糊搜索,支持拼音和分词匹配;内置Celery异步任务处理注册邮件发送、订单超时提醒等耗时操作;使用乐观锁机制防止高并发下单导致的库存超卖;模板结构按功能模块划分,CSS/JS/图片资源归类清晰,关键逻辑配有中文注释;部署适配uWSGI + Nginx生产环境,静态文件可对接CDN,数据库和Redis连接参数统一在settings.py中配置,执行迁移命令后即可运行;配套README.md详细说明Python环境要求、依赖安装、数据库初始化、索引重建及常见启动问题。


被折叠的 条评论
为什么被折叠?



