如果把品牌比作一支乐队🎸,电商、实体店、私域就是三大乐手,而备货比例就是给每个乐手分配多少"演奏时间"。咱们今天就用 Python 当指挥棒,把这场"库存交响乐"的比例调校清楚。

 

线上线下多渠道利润分配与备货平衡算法

 

(Omni-Channel Inventory Allocation & Profit Distribution Engine)

 

定位:教学级多渠道库存优化与利润归因工具

语言:Python 3.10+

适用场景:时尚产业与品牌创新课程、全渠道供应链管理、零售利润优化

 

一、实际应用场景描述

 

在现代时尚零售体系中,品牌普遍采用全渠道(Omni-Channel)策略,同时运营:

 

- 电商平台(天猫、京东、抖音小店):触达广、转化快、价格敏感

- 实体门店(直营店、商场专柜):体验好、客单价高、库存周转慢

- 私域渠道(微信社群、小程序、会员体系):复购高、运营成本低、流量有限

 

在制订季度商品企划时,核心决策问题是:

 

有限的库存(如 5000 件新品),应该按什么比例分配到三个渠道?

如何在"卖得动"和"赚得多"之间找到最优解?

这不是简单的"三七开"能解决的,需要量化模型支撑。

 

二、引入痛点(行业现实问题)

 

1. 拍脑袋定比例,缺乏数据依据

 

- 常见做法:"电商 50%、门店 35%、私域 15%"——依据是什么?

- 不同品类、不同季节、不同城市等级的最优比例完全不同

- 缺少一个可计算的优化框架

 

2. 各渠道"利润逻辑"不统一

 

渠道 成本结构 利润驱动因素

电商 平台扣点高、物流成本 走量、转化率

实体店 租金人力重、装修摊销 客单价、连带率

私域 获客成本低、运营轻 复购率、信任转化

 

用同一把尺子衡量三个渠道,必然失真。

 

3. 库存错配代价高昂

 

- 电商缺货 → 错失流量窗口期(平台活动就那几天)

- 门店积压 → 季末打折伤品牌

- 私域没货 → 老客户流失

 

三、核心逻辑讲解(建模思路)

 

1. 核心假设(教学简化)

 

- 总库存量固定(约束条件)

- 各渠道有独立的单位经济模型(售价、成本、费率不同)

- 目标函数:最大化总净利润

- 约束条件:各渠道最低/最高备货量(如门店不能低于展示量)

 

2. 关键变量定义

 

变量 含义

 

"total_inventory" 总可用库存(件)

 

"unit_cost" 单件成本(元)

 

"price_ecom" / 

"price_store" / 

"price_private" 各渠道售价

 

"platform_fee_rate" 电商平台扣点比例

 

"store_opex_per_unit" 门店单件运营分摊成本

 

"private_conversion_rate" 私域转化率

 

"min_store_stock" 门店最低陈列库存

 

"max_ecom_stock" 电商最大可承载库存(仓储/平台限制)

 

3. 利润公式

 

渠道净利润 = 销量 × (售价 − 成本 − 渠道专属费用)

 

电商净利润 = 销量 × (price_ecom − unit_cost − price_ecom × platform_fee_rate)

门店净利润 = 销量 × (price_store − unit_cost − store_opex_per_unit)

私域净利润 = 销量 × (price_private − unit_cost) (无平台扣点)

 

总净利润 = Σ 各渠道净利润

 

4. 优化思路:遍历搜索法

 

由于只有 3 个渠道,可以用离散遍历寻找最优分配:

 

遍历电商库存: 0 → total_inventory

  遍历门店库存: 0 → (total_inventory − 电商库存)

    私域库存 = 剩余

    计算总利润

    记录最大值

 

工程实践中可用线性规划(SciPy)或启发式算法,此处为教学可读性采用遍历法。

 

四、项目结构

 

channel-allocation-engine/

├── README.md

├── USAGE.md

├── main.py

├── modules/

│ ├── channel.py # 渠道利润模型

│ ├── optimizer.py # 库存分配优化引擎

│ ├── constraints.py # 约束条件校验

│ └── reporter.py # 结果输出与对比

└── config/

    └── allocation.yaml

 

五、核心代码实现(Python)

 

1️⃣ 渠道利润模型 

"channel.py"

 

# channel.py

# 定义各渠道的独立利润计算模型

 

 

from dataclasses import dataclass

from typing import Optional

 

 

@dataclass

class ChannelProfile:

    """单个渠道的利润属性配置"""

 

    name: str

    unit_cost: float # 单件成本(共享)

    selling_price: float # 渠道售价

    variable_cost_per_unit: float # 渠道专属变动成本(扣点/运费等)

    fixed_cost_total: float = 0.0 # 渠道固定成本总额(可选)

    max_capacity: Optional[int] = None # 最大可承载库存

    min_stock: Optional[int] = None # 最低保底库存

 

    def profit_per_unit(self, sold: int) -> float:

        """计算该渠道的单件毛利润"""

        if sold <= 0:

            return 0.0

        revenue = sold * self.selling_price

        variable_costs = sold * (

            self.unit_cost + self.variable_cost_per_unit

        )

        return revenue - variable_costs - self.fixed_cost_total

 

    def unit_margin(self) -> float:

        """单件贡献毛利(不含固定成本)"""

        return self.selling_price - self.unit_cost - self.variable_cost_per_unit

 

 

def build_channel_profiles(unit_cost: float) -> dict:

    """工厂方法:构建三个渠道的配置"""

    return {

        "ecom": ChannelProfile(

            name="电商平台",

            unit_cost=unit_cost,

            selling_price=299.0,

            variable_cost_per_unit=299.0 * 0.08, # 平台扣点 8%

            fixed_cost_total=5000, # 平台年费/推广费分摊

        ),

        "store": ChannelProfile(

            name="实体门店",

            unit_cost=unit_cost,

            selling_price=459.0,

            variable_cost_per_unit=65.0, # 租金+人力+水电分摊/件

            fixed_cost_total=30000, # 门店固定开支

            min_stock=200, # 最低陈列量

        ),

        "private": ChannelProfile(

            name="私域渠道",

            unit_cost=unit_cost,

            selling_price=349.0,

            variable_cost_per_unit=15.0, # 小程序手续费+包装

            fixed_cost_total=2000, # 社群运营分摊

        ),

    }

 

2️⃣ 约束条件校验 

"constraints.py"

 

# constraints.py

# 校验库存分配是否满足业务约束

 

 

from modules.channel import ChannelProfile

 

 

def validate_allocation(

    ecom_stock: int,

    store_stock: int,

    private_stock: int,

    profiles: dict,

    total_inventory: int

) -> tuple[bool, str]:

    """

    校验一组分配方案是否合法

 

    返回:(is_valid, error_message)

    """

    # 1. 总量守恒

    if ecom_stock + store_stock + private_stock != total_inventory:

        return False, "库存总量不匹配"

 

    # 2. 非负

    if any(s < 0 for s in [ecom_stock, store_stock, private_stock]):

        return False, "库存不能为负"

 

    # 3. 门店最低陈列

    store = profiles["store"]

    if store.min_stock and store_stock < store.min_stock:

        return False, f"门店库存低于最低陈列量 {store.min_stock}"

 

    # 4. 电商最大容量

    ecom = profiles["ecom"]

    if ecom.max_capacity and ecom_stock > ecom.max_capacity:

        return False, f"电商库存超过最大承载量 {ecom.max_capacity}"

 

    return True, ""

 

 

def apply_capacity_constraints(

    profiles: dict,

    total_inventory: int

) -> dict:

    """根据渠道配置生成约束边界"""

    return {

        "ecom_max": profiles["ecom"].max_capacity or total_inventory,

        "store_min": profiles["store"].min_stock or 0,

        "store_max": total_inventory,

        "private_max": total_inventory,

    }

 

3️⃣ 优化引擎 

"optimizer.py"

 

# optimizer.py

# 通过遍历搜索最优库存分配方案

 

 

from modules.channel import ChannelProfile

from modules.constraints import validate_allocation

 

 

def calculate_total_profit(

    ecom_stock: int,

    store_stock: int,

    private_stock: int,

    profiles: dict,

    sell_through_rates: dict

) -> float:

    """

    计算给定分配方案下的总净利润

 

    sell_through_rates: 各渠道预计售罄率(0~1)

    """

    total = 0.0

    for channel_key, stock in [

        ("ecom", ecom_stock),

        ("store", store_stock),

        ("private", private_stock),

    ]:

        profile: ChannelProfile = profiles[channel_key]

        expected_sales = int(stock * sell_through_rates[channel_key])

        profit = profile.profit_per_unit(expected_sales)

        total += profit

    return round(total, 2)

 

 

def find_optimal_allocation(

    total_inventory: int,

    profiles: dict,

    sell_through_rates: dict,

    step: int = 50

) -> dict:

    """

    遍历搜索最优库存分配方案

 

    step: 遍历步长(件),越小越精确,越大越快

 

    返回:{

        "best_ecom": int,

        "best_store": int,

        "best_private": int,

        "max_profit": float,

        "all_results": list # 所有可行方案

    }

    """

    best_profit = float("-inf")

    best_alloc = None

    all_results = []

 

    # 遍历电商库存

    for ecom_s in range(0, total_inventory + 1, step):

        # 遍历门店库存

        for store_s in range(0, total_inventory - ecom_s + 1, step):

            private_s = total_inventory - ecom_s - store_s

 

            is_valid, _ = validate_allocation(

                ecom_s, store_s, private_s, profiles, total_inventory

            )

            if not is_valid:

                continue

 

            profit = calculate_total_profit(

                ecom_s, store_s, private_s, profiles, sell_through_rates

            )

 

            entry = {

                "ecom": ecom_s,

                "store": store_s,

                "private": private_s,

                "profit": profit,

            }

            all_results.append(entry)

 

            if profit > best_profit:

                best_profit = profit

                best_alloc = entry

 

    return {

        "best_ecom": best_alloc["ecom"],

        "best_store": best_alloc["store"],

        "best_private": best_alloc["private"],

        "max_profit": best_profit,

        "all_results": all_results,

    }

 

 

def profit_contribution_breakdown(

    allocation: dict,

    profiles: dict,

    sell_through_rates: dict

) -> list:

    """拆解各渠道对总利润的贡献"""

    breakdown = []

    for key in ["ecom", "store", "private"]:

        profile = profiles[key]

        stock = allocation[key]

        expected_sales = int(stock * sell_through_rates[key])

        profit = profile.profit_per_unit(expected_sales)

        breakdown.append({

            "channel": profile.name,

            "stock": stock,

            "expected_sales": expected_sales,

            "profit": round(profit, 2),

            "margin_per_unit": round(profile.unit_margin(), 2),

        })

    return breakdown

 

4️⃣ 结果输出 

"reporter.py"

 

# reporter.py

# 格式化输出优化结果

 

 

def print_optimal_allocation(result: dict, total_inventory: int):

    print("\n" + "="*55)

    print(" 最优库存分配方案")

    print("="*55)

    print(f" 电商库存: {result['best_ecom']:>6} 件 "

          f"({result['best_ecom']/total_inventory*100:.1f}%)")

    print(f" 门店库存: {result['best_store']:>6} 件 "

          f"({result['best_store']/total_inventory*100:.1f}%)")

    print(f" 私域库存: {result['best_private']:>6} 件 "

          f"({result['best_private']/total_inventory*100:.1f}%)")

    print(f"\n 预计最大净利润: {result['max_profit']:.2f} 元")

 

 

def print_profit_breakdown(breakdown: list, total_profit: float):

    print(f"\n{'='*55}")

    print(" 各渠道利润贡献拆解")

    print("="*55)

    print(f" {'渠道':<10} {'库存':<8} {'预计销量':<10} {'利润':<12} {'占比':<8}")

    print(f" {'-'*52}")

 

    for b in breakdown:

        pct = (b["profit"] / total_profit * 100) if total_profit > 0 else 0

        print(f" {b['channel']:<10} {b['stock']:<8} {b['expected_sales']:<10} "

              f"{b['profit']:<12.2f} {pct:.1f}%")

 

 

def print_comparison_with_baseline(

    optimal: dict,

    baseline: dict,

    total_inventory: int

):

    """对比优化方案与基准方案(如平均分配)"""

    print(f"\n{'='*55}")

    print(" 优化方案 vs 基准方案对比")

    print("="*55)

 

    baseline_profit = baseline["profit"]

    opt_profit = optimal["max_profit"]

    lift = ((opt_profit - baseline_profit) / baseline_profit * 100)

    lift = round(lift, 2) if baseline_profit > 0 else 0

 

    print(f" 基准利润: {baseline_profit:.2f} 元")

    print(f" 优化利润: {opt_profit:.2f} 元")

    print(f" 利润提升: +{lift}%")

 

    # 比例变化

    for ch in ["ecom", "store", "private"]:

        base_pct = baseline[ch] / total_inventory * 100

        opt_pct = optimal[f"best_{ch}"] / total_inventory * 100

        print(f" {ch} 占比: {base_pct:.0f}% → {opt_pct:.0f}% "

              f"(Δ{opt_pct - base_pct:+.0f}%)")

 

5️⃣ 主程序 

"main.py"

 

# main.py

import yaml

from modules.channel import build_channel_profiles

from modules.optimizer import (

    find_optimal_allocation,

    profit_contribution_breakdown,

)

from modules.constraints import apply_capacity_constraints

from modules.reporter import (

    print_optimal_allocation,

    print_profit_breakdown,

    print_comparison_with_baseline,

)

 

def load_cfg(path: str) -> dict:

    with open(path, "r", encoding="utf-8") as f:

        return yaml.safe_load(f)

 

 

if __name__ == "__main__":

    cfg = load_cfg("config/allocation.yaml")

 

    total_inv = cfg["inventory"]["total_units"]

    unit_cost = cfg["product"]["unit_cost"]

 

    # 构建渠道模型

    profiles = build_channel_profiles(unit_cost)

 

    # 应用渠道专属售价/费率覆盖(从配置文件)

    if "price_overrides" in cfg:

        for ch, price in cfg["price_overrides"].items():

            if ch in profiles:

                profiles[ch].selling_price = price

 

    if "platform_fee_rates" in cfg:

        for ch, rate in cfg["platform_fee_rates"].items():

            if ch in profiles:

                profiles[ch].variable_cost_per_unit = (

                    profiles[ch].selling_price * rate

                )

 

    # 售罄率

    sell_through = cfg["sell_through_rates"]

 

    # 约束边界

    constraints = apply_capacity_constraints(profiles, total_inv)

 

    print("="*55)

    print(" 全渠道库存分配优化引擎")

    print("="*55)

    print(f"\n总库存: {total_inv} 件 | 单件成本: {unit_cost} 元")

    print(f"步长: {cfg['optimization']['step']} 件")

 

    # 搜索最优方案

    result = find_optimal_allocation(

        total_inventory=total_inv,

        profiles=profiles,

        sell_through_rates=sell_through,

        step=cfg["optimization"]["step"]

    )

 

    # 基准方案(平均分配)

    base_per = total_inv // 3

    remainder = total_inv - base_per * 3

    baseline = {

        "ecom": base_per + (1 if remainder > 0 else 0),

        "store": base_per + (1 if remainder > 1 else 0),

        "private": total_inv - (base_per * 2 + min(remainder, 2)),

        "profit": 0.0, # will be calculated below

    }

    baseline["profit"] = sum(

        profiles[ch].profit_per_unit(

            int(baseline[ch] * sell_through[ch])

        )

        for ch in ["ecom", "store", "private"]

    )

 

    # 利润拆解

    breakdown = profit_contribution_breakdown(

        {

            "ecom": result["best_ecom"],

            "store": result["best_store"],

            "private": result["best_private"],

        },

        profiles,

        sell_through

    )

 

    # 输出

    print_optimal_allocation(result, total_inv)

    print_profit_breakdown(breakdown, result["max_profit"])

    print_comparison_with_baseline(result, baseline, total_inv)

 

6️⃣ 配置文件 

"config/allocation.yaml"

 

# 全渠道库存分配配置

 

inventory:

  total_units: 3000 # 本季新品总库存

 

product:

  unit_cost: 120 # 单件生产成本(元)

 

price_overrides:

  ecom: 279.0 # 电商促销价

  store: 459.0 # 门店正价

  private: 349.0 # 私域会员价

 

platform_fee_rates:

  ecom: 0.10 # 电商平台扣点 10%

  store: 0.00 # 门店无平台扣点

  private: 0.03 # 小程序手续费 3%

 

sell_through_rates:

  ecom: 0.88 # 电商售罄率 88%(流量大)

  store: 0.55 # 门店售罄率 55%(客流有限)

  private: 0.72 # 私域售罄率 72%(精准用户)

 

optimization:

  step: 50 # 遍历步长(调小→更精确,调大→更快)

 

六、README.md

 

# 线上线下多渠道利润分配与备货平衡算法

 

教学级全渠道库存优化工具,用于求解电商、实体店、私域三渠道的最优备货比例。

 

## 功能特点

 

- 三渠道独立利润模型(含平台扣点、门店摊销、私域低成本)  

- 遍历搜索法寻找净利润最大化分配方案  

- 自动校验门店最低陈列量、电商最大承载量等约束  

- 利润贡献拆解 + 优化方案 vs 基准方案对比  

 

## 使用方法

 

bash

 

pip install pyyaml

 

python main.py

 

 

## 适用对象

 

- 时尚产业与品牌创新课程(全渠道战略模块)  

- 零售供应链管理教学案例  

- 利润优化与资源分配建模练习  

 

七、USAGE.md(使用说明)

 

# 使用说明

 

## 核心参数说明

 

### inventory

- total_units:季度总可用库存(件)

 

### product

- unit_cost:单件生产成本(含面料+加工+物流)

 

### price_overrides

- 各渠道售价,通常:门店 > 私域 > 电商

 

### platform_fee_rates

- 电商:平台扣点(通常 5%~15%)  

- 门店:0(自有渠道)  

- 私域:小程序手续费(通常 1%~3%)

 

### sell_through_rates(关键变量)

- 电商:高(0.80~0.95),流量驱动  

- 门店:中低(0.40~0.65),受选址影响大  

- 私域:中高(0.60~0.80),精准触达  

 

### optimization

- step:遍历步长,默认 50 件,可调至 10 提高精度

 

## 典型实验

 

### 实验 1:售罄率敏感性

- 将门店 sell_through 从 0.40 逐步调至 0.70  

- 观察门店库存占比的变化趋势

 

### 实验 2:平台扣点冲击

- 将电商 platform_fee_rate 从 0.05 调至 0.20  

- 分析电商利润贡献是否被压缩

 

### 实验 3:总库存弹性

- 将 total_units 从 1000 调到 10000  

- 观察最优比例是否收敛到某个"黄金比例"

 

## 输出解读

 

- 最优分配:净利润最大化的备货比例  

- 利润贡献拆解:哪个渠道是"利润奶牛"  

- 基准对比:优化方案比平均分配多赚多少  

 

八、核心知识点卡片

 

┌──────────────────────────────────────┐

│ 全渠道利润模型 │

│ 每个渠道有独立的成本结构和售价体系 │

│ 不能"一刀切"地比较 │

└──────────────────────────────────────┘

 

┌──────────────────────────────────────┐

│ 约束优化(Constraint Optimization) │

│ 在总量守恒 + 业务约束下寻找最优解 │

│ 是运筹学的核心问题 │

└──────────────────────────────────────┘

 

┌──────────────────────────────────────┐

│ 遍历搜索法 │

│ 小变量空间下最直观的优化方法 │

│ 教学价值高,工程可用 SciPy 替代 │

└──────────────────────────────────────┘

 

┌──────────────────────────────────────┐

│ 售罄率是分配的核心杠杆 │

│ 同样 1000 件,售罄率差 20% │

│ 利润差距可能是倍数级的 │

└──────────────────────────────────────┘

 

九、总结(中立立场)

 

本程序构建了一个结构化的全渠道库存分配决策框架:

 

- 核心洞察 1:电商虽然量大,但平台扣点会侵蚀利润;门店虽然售罄率低,但单件贡献毛利往往最高

- 核心洞察 2:售罄率(sell_through_rate)是影响分配方案的最敏感变量——提高门店 10% 售罄率,可能比多备 200 件更有价值

- 核心洞察 3:优化分配比平均分配通常能带来 5%~25% 的利润提升,差距来自"把货放到能卖出去的地方"

 

需要强调的是:

 

- 本模型为教学简化,未考虑跨渠道库存调拨、退货率、季节波动

- 售罄率需结合历史数据或行业报告审慎估算

- 遍历步长(step)影响精度与速度的平衡

 

该程序适合用于:

 

- 时尚产业与品牌创新课程中的全渠道战略与供应链管理模块

- 零售利润优化与资源分配的教学案例

- Python 优化算法与约束建模的编程练习

 

如需进一步扩展,可加入:

 

- 线性规划求解器(SciPy optimize)替代遍历,提升效率

- 跨渠道转移成本模型(如门店→电商退货的物流损耗)

- 多品类并行优化(不同品类有不同最优比例)

- Matplotlib 可视化(利润热力图:电商占比 × 门店占比 → 利润)

 

利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐