实现 IoT SIM 管理模块数据模型和数据库结构
- 添加 IoT 核心业务表:运营商、IoT 卡、设备、号卡、套餐、订单等 - 添加分佣系统表:分佣规则、分佣记录、运营商结算等 - 添加轮询和流量管理表:轮询配置、流量使用记录等 - 添加财务和系统管理表:佣金提现、换卡申请等 - 实现完整的 GORM 模型和常量定义 - 添加数据库迁移脚本和详细文档 - 集成 OpenSpec 工作流工具(opsx 命令和 skills) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
940
docs/iot-sim-management/分佣系统说明.md
Normal file
940
docs/iot-sim-management/分佣系统说明.md
Normal file
@@ -0,0 +1,940 @@
|
||||
# IoT SIM 管理系统 - 分佣系统说明
|
||||
|
||||
## 概述
|
||||
|
||||
IoT SIM 管理系统实现了一套灵活的多级代理分佣体系,支持三种分佣模式(一次性分佣、长期分佣、组合分佣),支持阶梯奖励机制,支持自动解冻和手动审批,支持 OR 条件解冻逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 分佣架构
|
||||
|
||||
### 多级代理树形结构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 平台(Platform) │
|
||||
│ Level 0 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌─────────────────┼─────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ 一级代理 A │ │ 一级代理 B │ │ 一级代理 C │
|
||||
│ Level 1 │ │ Level 1 │ │ Level 1 │
|
||||
│ Path: /A/ │ │ Path: /B/ │ │ Path: /C/ │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘
|
||||
│ │
|
||||
├────┬────┐ ├────┬────┐
|
||||
▼ ▼ ▼ ▼ ▼ ▼
|
||||
┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐
|
||||
│A-1 │ │A-2 │ │A-3 │ │B-1 │ │B-2 │ │B-3 │
|
||||
│L2 │ │L2 │ │L2 │ │L2 │ │L2 │ │L2 │
|
||||
└────┘ └────┘ └────┘ └────┘ └────┘ └────┘
|
||||
```
|
||||
|
||||
**代理层级关系表**: `agent_hierarchies`
|
||||
|
||||
---
|
||||
|
||||
## 三种分佣模式
|
||||
|
||||
### 1. 一次性分佣 (One-time Commission)
|
||||
|
||||
**特点**: 订单完成后立即发放佣金
|
||||
|
||||
**适用场景**:
|
||||
- 首次激活奖励
|
||||
- 推广奖励
|
||||
- 快速返佣
|
||||
|
||||
**示例**:
|
||||
```
|
||||
用户购买套餐 → 订单完成 → 立即发放佣金给上级代理
|
||||
```
|
||||
|
||||
**配置示例**:
|
||||
```sql
|
||||
INSERT INTO commission_rules (
|
||||
rule_name,
|
||||
rule_type,
|
||||
package_series_id,
|
||||
commission_type,
|
||||
commission_value,
|
||||
status
|
||||
) VALUES (
|
||||
'一次性分佣-套餐激活奖励',
|
||||
'one_time',
|
||||
1, -- 套餐系列 ID
|
||||
'fixed', -- 固定金额
|
||||
10.00, -- 10元
|
||||
1 -- 启用
|
||||
);
|
||||
```
|
||||
|
||||
**业务流程**:
|
||||
```
|
||||
订单创建 → 订单支付 → 订单完成
|
||||
│
|
||||
└─→ 创建分佣记录 (status=1 待发放)
|
||||
│
|
||||
└─→ 自动发放 (status=2 已发放)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 长期分佣 (Long-term Commission)
|
||||
|
||||
**特点**: 订单完成后冻结佣金,满足解冻条件后发放
|
||||
|
||||
**适用场景**:
|
||||
- 续费奖励
|
||||
- 留存奖励
|
||||
- 长期激励
|
||||
|
||||
**解冻条件**:
|
||||
- **时间条件**: 冻结 N 天后自动解冻
|
||||
- **流量条件**: IoT 卡累计使用 M MB 流量后解冻
|
||||
- **OR 逻辑**: 时间到期 **OR** 流量达标,满足任一条件即可解冻
|
||||
|
||||
**示例**:
|
||||
```
|
||||
用户购买套餐 → 订单完成 → 冻结佣金 (30天或1GB流量)
|
||||
↓
|
||||
时间到期 OR 流量达标
|
||||
↓
|
||||
自动解冻发放
|
||||
```
|
||||
|
||||
**配置示例**:
|
||||
```sql
|
||||
INSERT INTO commission_rules (
|
||||
rule_name,
|
||||
rule_type,
|
||||
package_series_id,
|
||||
commission_type,
|
||||
commission_value,
|
||||
freeze_days,
|
||||
freeze_data_mb,
|
||||
unfreeze_mode,
|
||||
status
|
||||
) VALUES (
|
||||
'长期分佣-续费奖励',
|
||||
'long_term',
|
||||
1,
|
||||
'percentage', -- 百分比
|
||||
0.10, -- 10%
|
||||
30, -- 冻结30天
|
||||
1024, -- 或使用1GB流量
|
||||
'auto', -- 自动解冻
|
||||
1
|
||||
);
|
||||
```
|
||||
|
||||
**业务流程**:
|
||||
```
|
||||
订单创建 → 订单支付 → 订单完成
|
||||
│
|
||||
└─→ 创建分佣记录 (status=3 已冻结)
|
||||
│
|
||||
├─→ 时间检查: 30天后 → 自动解冻 (status=2 已发放)
|
||||
│
|
||||
└─→ 流量检查: 使用1GB流量后 → 自动解冻 (status=2 已发放)
|
||||
```
|
||||
|
||||
**解冻条件数据结构**:
|
||||
```json
|
||||
{
|
||||
"time_based": {
|
||||
"days": 30,
|
||||
"deadline": "2025-02-10T00:00:00Z"
|
||||
},
|
||||
"data_based": {
|
||||
"data_mb": 1024,
|
||||
"iot_card_id": 12345
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 组合分佣 (Combined Commission)
|
||||
|
||||
**特点**: 同时包含一次性分佣和长期分佣,订单完成后部分立即发放,部分冻结
|
||||
|
||||
**适用场景**:
|
||||
- 首充奖励(立即发放) + 留存奖励(冻结发放)
|
||||
- 灵活激励机制
|
||||
|
||||
**示例**:
|
||||
```
|
||||
用户购买套餐 → 订单完成 → 立即发放 5元 + 冻结 10元 (30天后发放)
|
||||
```
|
||||
|
||||
**配置示例**:
|
||||
```sql
|
||||
-- 1. 创建组合分佣规则
|
||||
INSERT INTO commission_rules (
|
||||
rule_name,
|
||||
rule_type,
|
||||
package_series_id,
|
||||
status
|
||||
) VALUES (
|
||||
'组合分佣-首充+留存',
|
||||
'combined',
|
||||
1,
|
||||
1
|
||||
);
|
||||
|
||||
-- 2. 配置一次性条件
|
||||
INSERT INTO commission_combined_conditions (
|
||||
rule_id,
|
||||
condition_type,
|
||||
commission_type,
|
||||
commission_value
|
||||
) VALUES (
|
||||
1, -- 上面创建的规则 ID
|
||||
'one_time',
|
||||
'fixed',
|
||||
5.00 -- 立即发放 5元
|
||||
);
|
||||
|
||||
-- 3. 配置长期条件
|
||||
INSERT INTO commission_combined_conditions (
|
||||
rule_id,
|
||||
condition_type,
|
||||
commission_type,
|
||||
commission_value,
|
||||
freeze_days,
|
||||
freeze_data_mb
|
||||
) VALUES (
|
||||
1,
|
||||
'long_term',
|
||||
'fixed',
|
||||
10.00, -- 冻结 10元
|
||||
30, -- 30天
|
||||
1024 -- 或1GB流量
|
||||
);
|
||||
```
|
||||
|
||||
**业务流程**:
|
||||
```
|
||||
订单创建 → 订单支付 → 订单完成
|
||||
│
|
||||
├─→ 创建一次性分佣记录 (status=1 待发放) → 立即发放 (status=2)
|
||||
│
|
||||
└─→ 创建长期分佣记录 (status=3 已冻结) → 满足条件后解冻
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 阶梯奖励机制
|
||||
|
||||
### 阶梯奖励说明
|
||||
|
||||
阶梯奖励允许根据订单数量设置不同的分佣标准,订单数量越多,分佣越高。
|
||||
|
||||
**示例配置**:
|
||||
```
|
||||
1-10 单: 10元/单
|
||||
11-50 单: 15元/单
|
||||
51+ 单: 20元/单
|
||||
```
|
||||
|
||||
### 配置示例
|
||||
|
||||
```sql
|
||||
-- 1. 创建支持阶梯的分佣规则
|
||||
INSERT INTO commission_rules (
|
||||
rule_name,
|
||||
rule_type,
|
||||
package_series_id,
|
||||
enable_ladder,
|
||||
status
|
||||
) VALUES (
|
||||
'阶梯分佣-月度订单量',
|
||||
'one_time',
|
||||
1,
|
||||
true, -- 启用阶梯
|
||||
1
|
||||
);
|
||||
|
||||
-- 2. 配置阶梯奖励
|
||||
INSERT INTO commission_ladder (rule_id, min_quantity, max_quantity, commission_type, commission_value) VALUES
|
||||
(1, 1, 10, 'fixed', 10.00),
|
||||
(1, 11, 50, 'fixed', 15.00),
|
||||
(1, 51, NULL, 'fixed', 20.00); -- NULL 表示无上限
|
||||
```
|
||||
|
||||
### 阶梯计算逻辑
|
||||
|
||||
```go
|
||||
// 伪代码
|
||||
func CalculateLadderCommission(agentID uint, ruleID uint, currentMonth string) float64 {
|
||||
// 1. 查询阶梯配置
|
||||
ladders := db.FindCommissionLadders(ruleID)
|
||||
|
||||
// 2. 统计当月订单数量
|
||||
orderCount := db.CountOrders(agentID, currentMonth)
|
||||
|
||||
// 3. 匹配阶梯
|
||||
for _, ladder := range ladders {
|
||||
if orderCount >= ladder.MinQuantity &&
|
||||
(ladder.MaxQuantity == nil || orderCount <= ladder.MaxQuantity) {
|
||||
if ladder.CommissionType == "fixed" {
|
||||
return ladder.CommissionValue
|
||||
} else if ladder.CommissionType == "percentage" {
|
||||
orderAmount := db.GetOrderAmount(orderID)
|
||||
return orderAmount * ladder.CommissionValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 分佣计算方式
|
||||
|
||||
### 1. 固定金额 (Fixed)
|
||||
|
||||
**说明**: 每笔订单固定分佣 N 元
|
||||
|
||||
**示例**:
|
||||
```sql
|
||||
commission_type = 'fixed'
|
||||
commission_value = 10.00
|
||||
```
|
||||
|
||||
**计算公式**:
|
||||
```
|
||||
分佣金额 = commission_value = 10.00 元
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 百分比 (Percentage)
|
||||
|
||||
**说明**: 按订单金额的 N% 分佣
|
||||
|
||||
**示例**:
|
||||
```sql
|
||||
commission_type = 'percentage'
|
||||
commission_value = 0.10 -- 10%
|
||||
```
|
||||
|
||||
**计算公式**:
|
||||
```
|
||||
分佣金额 = 订单金额 × commission_value
|
||||
= 100.00 × 0.10
|
||||
= 10.00 元
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 分佣记录表
|
||||
|
||||
### 表结构: `commission_records`
|
||||
|
||||
分佣记录表记录每笔分佣的详细信息:
|
||||
|
||||
```sql
|
||||
CREATE TABLE commission_records (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
order_id BIGINT NOT NULL,
|
||||
agent_id BIGINT NOT NULL,
|
||||
rule_id BIGINT NOT NULL,
|
||||
commission_type VARCHAR(50) NOT NULL,
|
||||
commission_amount DECIMAL(10,2) NOT NULL,
|
||||
status INT NOT NULL DEFAULT 1,
|
||||
freeze_days INT DEFAULT 0,
|
||||
freeze_data_mb BIGINT DEFAULT 0,
|
||||
unfreeze_conditions JSONB,
|
||||
unfrozen_at TIMESTAMPTZ,
|
||||
distributed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 状态说明
|
||||
|
||||
| status | 状态 | 说明 |
|
||||
|--------|------|------|
|
||||
| 1 | 待发放 | 一次性分佣,等待发放 |
|
||||
| 2 | 已发放 | 已发放到代理账户 |
|
||||
| 3 | 已冻结 | 长期分佣,冻结中 |
|
||||
| 4 | 已取消 | 订单取消或退款,分佣取消 |
|
||||
|
||||
---
|
||||
|
||||
## OR 条件解冻逻辑
|
||||
|
||||
### 解冻条件设计
|
||||
|
||||
长期分佣支持 **OR 条件解冻**,即时间到期 **OR** 流量达标,满足任一条件即可自动解冻。
|
||||
|
||||
**解冻条件数据结构**:
|
||||
```json
|
||||
{
|
||||
"time_based": {
|
||||
"days": 30,
|
||||
"deadline": "2025-02-10T00:00:00Z"
|
||||
},
|
||||
"data_based": {
|
||||
"data_mb": 1024,
|
||||
"iot_card_id": 12345
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 解冻检查逻辑
|
||||
|
||||
```go
|
||||
// 伪代码
|
||||
func CheckUnfreezeConditions(record *CommissionRecord) bool {
|
||||
var conditions struct {
|
||||
TimeBased struct {
|
||||
Days int `json:"days"`
|
||||
Deadline time.Time `json:"deadline"`
|
||||
} `json:"time_based"`
|
||||
DataBased struct {
|
||||
DataMB int64 `json:"data_mb"`
|
||||
IotCardID uint `json:"iot_card_id"`
|
||||
} `json:"data_based"`
|
||||
}
|
||||
|
||||
json.Unmarshal(record.UnfreezeConditions, &conditions)
|
||||
|
||||
// 检查时间条件
|
||||
if time.Now().After(conditions.TimeBased.Deadline) {
|
||||
return true // 时间到期,可以解冻
|
||||
}
|
||||
|
||||
// 检查流量条件
|
||||
if conditions.DataBased.IotCardID > 0 {
|
||||
card := db.FindIotCardByID(conditions.DataBased.IotCardID)
|
||||
if card.DataUsageMB >= conditions.DataBased.DataMB {
|
||||
return true // 流量达标,可以解冻
|
||||
}
|
||||
}
|
||||
|
||||
return false // 条件均未满足
|
||||
}
|
||||
```
|
||||
|
||||
### 自动解冻定时任务
|
||||
|
||||
```go
|
||||
// 伪代码
|
||||
func UnfreezeCommissionTask() {
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
// 查询所有冻结中的分佣记录
|
||||
records := db.FindCommissionRecords("status = 3")
|
||||
|
||||
for _, record := range records {
|
||||
if CheckUnfreezeConditions(&record) {
|
||||
// 解冻
|
||||
record.Status = 2 // 已发放
|
||||
record.UnfrozenAt = time.Now()
|
||||
record.DistributedAt = time.Now()
|
||||
db.Save(&record)
|
||||
|
||||
// 发放到代理账户
|
||||
DistributeCommission(record.AgentID, record.CommissionAmount)
|
||||
|
||||
logger.Info("分佣解冻成功",
|
||||
zap.Uint("record_id", record.ID),
|
||||
zap.Uint("agent_id", record.AgentID),
|
||||
zap.Float64("amount", record.CommissionAmount),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 分佣审批流程
|
||||
|
||||
### 自动审批 vs 手动审批
|
||||
|
||||
分佣规则的 `unfreeze_mode` 字段控制解冻模式:
|
||||
|
||||
- **auto**: 自动解冻,满足条件后自动发放
|
||||
- **manual**: 手动审批,需要人工审核通过后才能发放
|
||||
|
||||
### 手动审批流程
|
||||
|
||||
```
|
||||
订单完成 → 创建分佣记录 (status=3 已冻结)
|
||||
↓
|
||||
满足解冻条件
|
||||
↓
|
||||
创建审批记录 (approval_status=1 待审批)
|
||||
↓
|
||||
审批人审核
|
||||
├─→ 通过 (approval_status=2) → 发放佣金 (status=2 已发放)
|
||||
└─→ 拒绝 (approval_status=3) → 取消分佣 (status=4 已取消)
|
||||
```
|
||||
|
||||
### 审批表: `commission_approvals`
|
||||
|
||||
```sql
|
||||
CREATE TABLE commission_approvals (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
commission_record_id BIGINT UNIQUE NOT NULL,
|
||||
agent_id BIGINT NOT NULL,
|
||||
approval_status INT NOT NULL DEFAULT 1,
|
||||
approver_id BIGINT,
|
||||
approval_reason TEXT,
|
||||
approved_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 审批状态
|
||||
|
||||
| approval_status | 状态 | 说明 |
|
||||
|----------------|------|------|
|
||||
| 1 | 待审批 | 等待审批人审核 |
|
||||
| 2 | 已通过 | 审批通过,发放佣金 |
|
||||
| 3 | 已拒绝 | 审批拒绝,取消分佣 |
|
||||
|
||||
---
|
||||
|
||||
## 分佣模板
|
||||
|
||||
### 模板设计
|
||||
|
||||
分佣模板用于快速创建分佣规则,避免重复配置。
|
||||
|
||||
**表结构**: `commission_templates`
|
||||
|
||||
```sql
|
||||
CREATE TABLE commission_templates (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
template_name VARCHAR(255) NOT NULL,
|
||||
template_data JSONB NOT NULL,
|
||||
description TEXT,
|
||||
status INT NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 模板数据格式
|
||||
|
||||
```json
|
||||
{
|
||||
"rule_type": "combined",
|
||||
"package_series_id": 1,
|
||||
"conditions": [
|
||||
{
|
||||
"condition_type": "one_time",
|
||||
"commission_type": "fixed",
|
||||
"commission_value": 5.00
|
||||
},
|
||||
{
|
||||
"condition_type": "long_term",
|
||||
"commission_type": "fixed",
|
||||
"commission_value": 10.00,
|
||||
"freeze_days": 30,
|
||||
"freeze_data_mb": 1024
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 使用模板创建规则
|
||||
|
||||
```go
|
||||
// 伪代码
|
||||
func CreateRuleFromTemplate(templateID uint, seriesID uint) error {
|
||||
template := db.FindTemplateByID(templateID)
|
||||
|
||||
var data struct {
|
||||
RuleType string `json:"rule_type"`
|
||||
PackageSeriesID uint `json:"package_series_id"`
|
||||
Conditions []struct {
|
||||
ConditionType string `json:"condition_type"`
|
||||
CommissionType string `json:"commission_type"`
|
||||
CommissionValue float64 `json:"commission_value"`
|
||||
FreezeDays int `json:"freeze_days"`
|
||||
FreezeDataMB int64 `json:"freeze_data_mb"`
|
||||
} `json:"conditions"`
|
||||
}
|
||||
|
||||
json.Unmarshal(template.TemplateData, &data)
|
||||
|
||||
// 创建分佣规则
|
||||
rule := CommissionRule{
|
||||
RuleName: template.TemplateName,
|
||||
RuleType: data.RuleType,
|
||||
PackageSeriesID: seriesID,
|
||||
Status: 1,
|
||||
}
|
||||
db.Create(&rule)
|
||||
|
||||
// 创建组合条件
|
||||
for _, cond := range data.Conditions {
|
||||
condition := CommissionCombinedCondition{
|
||||
RuleID: rule.ID,
|
||||
ConditionType: cond.ConditionType,
|
||||
CommissionType: cond.CommissionType,
|
||||
CommissionValue: cond.CommissionValue,
|
||||
FreezeDays: cond.FreezeDays,
|
||||
FreezeDataMB: cond.FreezeDataMB,
|
||||
}
|
||||
db.Create(&condition)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 运营商结算
|
||||
|
||||
### 结算表: `carrier_settlements`
|
||||
|
||||
记录与运营商的月度结算情况:
|
||||
|
||||
```sql
|
||||
CREATE TABLE carrier_settlements (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
carrier_id BIGINT NOT NULL,
|
||||
settlement_month VARCHAR(7) NOT NULL,
|
||||
total_orders INT DEFAULT 0,
|
||||
total_amount DECIMAL(10,2) DEFAULT 0,
|
||||
settlement_status INT NOT NULL DEFAULT 1,
|
||||
settled_at TIMESTAMPTZ,
|
||||
paid_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 结算状态
|
||||
|
||||
| settlement_status | 状态 | 说明 |
|
||||
|------------------|------|------|
|
||||
| 1 | 待结算 | 月度未结束 |
|
||||
| 2 | 已结算 | 已统计金额 |
|
||||
| 3 | 已支付 | 已支付给运营商 |
|
||||
|
||||
### 月度结算流程
|
||||
|
||||
```
|
||||
每月1号 → 统计上月订单数据
|
||||
↓
|
||||
创建结算记录 (settlement_status=1 待结算)
|
||||
↓
|
||||
财务审核
|
||||
↓
|
||||
确认结算 (settlement_status=2 已结算)
|
||||
↓
|
||||
支付运营商 (settlement_status=3 已支付)
|
||||
```
|
||||
|
||||
### 结算计算逻辑
|
||||
|
||||
```go
|
||||
// 伪代码
|
||||
func GenerateCarrierSettlement(carrierID uint, month string) error {
|
||||
// 1. 统计上月订单
|
||||
orders := db.FindOrders("carrier_id = ? AND DATE_FORMAT(completed_at, '%Y-%m') = ?", carrierID, month)
|
||||
|
||||
totalOrders := len(orders)
|
||||
totalAmount := 0.0
|
||||
for _, order := range orders {
|
||||
totalAmount += order.Amount
|
||||
}
|
||||
|
||||
// 2. 创建结算记录
|
||||
settlement := CarrierSettlement{
|
||||
CarrierID: carrierID,
|
||||
SettlementMonth: month,
|
||||
TotalOrders: totalOrders,
|
||||
TotalAmount: totalAmount,
|
||||
SettlementStatus: 1, // 待结算
|
||||
}
|
||||
db.Create(&settlement)
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 提现管理
|
||||
|
||||
### 提现申请表: `commission_withdrawal_requests`
|
||||
|
||||
```sql
|
||||
CREATE TABLE commission_withdrawal_requests (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
agent_id BIGINT NOT NULL,
|
||||
withdrawal_amount DECIMAL(10,2) NOT NULL,
|
||||
withdrawal_method VARCHAR(20) NOT NULL,
|
||||
account_info JSONB NOT NULL,
|
||||
status INT NOT NULL DEFAULT 1,
|
||||
reviewer_id BIGINT,
|
||||
review_reason TEXT,
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
paid_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 提现状态
|
||||
|
||||
| status | 状态 | 说明 |
|
||||
|--------|------|------|
|
||||
| 1 | 待审核 | 等待审核 |
|
||||
| 2 | 已通过 | 审核通过,等待打款 |
|
||||
| 3 | 已拒绝 | 审核拒绝 |
|
||||
| 4 | 已打款 | 已打款到账户 |
|
||||
| 5 | 已取消 | 用户取消 |
|
||||
|
||||
### 提现流程
|
||||
|
||||
```
|
||||
代理提交提现申请 (status=1 待审核)
|
||||
↓
|
||||
财务审核
|
||||
├─→ 通过 (status=2 已通过) → 打款 (status=4 已打款)
|
||||
└─→ 拒绝 (status=3 已拒绝)
|
||||
```
|
||||
|
||||
### 提现设置表: `commission_withdrawal_settings`
|
||||
|
||||
```sql
|
||||
CREATE TABLE commission_withdrawal_settings (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
agent_id BIGINT UNIQUE NOT NULL,
|
||||
min_withdrawal_amount DECIMAL(10,2) DEFAULT 0,
|
||||
max_withdrawal_amount DECIMAL(10,2) DEFAULT 0,
|
||||
withdrawal_fee_rate DECIMAL(5,4) DEFAULT 0,
|
||||
auto_approval_enabled BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 提现规则检查
|
||||
|
||||
```go
|
||||
// 伪代码
|
||||
func ValidateWithdrawalRequest(agentID uint, amount float64) error {
|
||||
setting := db.FindWithdrawalSetting(agentID)
|
||||
|
||||
// 检查最小金额
|
||||
if amount < setting.MinWithdrawalAmount {
|
||||
return fmt.Errorf("提现金额不能低于 %.2f 元", setting.MinWithdrawalAmount)
|
||||
}
|
||||
|
||||
// 检查最大金额
|
||||
if setting.MaxWithdrawalAmount > 0 && amount > setting.MaxWithdrawalAmount {
|
||||
return fmt.Errorf("提现金额不能高于 %.2f 元", setting.MaxWithdrawalAmount)
|
||||
}
|
||||
|
||||
// 检查账户余额
|
||||
balance := db.GetAgentBalance(agentID)
|
||||
fee := amount * setting.WithdrawalFeeRate
|
||||
totalAmount := amount + fee
|
||||
|
||||
if balance < totalAmount {
|
||||
return fmt.Errorf("余额不足,需要 %.2f 元(含手续费 %.2f 元)", totalAmount, fee)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 分佣业务流程示例
|
||||
|
||||
### 示例 1: 一次性分佣
|
||||
|
||||
```
|
||||
1. 用户购买套餐(100元)
|
||||
↓
|
||||
2. 订单完成
|
||||
↓
|
||||
3. 触发分佣计算
|
||||
- 规则: 一次性分佣,固定金额 10元
|
||||
- 创建分佣记录: agent_id=123, commission_amount=10.00, status=1 待发放
|
||||
↓
|
||||
4. 自动发放
|
||||
- 更新分佣记录: status=2 已发放, distributed_at=NOW()
|
||||
- 更新代理账户余额: balance += 10.00
|
||||
↓
|
||||
5. 完成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 示例 2: 长期分佣(OR 条件解冻)
|
||||
|
||||
```
|
||||
1. 用户购买套餐(100元)
|
||||
↓
|
||||
2. 订单完成
|
||||
↓
|
||||
3. 触发分佣计算
|
||||
- 规则: 长期分佣,10%,冻结30天 OR 使用1GB流量
|
||||
- 创建分佣记录: agent_id=123, commission_amount=10.00, status=3 已冻结
|
||||
- 解冻条件: {"time_based": {"days": 30}, "data_based": {"data_mb": 1024}}
|
||||
↓
|
||||
4. 定时任务检查解冻条件
|
||||
- 时间检查: 30天后 → 满足条件 → 解冻
|
||||
- 流量检查: 使用1GB流量后 → 满足条件 → 解冻
|
||||
↓
|
||||
5. 自动解冻
|
||||
- 更新分佣记录: status=2 已发放, unfrozen_at=NOW(), distributed_at=NOW()
|
||||
- 更新代理账户余额: balance += 10.00
|
||||
↓
|
||||
6. 完成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 示例 3: 组合分佣
|
||||
|
||||
```
|
||||
1. 用户购买套餐(100元)
|
||||
↓
|
||||
2. 订单完成
|
||||
↓
|
||||
3. 触发分佣计算
|
||||
- 规则: 组合分佣
|
||||
- 一次性条件: 固定金额 5元
|
||||
- 长期条件: 固定金额 10元,冻结30天
|
||||
↓
|
||||
4. 创建两条分佣记录
|
||||
- 记录1: agent_id=123, commission_amount=5.00, status=1 待发放
|
||||
- 记录2: agent_id=123, commission_amount=10.00, status=3 已冻结
|
||||
↓
|
||||
5. 立即发放一次性分佣
|
||||
- 记录1: status=2 已发放
|
||||
- 代理账户余额: balance += 5.00
|
||||
↓
|
||||
6. 30天后自动解冻长期分佣
|
||||
- 记录2: status=2 已发放
|
||||
- 代理账户余额: balance += 10.00
|
||||
↓
|
||||
7. 完成
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 监控和统计
|
||||
|
||||
### 分佣统计指标
|
||||
|
||||
1. **代理分佣总额**
|
||||
- 待发放金额
|
||||
- 已发放金额
|
||||
- 已冻结金额
|
||||
|
||||
2. **分佣发放效率**
|
||||
- 平均发放时长
|
||||
- 平均解冻时长
|
||||
|
||||
3. **提现统计**
|
||||
- 提现申请数量
|
||||
- 提现成功率
|
||||
- 提现金额统计
|
||||
|
||||
### SQL 查询示例
|
||||
|
||||
```sql
|
||||
-- 1. 代理分佣总额统计
|
||||
SELECT
|
||||
agent_id,
|
||||
SUM(CASE WHEN status = 1 THEN commission_amount ELSE 0 END) AS pending_amount,
|
||||
SUM(CASE WHEN status = 2 THEN commission_amount ELSE 0 END) AS distributed_amount,
|
||||
SUM(CASE WHEN status = 3 THEN commission_amount ELSE 0 END) AS frozen_amount
|
||||
FROM commission_records
|
||||
WHERE agent_id = 123
|
||||
GROUP BY agent_id;
|
||||
|
||||
-- 2. 月度分佣统计
|
||||
SELECT
|
||||
DATE_FORMAT(created_at, '%Y-%m') AS month,
|
||||
COUNT(*) AS total_records,
|
||||
SUM(commission_amount) AS total_amount
|
||||
FROM commission_records
|
||||
WHERE agent_id = 123
|
||||
AND status = 2
|
||||
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
|
||||
ORDER BY month DESC;
|
||||
|
||||
-- 3. 提现统计
|
||||
SELECT
|
||||
status,
|
||||
COUNT(*) AS request_count,
|
||||
SUM(withdrawal_amount) AS total_amount
|
||||
FROM commission_withdrawal_requests
|
||||
WHERE agent_id = 123
|
||||
GROUP BY status;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 合理设置冻结条件
|
||||
|
||||
- **过短**: 可能导致代理流失
|
||||
- **过长**: 影响代理积极性
|
||||
- **建议**: 根据业务特点和用户留存数据设置合理的冻结期
|
||||
|
||||
### 2. 使用 OR 条件解冻
|
||||
|
||||
- **优势**: 提高解冻灵活性,代理满足任一条件即可获得佣金
|
||||
- **示例**: 30天 OR 1GB流量,满足其一即可解冻
|
||||
|
||||
### 3. 启用阶梯奖励
|
||||
|
||||
- **优势**: 激励代理提高订单量
|
||||
- **示例**: 月订单量越多,单笔佣金越高
|
||||
|
||||
### 4. 定期审查分佣规则
|
||||
|
||||
- 定期分析分佣数据,优化分佣规则
|
||||
- 根据代理反馈调整冻结条件和佣金比例
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
IoT SIM 管理系统的分佣系统具有以下特点:
|
||||
|
||||
1. **三种分佣模式**: 一次性分佣、长期分佣、组合分佣
|
||||
2. **阶梯奖励机制**: 支持根据订单数量设置不同的分佣标准
|
||||
3. **OR 条件解冻**: 时间到期 OR 流量达标,满足任一条件即可解冻
|
||||
4. **自动 + 手动审批**: 支持自动解冻和手动审批两种模式
|
||||
5. **分佣模板**: 快速创建分佣规则,避免重复配置
|
||||
6. **运营商结算**: 记录与运营商的月度结算情况
|
||||
7. **提现管理**: 完善的提现申请和审批流程
|
||||
8. **多级代理**: 支持无限层级的代理树形结构
|
||||
|
||||
通过灵活配置和使用分佣系统,可以激励代理积极性,提高销售业绩,实现平台与代理的双赢。
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: v1.0
|
||||
**最后更新**: 2026-01-12
|
||||
**维护人员**: Claude Sonnet 4.5
|
||||
Reference in New Issue
Block a user