
简介一套基于Python的汉服商城管理系统完整项目实例面向具备Python与Web开发基础、1~3年经验的中初级开发者和计算机专业学生也适合对电商系统、文化类数字化平台开发感兴趣的读者。资源解决传统服饰电商在商品管理、订单处理、用户权限控制、智能推荐与数据分析等环节的数字化与自动化问题。资源为1个docx文档压缩包仅82KB文件虽小但内容系统文档按项目背景、需求分析、项目挑战及解决方案、系统架构、功能模块、数据库设计、API接口规范、前后端代码实现、运行调试等章节组织目录清晰可直接用作毕业设计、课程设计或中小型电商项目开发的起始模板。目前已有63人学习浏览。读者可从中掌握Flask/Django框架、MySQL数据库、协同过滤推荐算法、RBAC权限模型、RESTful API设计等关键技术并借鉴订单状态自动流转、高并发处理、跨平台集成、数据可视化等工程化实现思路也可参考其汉服文化数字化管理与智能运营的实践对文化类电商平台开发与运营具有实操指导价值。1. 把汉服商城管理系统拆成可落地的四层推荐、订单、GUI 与数据很多人在数据库课程设计或毕业设计里选“商城管理系统”最后交上去的往往是一个能增删改查的壳商品表、订单表、登录窗口点击按钮弹个提示框演示结束。但“汉服商城”这个标题里真正值钱的部分是商品智能推荐和订单全流程自动化这两件事。前者要求你有用户行为数据、有评分或相似度计算、有推荐结果的落库与展示后者要求订单从创建、支付回调、库存扣减到状态变更每一步都有明确的状态机和幂等保护而不只是往 order 表里 INSERT 一行。本文会按一条能实际跑通的主线展开先搭 Python SQLite Tkinter 的基础骨架再实现基于协同过滤的汉服推荐引擎然后设计订单状态机与自动流转逻辑最后把推荐结果按用户维度落库并给出 GUI 图表联动的完整方案。这不是某个虚构项目的源码讲解而是把这类系统最常见、最可靠的做法讲清楚。适合正在做课程设计、想补全项目深度或打算用 Python 做小型商城原型验证的开发者。2. 基于 Python 的商城骨架SQLite 数据模型与 Tkinter GUI 的联动设计2.1 数据模型设计商品、用户、评分和订单该建什么表做汉服商城管理系统第一步不是写界面而是把表结构定清楚。推荐系统要读用户行为订单自动化要读写库存和状态GUI 要按关键字和分类查询商品这些都依赖一张设计合理的关系表。常见做法是建 user、product、rating、orders、order_item、stock_log 六张表其中 stock_log 不是可选项目它是订单自动化里保证库存不超卖的关键。CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, created_at TEXT DEFAULT (datetime(now,localtime)) ); CREATE TABLE product ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, category TEXT NOT NULL, style TEXT, price REAL NOT NULL, stock INTEGER NOT NULL DEFAULT 0, description TEXT, image_path TEXT ); CREATE TABLE rating ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, product_id INTEGER NOT NULL, score INTEGER NOT NULL CHECK(score BETWEEN 1 AND 5), create_time TEXT DEFAULT (datetime(now,localtime)), UNIQUE(user_id, product_id) ); CREATE TABLE orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_no TEXT UNIQUE NOT NULL, user_id INTEGER NOT NULL, status TEXT NOT NULL DEFAULT pending_payment, total_price REAL NOT NULL, created_at TEXT DEFAULT (datetime(now,localtime)), paid_at TEXT, shipped_at TEXT ); CREATE TABLE order_item ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id INTEGER NOT NULL, product_id INTEGER NOT NULL, quantity INTEGER NOT NULL, price REAL NOT NULL ); CREATE TABLE stock_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_id INTEGER NOT NULL, change_qty INTEGER NOT NULL, order_no TEXT, create_time TEXT DEFAULT (datetime(now,localtime)) );这里把 rating 表加上了 UNIQUE(user_id, product_id)是为了保证同一个用户对同一件汉服只能打一次分推荐算法读评分时就不会出现重复计数。orders 和 order_item 拆成两张表是为了让一个订单可以包含多件不同形制的汉服比如同时下单一件齐胸襦裙和一条大袖衫。stock_log 表的作用写在名称里它记录每次库存变动的来源订单号订单取消或售后时靠这张表回溯。2.2 用 SQLite 还是 MySQL单机项目里怎么选热词里能看到 mysql 数据库 join 含义、数据库连接池、数据库同步工具这些检索词但汉服商城管理系统作为课程设计或原型项目SQLite 反而更合适。原因有两点第一SQLite 是零配置文件Python 标准库直接内嵌交给老师演示时不需要额外装 MySQL第二推荐引擎和订单状态机都是在应用层做的数据库只负责持久化SQLite 完全够用。如果后续要改成 MySQL需要改动的地方只有数据库连接层。整篇文章的 SQL 都按标准写法来避免使用 SQLite 特有的语法。唯一要注意的是事务控制SQLite 默认 autocommit 是开的执行 INSERT 和 UPDATE 时要手动 BEGIN 和 COMMIT或者在 Python 的 sqlite3 模块里用with conn:上下文管理来保证原子性。订单扣库存这种操作必须把“查库存、扣库存、写 stock_log、更新订单状态”放在同一个事务里。2.3 Tkinter 单文件 GUI 与业务逻辑分层很多 Python 商城项目的 GUI 都是一个窗口处理所有按钮回调代码写到两三千行后按钮之间的状态互相污染。放大到系统层面看Tkinter 本身没有 MVC 约束需要自己在代码里分层。常见的做法是分三层app.py 负责窗口布局和按钮绑定service.py 封装推荐引擎和订单服务db.py 提供数据库连接和增删改查封装。这样的结构让 GUI 侧只做事件分发和结果展示不会为了查一次库存去手写 SELECT。# db.py 示例数据库连接与基础查询封装 import sqlite3 DB_PATH hanfu_shop.db def get_conn(): conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row return conn def query_all(sql, params()): conn get_conn() rows conn.execute(sql, params).fetchall() conn.close() return rows def execute(sql, params()): conn get_conn() with conn: conn.execute(sql, params) return conn.total_changes这段代码里的with conn:是 sqlite3 模块的坑位点它保证 execute 成功时提交、异常时回滚。订单状态更新和库存扣减都必须走 execute不要自己写手动 commit否则中途出错会让库存扣了但订单还是待支付状态。GUI 侧通过 tkinter.ttk.Treeview 展示列表数据每次查询后先清空旧数据再插入新结果这是 Tkinter 表格刷新的标准姿势。3. 汉服商品智能推荐基于用户的协同过滤在本地数据集上的落地3.1 为什么选协同过滤而不是基于内容的推荐汉服商城有一个天然特点商品属性里“形制”“朝代风格”和“适用场合”之间没有硬性规则用户可能因为“齐胸襦裙适合拍写真”而购买也可能因为“马面裙日常穿”而下单。基于内容的推荐很难把这种隐式偏好建模好因为商品标签串的相似度并不能反映用户审美迁移。基于用户的协同过滤的核心假设是如果用户 A 和用户 B 对多件汉服的评分趋于一致那么 A 喜欢而 B 未评分的汉服B 大概率也喜欢。这个假设在小规模数据集上效果稳定而且实现成本低适合课程设计撑起推荐模块的深度。协同过滤的实现路径分成三步先构造用户-商品评分矩阵再计算用户间相似度最后取 Top-N 相似用户的评分加权。评分矩阵不需要真正存储为二维数组用 Python 的 dict 嵌套结构就能表达稀疏部分天然忽略。排序部分用 Python 内置的 sorted 按相似度降序取前 K 个不需要引入 pandas 或 numpy避免环境配置负担。3.2 用户相似度计算皮尔逊系数在稀疏评分矩阵上的实现计算用户相似度时直接对所有共同评分商品做皮尔逊相关系数。皮尔逊系数能消除用户评分尺度差异比如一个用户习惯打 3 分、另一个习惯打 5 分只要他们对同一件汉服的相对偏好一致相关系数就会偏正。这里给出一个不依赖第三方库的实现用 dict 存每个用户的评分数据。# recommend.py 基于用户的协同过滤核心计算 def pearson_score(user_ratings_a, user_ratings_b): common set(user_ratings_a.keys()) set(user_ratings_b.keys()) n len(common) if n 0: return 0.0 sum_a sum(user_ratings_a[k] for k in common) sum_b sum(user_ratings_b[k] for k in common) sum_a2 sum(user_ratings_a[k] ** 2 for k in common) sum_b2 sum(user_ratings_b[k] ** 2 for k in common) sum_ab sum(user_ratings_a[k] * user_ratings_b[k] for k in common) numerator sum_ab - (sum_a * sum_b) / n denominator ((sum_a2 - sum_a ** 2 / n) * (sum_b2 - sum_b ** 2 / n)) ** 0.5 if denominator 0: return 0.0 return numerator / denominator实现里有三个地方值得注意。第一common 集合必须至少有一件汉服否则直接返回 0。第二denominator 等于 0 时说明某一方的评分完全相同常见于只给同一商品打过分的用户此时返回 0避免出现 NaN。第三这里的评分来自 rating 表score 是 1 到 5 的整数如果后续改造成用 purchase 次数隐式评分只需要把 score 换成频率或次数相似度公式无需变动。3.3 推荐排序与 Top-N 结果的生成拿到当前用户对所有其他用户的相似度后加权汇总候选商品的评分。计算规则是候选商品必须满足“用户未评分”条件预测分数等于所有相似用户对该商品的评分乘用户相似度之和除以相似度绝对值之和。这样得到的是介于 1 到 5 之间的预测值按降序取前 10 条返回。def recommend_for_user(user_id, user_ratings, similarity_funcpearson_score, top_n10): target user_ratings.get(user_id, {}) if not target: return [] scores {} sim_sums {} for other_uid, other_ratings in user_ratings.items(): if other_uid user_id: continue sim similarity_func(target, other_ratings) if sim 0: continue for product_id, score in other_ratings.items(): if product_id in target: continue scores.setdefault(product_id, 0) scores[product_id] score * sim sim_sums.setdefault(product_id, 0) sim_sums[product_id] sim ranked sorted( [(pred_score / sim_sums[pid], pid) for pid, pred_score in scores.items()], reverseTrue ) return [(pid, round(pred, 2)) for pred, pid in ranked[:top_n]]scores 和 sim_sums 两个 dict 是加权平均的两部分分子是评分加权和分母是相似度总和。只用相似度大于 0 的用户参与预测避免把不相似的用户噪声带进来。sim 等于 0 的用户直接跳过这是推荐结果质量下降的主要因素之一。如果数据集很小top_n 可以缩小到 5如果商品池超过 50 件取 10 比较稳妥。3.4 冷启动路径新用户和新商品各怎么办这个方案里有一个冷启动问题新用户没有评分pearson_score 里 common 为空返回 0最终推荐列表为空。汉服商城管理系统里最常见的做法是给新用户推全局热门商品按 product 表里的评分次数和平均分排序。新商品则靠上架后主动导出到推荐折线图的“新品”分组里算法侧不做特殊处理。SELECT p.id, p.name, COUNT(r.score) AS cnt, AVG(r.score) AS avg_score FROM product p LEFT JOIN rating r ON p.id r.product_id GROUP BY p.id ORDER BY cnt DESC, avg_score DESC LIMIT 10这段 SQL 的热门兜底逻辑用 COUNT 和 AVG 综合排序而不是只看平均分防止只有 1 条 5 分评分的新商品冲到第一位。我把这个查询封装在 recommend_service 的 get_hot_products 方法里GUI 登录后默认展示热门榜等用户产生行为后再切换到协同过滤结果。这是最符合项目演示节奏的方案也方便后续接入用户画像后做二次升级。4. 订单全流程自动化状态机、库存扣减与支付回调的一致性4.1 订单状态机的节点定义与合法流转方向订单自动化听起来很复杂落到代码层面就是一张状态迁移表。汉服商城的订单状态可以收敛成五个节点pending_payment、paid、shipped、completed、cancelled。合法流转路径只有四条pending_payment 到 paid、pending_payment 到 cancelled、paid 到 shipped、shipped 到 completed。取消操作只在未支付状态下允许已支付订单要退款走售后流程课程的复杂度到这里就够了。状态机的价值在于把订单流转从“随手改字段”变成“显式定义合法路径”。每次更新状态都必须经过 TRANSITIONS 这个 dict 检查非法迁移直接抛异常。这样就不会出现在 GUI 里误点按钮把订单从 pending_payment 改成 completed 的尴尬情况。# order_service.py 订单状态机与自动流转 ORDER_STATUS { pending_payment: 待支付, paid: 已支付, shipped: 已发货, completed: 已完成, cancelled: 已取消 } VALID_TRANSITIONS { pending_payment: {paid, cancelled}, paid: {shipped}, shipped: {completed} } def change_order_status(order_no, new_status): conn get_conn() row conn.execute(SELECT status FROM orders WHERE order_no ?, (order_no,)).fetchone() if not row: raise ValueError(f订单 {order_no} 不存在) old_status row[status] if new_status in VALID_TRANSITIONS.get(old_status, set()): with conn: conn.execute( UPDATE orders SET status ? WHERE order_no ?, (new_status, order_no) ) return True raise ValueError(f非法状态流转: {old_status} - {new_status})执行更新前先 SELECT 当前状态是有意的双查询设计让状态机检查与应用层逻辑分离。如果有并发风险比如两人同时操作同一个订单需要在 UPDATE 语句里加 WHERE status ? 来做乐观锁但单机课程项目的规模里双查询已经能覆盖绝大多数场景。值得强调的是所有订单状态字段都存英文键中文映射只在 GUI 展示层做转换避免数据库里存“已支付”这种字符串导致排序和比较出错。4.2 创建订单的事务边界同时写入订单、明细和库存日志订单创建是整个系统里事务性最强的操作。一个订单里可能包含齐胸襦裙、大袖衫和发带三件商品其中任何一件库存不足都应当导致整单失败不能出现订单创建成功但某件商品库存为负的情况。这里的做法是把扣减库存和写入订单放在同一个事务里任何一步抛异常都会回滚。# order_service.py 创建订单并自动扣库存 def create_order(user_id, items, generate_order_noNone): generate_order_no generate_order_no or default_order_no order_no generate_order_no() conn get_conn() try: with conn: total_price 0 for product_id, qty in items: row conn.execute( SELECT id, price, stock FROM product WHERE id ?, (product_id,) ).fetchone() if not row: raise ValueError(f商品 {product_id} 不存在) if row[stock] qty: raise ValueError(f商品 {row[id]} 库存不足) for product_id, qty in items: row conn.execute( SELECT price FROM product WHERE id ?, (product_id,) ).fetchone() total_price row[price] * qty conn.execute( INSERT INTO orders(order_no, user_id, status, total_price) VALUES (?,?,?,?), (order_no, user_id, pending_payment, round(total_price, 2)) ) order_id conn.execute(SELECT id FROM orders WHERE order_no ?, (order_no,)).fetchone()[id] for product_id, qty in items: row conn.execute(SELECT price FROM product WHERE id ?, (product_id,)).fetchone() conn.execute( UPDATE product SET stock stock - ? WHERE id ?, (qty, product_id) ) conn.execute( INSERT INTO order_item(order_id, product_id, quantity, price) VALUES (?,?,?,?), (order_id, product_id, qty, row[price]) ) conn.execute( INSERT INTO stock_log(product_id, change_qty, order_no) VALUES (?,?,?), (product_id, -qty, order_no) ) except Exception: raise return order_no把查询库存循环和扣库存循环拆开是因为第二段循环里已经在事务中任何一条 UPDATE 失败都会让整单回滚。stock_log 里 change_qty 写负数语义清楚表示出库退货时可以写正数用来找回库存。注意 UPDATE product 的扣减没有 WHERE stock ? 守卫这是因为前面已经查过库存如果这是高并发生产系统必须把库存判断写进 UPDATE 的 WHERE 条件里否则会出现超卖。4.3 支付回调和超时取消的定时任务实现订单创建后状态是 pending_payment模拟支付回调和超时取消是订单全流程自动化里最有演示效果的部分。常见做法是实现一个简单的支付接口接收订单号后把状态从 pending_payment 改为 paid同时记录 paid_at 时间。超时取消则是后台循环扫描超过 15 分钟未支付的订单把它们统一置为 cancelled 并回补库存。# 扫描超时订单并回补库存建议每 60 秒执行一次 def cancel_expired_orders(timeout_minutes15): conn get_conn() expired_rows conn.execute( SELECT order_no FROM orders WHERE status pending_payment AND created_at datetime(now, ?), (f-{timeout_minutes} minutes,) ).fetchall() for row in expired_rows: order_no row[order_no] items conn.execute( SELECT product_id, quantity FROM order_item WHERE order_id (SELECT id FROM orders WHERE order_no ?), (order_no,) ).fetchall() with conn: for item in items: conn.execute( UPDATE product SET stock stock ? WHERE id ?, (item[quantity], item[product_id]) ) conn.execute( INSERT INTO stock_log(product_id, change_qty, order_no) VALUES (?,?,?), (item[product_id], item[quantity], order_no) ) conn.execute( UPDATE orders SET status cancelled WHERE order_no ? AND status pending_payment, (order_no,) )这段代码的设计重点在于回补库存前先查 order_item把数量取出来再加回去。直接在 product 表上把 stock 加回去然后写一个固定值到 stock_log 是不对的因为日志必须记录真实的商品数量和对应的订单号否则事务审计时对不上账。cancel_expired_orders 可以单独开一个后台线程配合 Python 的threading.Timer每 60 秒跑一轮也可以挂在 GUI 的退出事件里调用一次。4.4 幂等性设计重复支付、重复取消的防护订单自动化的坑大多出在重复请求上。GUI 按钮被双击会触发两次支付回调后台定时任务也可能和手动操作撞在同一条订单上。防护手段是在支付和取消两个入口都加状态条件比如上面代码里的WHERE status pending_payment能够让 UPDATE 在状态已经被改掉时不生效。同时给支付回调加一个配套查询让 SELECT 与 UPDATE 的 WHERE 条件保持一致。# order_service.py 幂等支付回调 def pay_order(order_no): conn get_conn() with conn: cursor conn.execute( UPDATE orders SET status paid, paid_at datetime(now) WHERE order_no ? AND status pending_payment, (order_no,) ) if cursor.rowcount 0: conn.execute(SELECT status FROM orders WHERE order_no ?, (order_no,)) raise ValueError(订单不存在或已处理过) return True判断 cursor.rowcount 而非直接 raise是因为 rowcount 等于 0 不仅代表订单不存在也代表状态已被其他操作改动。此时再查一次状态给出更准确的错误信息。这个模式就是幂等控制的常用实现支付、发货、完成三步各不相同但都靠 WHERE status 条件来拒绝重复操作。5. 推荐结果落库与 GUI 图表联动把算法结果变成可视化面板5.1 推荐结果写回数据库的用途与批处理策略推荐引擎每次计算都是在内存里完成的用户关掉程序结果就丢了。汉服商城管理系统里常把推荐结果落库常见做法是建一张 recommendation 表记录 user_id、product_id、score、generate_time 四个字段。落库有两个作用一是 GUI 侧查询时直接读表不需要重新跑算法加快界面响应二是可以对比不同推荐策略在某个时间点的效果差异。CREATE TABLE product_recommendation ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, product_id INTEGER NOT NULL, score REAL NOT NULL, generate_time TEXT DEFAULT (datetime(now,localtime)), UNIQUE(user_id, product_id, generate_time) );写入推荐结果时要注意 UNIQUE 约束的粒度。如果同一个用户每分钟刷新一次推荐generate_time 精确到秒就不会产生冲突。如果刷新太频繁且 generate_time 到秒重复UNIQUE 会导致 INSERT 失败这时需要改成 INSERT OR REPLACE。推荐结果表不应该无限积累否则查询会变慢常见做法是只保留最近一周的推荐记录用 DELETE 加时间范围定期清理。5.2 Tkinter 里嵌入 matplotlib 图表展示推荐热度recommendation 表的数据直接画成柱状图是最直观的演示方式。将预测评分最高的前 10 件汉服按商品名和预测分展示同时叠加一个推荐分时段曲线通过按钮切换刷新。Tkinter 本身不支持图表控件需要在窗口里嵌入 FigureCanvasTkAgg这是 Python 项目里最常用的 matplotlib 与 Tkinter 联动方案。# gui_dashboard.py 嵌入 matplotlib 柱状图 from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import matplotlib matplotlib.use(TkAgg) from matplotlib.figure import Figure import tkinter as tk def refresh_recommend_chart(canvas_frame, user_id): rows query_all( SELECT r.score, p.name FROM product_recommendation r JOIN product p ON p.id r.product_id WHERE r.user_id ? ORDER BY r.score DESC LIMIT 8, (user_id,) ) if not rows: rows [(0, 暂无推荐)] names [row[name] for row in rows] scores [row[score] for row in rows] fig Figure(figsize(6, 3), dpi100) ax fig.add_subplot(111) ax.barh(names[::-1], scores[::-1], color#9d8171) ax.set_title(AI 推荐评分 Top8) fig.tight_layout() for widget in canvas_frame.winfo_children(): widget.destroy() canvas FigureCanvasTkAgg(fig, mastercanvas_frame) canvas.draw() canvas.get_tk_widget().pack(filltk.BOTH, expandTrue)使用ax.barh(names[::-1], scores[::-1])是为了让评分最高的汉服显示在图表顶部符合大多数人的阅读顺序。每次刷新先 destroy 旧 canvas 再创建新的是避免 matplotlib 图形叠加残留的标准做法。colors 参数用的是偏棕色的复古色系与汉服主题保持一致GUI 里所有控件都能统一使用这套色值。5.3 库存预警与订单趋势的联动展示推荐图表之外订单趋势图可以直接从 orders 表按天统计订单数和销售额。SQL 层面用 date(created_at) 分组再用 strftime 格式化日期得到按天汇总的数据。这个查询与推荐图表并行刷新构成一句话能解释的闭环推荐引擎把商品推给用户用户下单生成订单订单状态自动化完成履约趋势图反映推荐转化效果。SELECT date(created_at) AS day, COUNT(*) AS order_count, SUM(total_price) AS revenue FROM orders WHERE status IN (paid, shipped, completed) GROUP BY day ORDER BY day一张图展示订单数一张图展示推荐评分不需要第三张图。库存预警可以直接显示在商品列表同一个界面里用红色背景标注 stock 5 的商品行让 GUI 具备一个基础的管理端告警能力。这个设计会让评委或读者一眼看出系统里推荐算法不是孤立的它是和订单、库存、用户行为数据连在一起的。6. 用一条主链路验证推荐与订单自动化的完整闭环写代码只是完成了系统的一部分更重要的是一分钟内能把整个流程串起来演示。推荐和订单自动化有一条最短验证路径先给用户 A 造 5 条评分再给用户 B 造 4 条与 A 重合的评分然后执行 create_order 再 pay_order最后刷新推荐图和订单趋势图。可以用一段脚本来代替手工点击让验证变得可重复。# verify_flow.py 一键验证推荐与订单自动化闭环 from typing import Dict, List, Tuple def seed_demo_data(): execute( INSERT INTO user(username,password) VALUES (alice,1234),(bob,1234) ON CONFLICT(username) DO NOTHING ) alice_id query_all(SELECT id FROM user WHERE usernamealice)[0][id] bob_id query_all(SELECT id FROM user WHERE usernamebob)[0][id] alice_ratings: List[Tuple[int, int]] [(1,5), (2,4), (3,5), (4,3)] bob_ratings: List[Tuple[int, int]] [(1,4), (2,5), (3,4), (5,5)] for pid, score in alice_ratings: execute( INSERT INTO rating(user_id, product_id, score) VALUES (?,?,?) ON CONFLICT(user_id, product_id) DO UPDATE SET scoreexcluded.score, (alice_id, pid, score) ) for pid, score in bob_ratings: execute( INSERT INTO rating(user_id, product_id, score) VALUES (?,?,?) ON CONFLICT(user_id, product_id) DO UPDATE SET scoreexcluded.score, (bob_id, pid, score) ) user_ratings build_user_ratings() top recommend_for_user(bob_id, user_ratings, top_n3) print(Bob 推荐结果:, top) order_no create_order(bob_id, [(1, 1), (3, 1)]) pay_order(order_no) print(订单号:, order_no, 已支付) cancel_expired_orders(timeout_minutes15) print(订单状态:, query_all(SELECT status FROM orders WHERE order_no ?, (order_no,))[0][status]) if __name__ __main__: seed_demo_data()把 ON CONFLICT 升级写成 DO UPDATE是为了让验证脚本可以重复运行否则第二次执行时会因为主键冲突报错。构建 user_ratings 时直接从 rating 表读所有用户评分推荐的顺序是稳定的因为相同数据下 pearson_score 的浮点计算是确定性的。验证完推荐结果后直接调 create_order 和 pay_order此时能看到同一批数据里推荐商品与订单商品有关联展示时可以说清楚推荐点击率的来源。这个脚本应该放在项目的根目录下用python verify_flow.py就能跑通Windows 和 Linux 环境结果一致。本文还有配套的精品资源点击获取