4. 特殊订单:赠品订单、补发订单、拆单与合单、跨境订单、企业采购订单

前面聊了那么多常规订单,今天咱们来点「不按套路出牌」的。特殊订单,说白了就是那些让系统架构师头疼、让开发同学加班的订单类型。我这些年踩过的坑,有一半都跟这些特殊场景有关。

嗯,咱们一个一个来看。

4.1 赠品订单

赠品订单,听着简单吧?不就是白送东西嘛。但实际做起来,坑多着呢。

核心问题:赠品要不要走库存?

我个人习惯是:走,必须走。不走库存的赠品,财务对账时就是一笔糊涂账。

赠品订单的典型场景:
  • 买一送一:主商品正常下单,赠品以0元子订单形式关联
  • 满赠活动:达到门槛后自动生成赠品订单
  • 注册礼/生日礼:独立发放,不依赖主订单

我在项目中遇到过一种情况:运营搞了个「买手机送耳机」活动,结果赠品耳机没扣库存,导致超卖2000单。最后只能给用户发道歉券,亏了十几万。

避坑指南: 赠品订单必须绑定主订单ID,否则售后退货时,用户退了手机留下耳机,系统根本不知道这笔账怎么算。

赠品订单的数据结构,我建议这样设计:

// 赠品订单核心字段
{
  "orderId": "G20240315001",      // 赠品订单号,前缀区分
  "parentOrderId": "M20240315001", // 主订单ID
  "giftType": "PURCHASE_GIFT",     // 赠品类型
  "price": 0,                      // 金额为0
  "inventoryDeducted": true,       // 是否已扣库存
  "returnPolicy": "WITH_MAIN"      // 退货策略:随主单
}

4.2 补发订单

补发订单,说白了就是「上次没发对,这次补给你」。但系统层面,它可不是简单的重新发一次货。

补发的几种原因:

  • 物流丢件:快递弄丢了,用户没收到
  • 商品破损:收到时坏了,需要换新
  • 漏发少发:仓库少装了东西
  • 地址错误:用户写错地址,退回重发

你想想看,补发订单要不要重新支付?当然不要。那财务怎么记账?

我建议的做法是:补发订单作为原订单的「售后子单」,不走正向支付流程。

我的经验: 补发订单一定要关联原售后单ID。我曾经见过一个系统,补发订单和售后单是两套独立流程,结果用户申请了退货退款,系统却还在走补发流程,货发出去了,钱也退了,血亏。

补发订单的状态机,比普通订单复杂得多:

// 补发订单状态流转
PENDING_APPROVE → APPROVED → WAITING_SEND → SHIPPED → RECEIVED
       ↓            ↓
   REJECTED    CANCELLED

4.3 拆单与合单

拆单和合单,是电商系统里最考验架构设计能力的环节。说白了就是:一个订单怎么拆成多个包裹发?多个订单怎么合并成一个包裹发?

拆单的场景

  • 仓库拆单:商品在不同仓库,必须分开发
  • 品类拆单:生鲜和日用品不能混装
  • 供应商拆单:自营和第三方商品分开
  • 物流拆单:大件商品走物流,小件走快递

我记得有一次做跨境项目,一个订单里有3件商品,分别在上海、广州、宁波三个保税仓。如果不拆单,系统直接报错——因为没有一个仓库能同时发这三件货。

合单的场景

  • 同一用户、同一地址:30分钟内下的两个订单,合并发货
  • 预售尾款+现货:付尾款时和现货合并
  • 凑单满减:用户为了凑满减分开下单,系统自动合单
注意: 合单不能影响优惠计算。比如用户用了两张优惠券下的两个订单,合单后优惠券的核销逻辑必须重新计算。我见过一个bug,合单后两张券都生效了,用户白嫖了60块钱。

拆合单的核心逻辑,我画了一张图:

// 拆合单决策树(伪代码)
function decideSplitOrMerge(order) {
  if (order.items 跨仓库) {
    return SPLIT_BY_WAREHOUSE
  }
  if (order.items 跨品类且不能混装) {
    return SPLIT_BY_CATEGORY
  }
  if (同一用户30分钟内多单且地址相同) {
    return MERGE
  }
  return SINGLE_PACKAGE
}

4.4 跨境订单

跨境订单,是我做过最复杂的订单类型,没有之一。为什么?因为涉及的东西太多了:海关、税率、身份证信息、物流追踪、多币种...

跨境订单的核心差异:

  • 实名认证:必须上传身份证正反面
  • 三单对碰:订单、支付单、物流单,三单信息必须一致才能清关
  • 税费计算:跨境综合税 = 关税 + 增值税 + 消费税
  • 物流分段:国内段 + 国际段 + 清关

我个人习惯把跨境订单的状态机设计成两段式:

// 跨境订单状态
国内段: PENDING_PAY → PAID → WAITING_CUSTOMS → CUSTOMS_CLEARED
国际段: CUSTOMS_CLEARED → INTERNATIONAL_SHIPPING → DOMESTIC_DELIVERY → RECEIVED
避坑指南: 我曾经遇到过一个坑:用户下单时填的身份证号和支付人姓名不一致,导致三单对碰失败,订单卡在海关整整7天。后来我们加了个校验:下单时实时校验身份证与支付人是否匹配。

跨境订单的税费计算,我建议用独立服务来做:

// 跨境税费计算
function calculateCrossBorderTax(product, user) {
  const dutyRate = getDutyRate(product.category)  // 关税税率
  const vatRate = 0.13                            // 增值税率
  const consumptionRate = getConsumptionRate(product) // 消费税率
  
  const duty = product.price * dutyRate
  const vat = (product.price + duty) * vatRate
  const consumption = (product.price + duty) / (1 - consumptionRate) * consumptionRate
  
  return {
    totalTax: duty + vat + consumption,
    duty, vat, consumption
  }
}

4.5 企业采购订单

企业采购订单,跟个人订单完全是两码事。你想想看,企业买东西要什么?发票、对公转账、审批流程、合同...

企业订单的核心特征:

  • 对公支付:支持银行转账、承兑汇票
  • 专票需求:必须开具增值税专用发票
  • 多级审批:采购员下单 → 部门经理审批 → 财务审批
  • 合同绑定:大额采购需要关联采购合同
  • 账期支持:先发货后付款,30天/60天账期

我在做企业采购平台时,最头疼的是价格体系。企业用户看到的价格和个人用户不一样,而且不同企业还有不同的协议价。

我的建议: 企业采购的订单系统,一定要和CRM系统打通。因为企业用户的信用额度、账期、协议价,都来自CRM。我曾经见过一个系统,订单系统和CRM各自维护一套企业信息,结果对账时差了200多万。

企业订单的审批流程,我建议用工作流引擎来处理:

// 企业采购审批流程
ORDER_CREATED → PENDING_APPROVAL → APPROVED → WAITING_PAYMENT → PAID → SHIPPED
                    ↓
               REJECTED → ORDER_CLOSED

这里有个细节:审批中的订单,库存要不要预占? 我个人习惯是预占,但设置一个超时释放时间。比如24小时内未审批,自动释放库存。否则采购员下一堆单不审批,库存全锁死了。

知识体系总览

最后,我把这五种特殊订单的核心逻辑画了张图,方便你理解它们之间的关系:

特殊订单体系 赠品订单 0元子订单·库存扣减 补发订单 售后子单·免支付 拆单与合单 仓库·品类·物流 跨境订单 三单对碰·税费 企业采购 对公·审批 各类型核心关注点 类型 核心挑战 关键设计 常见坑 赠品 库存与财务 绑定主订单 超卖 补发 售后关联 售后子单 重复发货 拆合单 库存与物流 决策树 优惠冲突 跨境 海关与税费 三单对碰 身份校验 企业 审批与账期 工作流引擎 库存锁死

这五种特殊订单,说白了就是常规订单的「变体」。但正是这些变体,决定了你的电商系统到底能不能扛住真实业务。我见过太多系统,常规订单跑得飞起,一到赠品、跨境就崩了。

嗯,今天就聊到这儿。这些特殊订单的处理逻辑,建议你在设计系统时提前规划好,别等到业务来了再补,那可就晚了。


专注资料整理