OpenClaw—AI研究OpenClaw—AI研究
  • AI动态
  • OpenClaw教程
  • 技术解读
  • 用户故事

【开发日志】ShopSystem Day 20: 满减/包邮营销活动(2026-09-02)

【开发日志】ShopSystem Day 20: 满减/包邮营销活动(2026-09-02)

2026年9月2日 by WoodStone

Day 20 完成了第三阶段「营销 + 推荐」的第 2 项:满减/包邮营销活动。新增 promotions 表记录活动配置,后台提供创建/启停/编辑/删除管理界面,结算页自动按订单金额匹配最优活动并实时落库到 orders 与 payments。叠加规则参考淘宝/京东主流设计:满减活动可与优惠券叠加(都减),包邮活动替换 ¥99 默认免邮逻辑,多个满减活动同时匹配只取减最多,多个包邮活动任一即生效,避免叠加优惠被薅羊毛。

后台满减/包邮活动管理列表

活动匹配在订单事务内完成,与行锁读 cart_items、库存原子扣减(Day 11 铁律)绑在一起,保证 totalAmount 与 promotion 选取在事务内原子落地。具体匹配 SQL 按 type、merchant_id 是否 NULL(平台优先)、threshold 降序排序,客户端取 reduction 最大与阈值最低两个分支。结算页用 useEffect 实时拉预览,在商品金额变化时刷新活动命中状态,提前告诉用户「已享满减 / 已包邮」或「还差 ¥X 可享」,避免结算时才发现差异。

设计上几个关键决策:第一,scope 本期只支持 all(整张订单生效),按购物车商品商家归属的 scope=’category’ / scope=’products’ 留 Day 21+ 扩展,本期不引入 promotion_products 关联表,避免时间预算膨胀;第二,orders.promotion_id 不加外键约束,删除活动后历史订单记录自然保留(对比 coupons 删除时软删是因为 user_coupons 有 FK),管理员不必先 UPDATE orders 才能删活动;第三,平台 vs 商家活动优先级:merchant_id IS NULL 排在前面,同 threshold 时优先平台,商家活动按全平台可见简化处理(购物车跨商家场景 Day 22 再细化);第四,文案三分支:allActive 为空显示「暂无可用活动」(不是误导性的「未达门槛」),closestGap 不为 null 时显示「还差 ¥X 可享满减/包邮」(用所有未命中活动中 threshold – totalPrice 的最小值),应用命中时显示绿色卡片明确活动名 + 规则 + 已自动应用标识。

前台结算页满减与包邮活动展示

本次还顺带修复了 4 个 Day 20 功能落地过程中暴露的真实问题。第一个是订单详情页(P1):OrderPageClient 原先不渲染「满减活动折扣 -¥X」行,根因是 Order interface 与 SSR/API 的 SELECT 都缺 promotion_id / promotion_discount / free_shipping 三字段,handleMockPay 与 onRefundApplied 回调里重 fetch API 时也会丢数据 — 这次三处(组件 + SSR page + API 路由)同步补齐,并把运费分支文案改为「免运费(活动)」区别于 ¥99 默认免邮。第二个是 payments.amount(P1):原先用 totalAmount.toFixed(2) 写入支付金额,但 totalAmount 是商品原值(¥199),未扣满减/优惠券,与支付网关实付不一致 — 改为 finalAmount = totalAmount – discountAmount – promotionDiscount + shippingFee(¥179),保持 orders.total_amount = 商品原值用于对账、payments.amount = 实付用于支付的语义分离。

第三个是 startsAt PATCH 越界(Bug):管理员把开始时间设到 2099-01-01 触发 MySQL STRICT_TRANS_TABLES 报 500。根因是 promotions.starts_at / ends_at 列类型是 TIMESTAMP(MySQL 范围仅 1970-2038),加了迁移 034_promotions_dates_to_datetime 把两列改为 DATETIME(范围扩展到 1000-9999 且不做时区转换);同时把 toMysqlDatetime helper 加上 fieldName 参数,格式无效时抛 Error 让 route handler 转 400,避免静默吞成 NULL 掩盖输入错误,但 null / undefined / 空串还原路径仍显式支持(还原场景 SQL 走 SET starts_at = NULL)。第四个是 Qclaw 浏览器测试发现的全站原生 confirm() 卡住问题:xb 工具无法点浏览器原生 confirm() 模态,只能操作 DOM,导致测试流程经常中断 — 新建 ConfirmDialog 组件(104 行,role=dialog + aria-modal + Esc/遮罩关闭 + danger 变体 ⚠ + 红按钮),把全站 15 处 confirm() 全部替换为 useState 控制的 DOM 弹窗,12 个组件覆盖 PromotionsAdmin / BrowseHistoryTab / AddressesTab / FavoritesTab / CartPageClient / OrderStatusActions / UserRowActions / MerchantReviewActions / CouponsAdmin / SettingsForm / FavoriteButton,Playwright 监听 dialog 事件 0 次触发验证。

订单详情页满减活动折扣展示修复后效果

总计 1 张新表(promotions 9 列 + 3 索引)、orders 加 3 列、4 个新 API、1 个新后台组件(273 行复用 CouponsAdmin 模板)、1 个新页(/ladmintood/promotions)、AdminShell 侧栏加 🎁 满减/包邮活动 入口(roles: admin + merchant)、CheckoutPageClient 加 🎁 section + 三分支文案、价格摘要加满减行、ConfirmDialog 组件 1 个新增 + 12 组件改造 15 处,3 个迁移文件(migrate.ts 新增 032/033/034)。后续工作重点是 Day 21 限时秒杀与 Day 22 店铺关注(第三阶段剩余 2 项)、活动 scope 按分类/商品细化的扩展、coupons.starts_at 也用同样方式改 DATETIME 防止同类 Bug、商家活动按购物车商品归属限定应用范围。本地详细版开发日志含测试账号密码、决策铁律、e2e 详情,公开版以本帖为准已脱敏。

← 返回文章列表
分类: 技术解读 标记: Day20, Next.js, ShopSystem, 开发日志, 满减包邮, 营销

© 2026 OpenClaw—AI研究 版权所有

沪ICP备2026010690号-1