PHPackages                             huage/flexible-billing-engine - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. huage/flexible-billing-engine

ActiveLibrary[Payment Processing](/categories/payments)

huage/flexible-billing-engine
=============================

灵活计费引擎 - 支持多层级条件判断、公式计算、规则匹配

v1.0.1(5mo ago)10MITPHPPHP &gt;=7.4CI passing

Since Jan 22Pushed 5mo agoCompare

[ Source](https://github.com/gaorunhua/fee-billing-engine)[ Packagist](https://packagist.org/packages/huage/flexible-billing-engine)[ RSS](/packages/huage-flexible-billing-engine/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (2)Versions (2)Used By (0)

composer require huage/flexible-billing-engine:dev-main

计费引擎完整功能说明文档
============

[](#计费引擎完整功能说明文档)

一、功能概述
------

[](#一功能概述)

本计费引擎是一个灵活的、可扩展的 PHP 计费系统，支持：

- 单条计费：计算单个订单/地址的费用
- 批量计费：批量计算多个地址的费用，支持组合超重、拦截、超SKU等复杂场景
- 多种计费场景：配比费用、组合价格、超重、超箱、超SKU等
- 灵活的条件判断：支持多种运算符和公式计算

二、核心架构
------

[](#二核心架构)

### 1. 单条计费流程

[](#1-单条计费流程)

```
公共参数 + 地址参数 → RuleEngine → BillingRule → 条件匹配 → BillingResult

```

### 2. 批量计费流程

[](#2-批量计费流程)

```
公共参数 → BatchBillingEngine → 一批地址参数 → 批量计算 → 多个 BatchBillingResult

```

三、支持的条件类型
---------

[](#三支持的条件类型)

### 1. 公共条件（CommonCondition）

[](#1-公共条件commoncondition)

用于过滤规则，只有满足所有公共条件的规则才会被评估。

**支持的运算符**：

- `==`, `!=`, `>`, `=`, ` 'address_group', 'operator' => 'in', 'value' => [1, 2, 3, '标准地址', '特殊地址']]
    ],
    200,
    '提拆',
    ...
);

// 地址参数1：address_id = 1，匹配成功（1 在 address_group 中）
$address1 = ['address_id' => 1, '托盘' => 5];

// 地址参数2：地址类型 = '标准地址'，匹配成功（'标准地址' 在 address_group 中）
$address2 = ['地址类型' => '标准地址', '托盘' => 5];

// 地址参数3：address_id = 5，地址类型 = '其他类型'，不匹配（都不在 address_group 中）
$address3 = ['address_id' => 5, '地址类型' => '其他类型', '托盘' => 5];
```

**变量配置**：

- `name`：变量名
- `precision`：精度（如 0.5、1），自动向上取整
- `min`：最小值
- `formula`：变量公式

**内置公式**：

- `BUILTIN_VAR1_DIV_VAR2_MUL_PRICE`：变量1 / 变量2 \* 单价

### 5. 超重条件（OverweightCondition）

[](#5-超重条件overweightcondition)

根据重量和板数计算超重费用。

**判断公式**：`(重量 * 2.2046 - 阈值 * 板数) > 0`

**计算逻辑**：

- 超重值 = `(重量 * 2.2046 - 阈值 * 板数)`
- 倍数 = `ceil(超重值 / 500) * 0.5`，最小为 1
- 费用 = `倍数 * 单价`

**批量模式下的两种处理方式**：

#### 模式1：单条超重模式（默认）

[](#模式1单条超重模式默认)

- 每条费用如果判定超重，除了返回组合单价，还要返回超重费用
- 费用备注会写清楚超重计算方式
- 示例： ```
    费用项1：组合价格 1000 CNY
    费用项2：超重费用 200 CNY
    备注：超重计算方式：(重量500 * 2.2046 - 阈值1000 * 板数1) = 102.3，倍数 = ceil(102.3 / 500) * 0.5 = 1

    ```

#### 模式2：组合超重模式

[](#模式2组合超重模式)

- 当获得了一批超重费用时，如果公共参数传的是组合超重模式
- 不再返回单个费用的超重费用，而是返回一个组合超重费用
- 组合的超重费用计算：把组合的全部重量加起来，然后计算超重费用
- 费用备注会写清楚超重计算方式
- 组合超重费用会添加到第一个地址的结果中
- 示例： ```
    地址1费用项：组合价格 1000 CNY
    地址2费用项：组合价格 1200 CNY
    地址1费用项（追加）：组合超重费用 300 CNY
    备注：组合超重计算方式：(总重量1000 * 2.2046 - 阈值1000 * 总板数2) = 1204.6，倍数 = ceil(1204.6 / 500) * 0.5 = 1.5

    ```

### 6. 超箱条件（OverContainerCondition）

[](#6-超箱条件overcontainercondition)

根据箱数计算超箱费用。

**判断公式**：`箱数 - 阈值 > 0`

**计算逻辑**：

- 超箱值 = `箱数 - 阈值`
- 计数 = `ceil(超箱值 / 500)`
- 费用 = `计数 * 单价`

### 7. 超SKU条件（OverSkuCondition）

[](#7-超sku条件overskucondition)

根据SKU数计算超SKU费用。

**判断公式**：`sku数 - 阈值 > 0`

**计算逻辑**：

- 超SKU值 = `sku数 - 阈值`
- 费用 = `超SKU值 * 单价`

**批量模式下的处理**：

- 如果传入的公共参数里有"整柜提拆SKU数"满足条件：
    - 返回固定费用（`整柜提拆SKU费用`）
    - 然后所有地址都走散板匹配逻辑
    - 但只有地址类型是"散板"的才走散板费用
- 如果设置了"超SKU阈值"和"超SKU单价"：
    - 如果总SKU数超过阈值，返回费用：`(总SKU数 - 超SKU阈值) * 超SKU单价`

四、批量计费功能
--------

[](#四批量计费功能)

### 1. BatchBillingEngine（批量计费引擎）

[](#1-batchbillingengine批量计费引擎)

**功能**：

- 支持传入公共参数和一批地址参数
- 返回多个费用结果（每个地址一个结果）
- 支持组合超重模式
- 支持拦截处理
- 支持超SKU处理

### 2. 使用流程

[](#2-使用流程)

```
// 1. 创建批量计费引擎
$batchEngine = new BatchBillingEngine();

// 2. 设置公共参数
$commonContext = new BillingContext([
    'user_id' => 1,
    'container_type' => '40HQ',
    '组合超重模式' => false,  // true=组合模式，false=单条模式
    '基础提拆费用' => 500,
    '超重阈值' => 1000,
    '超重单位' => 500,
    '超重单价' => 200,
    '整柜提拆SKU数' => 10,      // 可选：超过10个SKU触发整柜提拆逻辑
    '整柜提拆SKU费用' => 500,    // 可选：整柜提拆SKU时返回的固定费用
    '超SKU规则' => [             // 可选：超SKU规则数组
        ['threshold' => 8, 'price' => 50],   // 超过8个SKU，单价50
        ['threshold' => 15, 'price' => 100], // 超过15个SKU，单价100
    ],
    '地址价格' => [       // 提拆派类型需要
        1 => 200,
        2 => 250,
    ],
    'currency' => 'CNY'
]);
$batchEngine->setCommonContext($commonContext);

// 3. 设置规则（与单条计费相同）
$rule = new BillingRule('rule_001', '综合规则', 10);
// ... 添加各种条件
$batchEngine->getRuleEngine()->addRule($rule);

// 4. 批量计算
$addressList = [
    ['address_id' => 1, 'address_group' => '组1', '重量' => 500, '板数' => 1, '托盘' => 5],
    ['address_id' => 2, 'address_group' => '组2', '重量' => 600, '板数' => 1, '托盘' => 6],
];
$results = $batchEngine->calculateBatch($addressList);

// 5. 处理结果
foreach ($results as $index => $result) {
    foreach ($result->getItems() as $item) {
        echo "{$item->getFeeType()}: {$item->getAmount()} {$item->getCurrency()}\n";
        if ($item->getRemark()) {
            echo "  备注: {$item->getRemark()}\n";
        }
    }
}
```

### 3. 费用项结构（FeeItem）

[](#3-费用项结构feeitem)

每个费用项包含：

- `amount`：费用金额
- `currency`：币种
- `remark`：备注（用于说明计算方式）
- `feeType`：费用类型

### 4. 批量结果结构（BatchBillingResult）

[](#4-批量结果结构batchbillingresult)

每个批量结果包含：

- `items`：费用项列表（可能多个）
- `totalAmount`：总金额
- `currency`：币种
- `matchedRule`：匹配的规则

五、特殊场景处理
--------

[](#五特殊场景处理)

### 1. 拦截处理

[](#1-拦截处理)

**触发条件**：在获取组合费用时判定是拦截

**处理逻辑**：

- 按照基础提拆费用来计算费用
- 同时返回一条空的费用，备注是"拦截后续费用"

**示例**：

```
费用项1：拦截基础提拆费用 500 CNY，备注：拦截：按照基础提拆费用计算
费用项2：空费用 0 CNY，备注：拦截后续费用

```

### 2. 整柜提拆SKU数处理

[](#2-整柜提拆sku数处理)

**触发条件**：如果传入的公共参数里有"整柜提拆SKU数"满足条件

**处理逻辑**：

- 计算所有地址的总SKU数
- 如果总SKU数超过"整柜提拆SKU数"：
    - 返回固定费用（`整柜提拆SKU费用`）
    - 然后所有地址都走散板匹配逻辑
    - 但只有地址类型是"散板"的才走散板费用

**公共参数配置**：

```
$commonContext->set('整柜提拆SKU数', 10);      // 超过10个SKU触发
$commonContext->set('整柜提拆SKU费用', 500);    // 返回的固定费用
```

**示例**：

```
地址1：整柜提拆SKU费用 500 CNY（地址类型不是散板，不走散板费用）
地址2：整柜提拆SKU费用 500 CNY + 散板费用 300 CNY（地址类型是散板）
地址3：整柜提拆SKU费用 500 CNY（地址类型不是散板，不走散板费用）

```

### 3. 超SKU规则处理

[](#3-超sku规则处理)

**触发条件**：如果设置了"超SKU规则"（数组格式）

**处理逻辑**：

- 计算所有地址的总SKU数
- 遍历所有超SKU规则，如果总SKU数超过某个规则的阈值：
    - 计算该规则的费用：`(总SKU数 - 阈值) * 单价`
    - 累加所有匹配规则的费用
    - 费用添加到第一个地址的结果中

**公共参数配置**：

```
$commonContext->set('超SKU规则', [
    ['threshold' => 8, 'price' => 50],   // 超过8个SKU，单价50
    ['threshold' => 15, 'price' => 100], // 超过15个SKU，单价100
    // 或者使用中文键名
    ['超SKU阈值' => 20, '超SKU单价' => 150], // 超过20个SKU，单价150
]);
```

**示例**：

```
总SKU数 = 25
规则1：threshold = 8, price = 50，匹配，费用 = (25 - 8) * 50 = 850
规则2：threshold = 15, price = 100，匹配，费用 = (25 - 15) * 100 = 1000
规则3：threshold = 20, price = 150，匹配，费用 = (25 - 20) * 150 = 750
总超SKU费用 = 850 + 1000 + 750 = 2600 CNY
添加到第一个地址的结果中

```

### 3. 提拆派类型处理

[](#3-提拆派类型处理)

**特点**：

- 需要匹配地址价格
- 公共参数需要传入地址价格映射
- 匹配成功后返回：`地址价格 * 变量(托盘/体积)`
- 如果没有匹配上：
    - 检查地址类型
    - 如果是"散板"，抛出异常
    - 如果不是"散板"，使用默认单价

**示例**：

```
// 公共参数
$commonContext->set('地址价格', [
    1 => 200,  // 地址1价格200
    2 => 250,  // 地址2价格250
]);

// 地址参数
$address = [
    'address_id' => 1,
    '托盘' => 5
];

// 结果：200 * 5 = 1000 CNY
```

### 4. 私人地址处理

[](#4-私人地址处理)

**触发条件**：组合价格计算时，地址类型是"私人地址"

**处理逻辑**：

- 返回组合价格
- 同时返回一个空价格，备注是"详见后续账单"

**示例**：

```
费用项1：组合价格 1000 CNY
费用项2：空费用 0 CNY，备注：详见后续账单

```

六、公共参数说明
--------

[](#六公共参数说明)

**重要说明**：

- **必填参数**：只有 `currency`（币种）是必填的
- **可选参数**：其他所有参数都是可选的，根据不同的计费场景来设置
- **场景参数**：每个场景需要设置相应的参数，不需要的场景可以不设置
- **最小配置示例**： ```
    $commonContext = new BillingContext([
        'currency' => 'CNY',  // 只有币种是必填的
    ]);
    ```

参数名类型说明必填默认值使用场景`currency`string币种**是**CNY所有场景`组合超重模式`booltrue=组合模式，false=单条模式否false组合超重场景`基础提拆费用`float拦截时使用的基础费用否0拦截场景`超重阈值`float超重判断阈值否1000超重场景`超重单位`float每多少单位增加0.5否500超重场景`超重单价`float超重单价否0超重场景`整柜提拆SKU数`int超过几个SKU触发整柜提拆逻辑否-整柜提拆SKU场景`整柜提拆SKU费用`float整柜提拆SKU时返回的固定费用否-整柜提拆SKU场景`超SKU规则`array超SKU规则数组，格式：`[['threshold' => 8, 'price' => 50], ...]`否\[\]超SKU场景`地址价格`array地址价格映射（提拆派类型需要）否\[\]提拆派场景七、地址参数说明
--------

[](#七地址参数说明)

参数名类型说明必填`address_id`int/string地址ID（提拆派类型需要）是（提拆派）`address_name`string地址名称（提拆派类型备用）否`address_group`string地址组（用于条件匹配）是（条件匹配）`地址类型`string地址类型（如：标准地址、私人地址、散板）否`重量`float重量（超重场景需要）是（超重）`板数`float板数（超重场景需要）是（超重）`托盘`float托盘数（组合价格需要）是（组合价格）`sku数`intSKU数（超SKU场景需要）是（超SKU）八、费用类型说明
--------

[](#八费用类型说明)

费用类型说明`combination`组合价格`overweight`超重费用（单条模式）`combined_overweight`组合超重费用（组合模式）`intercept_base_fee`拦截基础提拆费用`intercept_follow`拦截后续费用（空费用）`base_fee`基础提拆费用（超SKU时使用）`private_address`私人地址后续账单（空费用）`over_container`超箱费用`over_sku`超SKU费用`ratio`配比费用九、完整使用示例
--------

[](#九完整使用示例)

### 示例1：单条计费

[](#示例1单条计费)

```
use FlexibleBilling\Rule\RuleEngine;
use FlexibleBilling\Rule\BillingRule;
use FlexibleBilling\Condition\CommonCondition;
use FlexibleBilling\Condition\CombinationCondition;
use FlexibleBilling\Context\BillingContext;

$engine = new RuleEngine();
$rule = new BillingRule('rule_001', '组合价格规则', 10);
$rule->addCommonCondition(new CommonCondition('user_id', '==', 1));

$combination = new CombinationCondition(
    [['field' => 'address_group', 'operator' => '==', 'value' => '组1']],
    200,
    '提拆',
    [['name' => '托盘', 'precision' => 1, 'min' => 1]],
    '{单价} * {托盘}',
    ''
);
$rule->addCombinationCondition($combination);

$engine->addRule($rule);

$context = new BillingContext([
    'user_id' => 1,
    'address_group' => '组1',
    '托盘' => 5
]);

$result = $engine->calculate($context);
echo "费用: {$result->getAmount()} {$result->getCurrency()}\n";
```

### 示例2：批量计费（组合超重模式）

[](#示例2批量计费组合超重模式)

```
use FlexibleBilling\Context\BillingContext;
use FlexibleBilling\Rule\BatchBillingEngine;
use FlexibleBilling\Rule\BillingRule;
use FlexibleBilling\Condition\CombinationCondition;

$batchEngine = new BatchBillingEngine();

// 设置公共参数
$commonContext = new BillingContext([
    'user_id' => 1,
    'container_type' => '40HQ',
    '组合超重模式' => true,  // 组合超重模式
    '基础提拆费用' => 500,
    '超重阈值' => 1000,
    '超重单位' => 500,
    '超重单价' => 200,
    'currency' => 'CNY'
]);
$batchEngine->setCommonContext($commonContext);

// 设置规则
$rule = new BillingRule('rule_001', '组合价格规则', 10);
$rule->addCommonCondition(new CommonCondition('user_id', '==', 1));

$combination = new CombinationCondition(
    [['field' => 'address_group', 'operator' => '==', 'value' => '组1']],
    200,
    '提拆',
    [['name' => '托盘', 'precision' => 1, 'min' => 1]],
    '{单价} * {托盘}',
    ''
);
$rule->addCombinationCondition($combination);

$batchEngine->getRuleEngine()->addRule($rule);

// 批量计算
$addressList = [
    [
        'address_group' => '组1',
        '重量' => 500,
        '板数' => 1,
        '托盘' => 5
    ],
    [
        'address_group' => '组1',
        '重量' => 600,
        '板数' => 1,
        '托盘' => 6
    ]
];

$results = $batchEngine->calculateBatch($addressList);

// 处理结果
foreach ($results as $index => $result) {
    echo "地址" . ($index + 1) . ":\n";
    foreach ($result->getItems() as $item) {
        echo "  {$item->getFeeType()}: {$item->getAmount()} {$item->getCurrency()}\n";
        if ($item->getRemark()) {
            echo "    备注: {$item->getRemark()}\n";
        }
    }
    echo "  总计: {$result->getTotalAmount()} {$result->getCurrency()}\n\n";
}
```

### 示例3：提拆派类型

[](#示例3提拆派类型)

```
// 设置公共参数（包含地址价格映射）
$commonContext = new BillingContext([
    'user_id' => 1,
    '地址价格' => [
        1 => 200,  // 地址1价格200
        2 => 250,  // 地址2价格250
    ],
    'currency' => 'CNY'
]);

// 设置规则（提拆派类型）
$combination = new CombinationCondition(
    [['field' => 'address_group', 'operator' => '==', 'value' => '组2']],
    200,  // 默认单价（如果地址未匹配且不是散板，使用此单价）
    '提拆派',  // 类型
    [['name' => '托盘', 'precision' => 1, 'min' => 1]],
    '',
    ''
);

// 地址参数
$address = [
    'address_id' => 1,
    'address_group' => '组2',
    '地址类型' => '标准地址',
    '托盘' => 5
];

// 结果：200 * 5 = 1000 CNY（地址1价格200）
```

十、功能总结
------

[](#十功能总结)

### 已实现的功能

[](#已实现的功能)

✅ **单条计费**：支持单个订单/地址的费用计算 ✅ **批量计费**：支持批量计算多个地址的费用 ✅ **组合超重**：支持两种模式（单条模式、组合模式） ✅ **拦截处理**：支持拦截时返回基础提拆费用 + 空费用 ✅ **超SKU处理**：支持组合模式下的超SKU处理 ✅ **提拆派类型**：支持地址价格匹配，散板异常处理 ✅ **私人地址**：支持私人地址时返回空费用备注 ✅ **费用备注**：超重费用包含详细的计算方式说明 ✅ **变量验证**：自定义公式和内置公式的变量验证 ✅ **多费用项**：支持一个地址返回多个费用项

### 设计特点

[](#设计特点)

- **灵活扩展**：支持自定义运算符、自定义公式
- **类型安全**：变量验证机制，防止计算错误
- **易于使用**：清晰的API，丰富的示例
- **高性能**：短路评估，按优先级排序
- **批量处理**：支持批量计算，提高效率

十一、匹配流程总结
---------

[](#十一匹配流程总结)

根据需求，匹配流程应该是：

```
1. 传入公共参数
2. 设置各种组合的价格信息（规则配置）
3. 设置一批地址参数
4. 最后执行获取一堆价格（批量计算）

```

**实现方式**：

```
// 1. 传入公共参数
$commonContext = new BillingContext([...公共参数...]);

// 2. 设置各种组合的价格信息（规则配置）
$rule = new BillingRule(...);
$rule->addCombinationCondition(...);
$batchEngine->getRuleEngine()->addRule($rule);

// 3. 设置一批地址参数
$addressList = [
    [...地址1参数...],
    [...地址2参数...],
    ...
];

// 4. 最后执行获取一堆价格
$results = $batchEngine->calculateBatch($addressList);
```

十二、注意事项
-------

[](#十二注意事项)

1. **组合超重模式**：如果启用，所有地址的超重费用会合并为一个组合超重费用，添加到第一个地址的结果中
2. **拦截处理**：拦截时使用基础提拆费用，不再计算其他费用
3. **超SKU处理**：如果总SKU数超过阈值，所有地址都返回基础提拆费用
4. **提拆派类型**：必须配置地址价格映射，否则无法匹配会抛出异常（如果地址类型是散板）
5. **私人地址**：会自动添加"详见后续账单"的空费用项
6. **费用备注**：超重费用会包含详细的计算方式说明
7. **变量验证**：使用自定义公式时，确保所有变量都已配置或存在于上下文中

###  Health Score

30

—

LowBetter than 62% of packages

Maintenance72

Regular maintenance activity

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity35

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

162d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/23747966?v=4)[gaorunhua](/maintainers/gaorunhua)[@gaorunhua](https://github.com/gaorunhua)

---

Top Contributors

[![gaorunhua](https://avatars.githubusercontent.com/u/23747966?v=4)](https://github.com/gaorunhua "gaorunhua (1 commits)")

---

Tags

billingformulaconditionpricingrule-engine

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/huage-flexible-billing-engine/health.svg)

```
[![Health](https://phpackages.com/badges/huage-flexible-billing-engine/health.svg)](https://phpackages.com/packages/huage-flexible-billing-engine)
```

###  Alternatives

[rcsofttech/audit-trail-bundle

Enterprise-grade, high-performance Symfony audit trail bundle. Automatically track Doctrine entity changes with split-phase architecture, multiple transports (HTTP, Queue, Doctrine), and sensitive data masking.

1189.8k](/packages/rcsofttech-audit-trail-bundle)[lemonsqueezy/laravel

A package to easily integrate your Laravel application with Lemon Squeezy.

590128.3k](/packages/lemonsqueezy-laravel)[mollie/laravel-cashier-mollie

Laravel Cashier provides an expressive, fluent interface to Mollie's subscription billing services.

178204.3k1](/packages/mollie-laravel-cashier-mollie)[lanos/laravel-cashier-stripe-connect

Adds Stripe Connect functionality to Laravel's main billing package, Cashier.

85165.4k](/packages/lanos-laravel-cashier-stripe-connect)[expdev07/laravel-cashier-stripe-connect

Adds Stripe Connect functionality to Laravel's main billing package, Cashier.

65141.6k](/packages/expdev07-laravel-cashier-stripe-connect)[danestves/laravel-polar

A package to easily integrate your Laravel application with Polar.sh

8120.4k](/packages/danestves-laravel-polar)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
