Browse Source

fix:代理商身份

super-yimizi 2 days ago
parent
commit
5a3a26d385

+ 1 - 1
application/admin/controller/commission/Apply.php

@@ -12,7 +12,7 @@ use think\Exception;
 class Apply extends Backend
 {
     protected $model = null;
-    protected $searchFields = 'id,user.nickname,real_name,company_name,status';
+    protected $searchFields = 'id,user.nickname,real_name,company_name,status,agent_type,identity.name';
     protected $relationSearch = true;
 
     public function _initialize()

+ 1 - 1
application/admin/controller/commission/Identity.php

@@ -8,7 +8,7 @@ use app\common\model\commission\Identity as IdentityModel;
 class Identity extends Backend
 {
     protected $model = null;
-    protected $searchFields = 'id,name';
+    protected $searchFields = 'id,name,agent_type';
     
     public function _initialize()
     {

+ 14 - 0
application/admin/view/commission/apply/detail.html

@@ -37,10 +37,24 @@
                     </div>
 
                     <div class="form-group">
+                        <label class="control-label col-xs-12 col-sm-2">代理商类型:</label>
+                        <div class="col-xs-12 col-sm-8">
+                            <p class="form-control-static">
+                                <span class="label label-{:$row.agent_type == 'regional' ? 'warning' : 'default'}">
+                                    {$row.agent_type_text|default='普通代理商'}
+                                </span>
+                            </p>
+                        </div>
+                    </div>
+
+                    <div class="form-group">
                         <label class="control-label col-xs-12 col-sm-2">申请地区:</label>
                         <div class="col-xs-12 col-sm-8">
                             <p class="form-control-static">
                                 {$row.province_name} - {$row.city_name} - {$row.district_name}
+                                {if condition="$row.agent_type == 'regional'"}
+                                    <br><small class="text-info"><i class="fa fa-info-circle"></i> 区域代理商将管辖该地区内的所有订单</small>
+                                {/if}
                             </p>
                         </div>
                     </div>

+ 16 - 0
application/admin/view/commission/identity/add.html

@@ -13,6 +13,22 @@
         </div>
     </div>
     <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Agent type')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                <label for="row[agent_type]-normal"><input id="row[agent_type]-normal" name="row[agent_type]" type="radio" value="normal" checked> 普通代理商</label> 
+                <label for="row[agent_type]-regional"><input id="row[agent_type]-regional" name="row[agent_type]" type="radio" value="regional"> 区域代理商</label> 
+            </div>
+        </div>
+    </div>
+    <div class="form-group" id="regional-rate-group" style="display: none;">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Regional rate')} (%):</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-regional_commission_rate" class="form-control" name="row[regional_commission_rate]" type="number" step="0.01" min="0" max="100" value="0.00">
+            <span class="help-block">区域代理商的分佣比例,单位为百分比</span>
+        </div>
+    </div>
+    <div class="form-group">
         <label class="control-label col-xs-12 col-sm-2">{:__('Description')}:</label>
         <div class="col-xs-12 col-sm-8">
             <input id="c-description" class="form-control" name="row[description]" type="text">

+ 16 - 0
application/admin/view/commission/identity/edit.html

@@ -13,6 +13,22 @@
         </div>
     </div>
     <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">{:__('Agent type')}:</label>
+        <div class="col-xs-12 col-sm-8">
+            <div class="radio">
+                <label for="row[agent_type]-normal"><input id="row[agent_type]-normal" name="row[agent_type]" type="radio" value="normal" {if condition="$row.agent_type == 'normal' || !$row.agent_type"}checked{/if}> 普通代理商</label> 
+                <label for="row[agent_type]-regional"><input id="row[agent_type]-regional" name="row[agent_type]" type="radio" value="regional" {if condition="$row.agent_type == 'regional'"}checked{/if}> 区域代理商</label> 
+            </div>
+        </div>
+    </div>
+    <div class="form-group" id="regional-rate-group" {if condition="$row.agent_type != 'regional'"}style="display: none;"{/if}>
+        <label class="control-label col-xs-12 col-sm-2">{:__('Regional rate')} (%):</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-regional_commission_rate" class="form-control" name="row[regional_commission_rate]" type="number" step="0.01" min="0" max="100" value="{$row.regional_commission_rate|default='0.00'}">
+            <span class="help-block">区域代理商的分佣比例,单位为百分比</span>
+        </div>
+    </div>
+    <div class="form-group">
         <label class="control-label col-xs-12 col-sm-2">{:__('Description')}:</label>
         <div class="col-xs-12 col-sm-8">
             <input id="c-description" class="form-control" name="row[description]" type="text" value="{$row.description|htmlentities}">

+ 1 - 1
application/api/controller/commission/AgentApply.php

@@ -2,7 +2,7 @@
 
 namespace app\api\controller\commission;
 
-use app\common\Service\commission\AgentApply as AgentApplyService;
+use app\common\Service\Commission\AgentApply as AgentApplyService;
 use app\common\model\commission\Apply as ApplyModel;
 use think\Exception;
 use app\api\controller\Base;

+ 14 - 28
application/common/Service/Commission/AgentApply.php

@@ -21,33 +21,6 @@ class AgentApply
     }
 
     /**
-     * 获取地区树形结构
-     */
-    public function getAreaTree($pid = 0, $level = 1, $maxLevel = 3)
-    {
-        if ($level > $maxLevel) {
-            return [];
-        }
-
-        $list = Area::where('pid', $pid)
-                   ->where('level', $level)
-                   ->field('id,pid,name,level')
-                   ->order('weigh desc,id asc')
-                   ->select();
-
-        $tree = [];
-        foreach ($list as $item) {
-            $node = $item->toArray();
-            if ($level < $maxLevel) {
-                $node['children'] = $this->getAreaTree($item['id'], $level + 1, $maxLevel);
-            }
-            $tree[] = $node;
-        }
-
-        return $tree;
-    }
-
-    /**
      * 提交代理商申请
      */
     public function submitApply($userId, $data)
@@ -79,12 +52,13 @@ class AgentApply
         // 根据申请类型验证必要字段
         $this->validateApplyData($data);
 
-        return Db::transaction(function () use ($userId, $data) {
+        return Db::transaction(function () use ($userId, $data, $identity) {
             // 创建申请记录
             $apply = new ApplyModel();
             $apply->user_id = $userId;
             $apply->apply_type = $data['apply_type'];
             $apply->agent_identity_id = $data['agent_identity_id'];
+            $apply->agent_type = $identity->agent_type; // 从身份配置获取代理商类型
             
             // 地区信息
             $apply->province_id = $data['province_id'];
@@ -229,12 +203,24 @@ class AgentApply
             return $existAgent;
         }
 
+        // 获取身份信息
+        $identity = IdentityModel::where('id', $apply->agent_identity_id)->find();
+
         // 创建分销商记录
         $agent = new AgentModel();
         $agent->user_id = $apply->user_id;
         $agent->level = 1; // 默认等级
+        $agent->agent_type = $apply->agent_type;
         $agent->status = AgentModel::AGENT_STATUS_NORMAL;
         $agent->become_time = time();
+
+        // 如果是区域代理商,设置管辖区域
+        if ($apply->agent_type == 'regional') {
+            $agent->manage_province_id = $apply->province_id;
+            $agent->manage_city_id = $apply->city_id;
+            $agent->manage_district_id = $apply->district_id;
+        }
+
         $agent->save();
 
         return $agent;

+ 17 - 0
application/common/Service/Commission/Order.php

@@ -3,6 +3,7 @@
 namespace app\common\Service\Commission;
 
 use app\common\Service\Commission\Config as ConfigService;
+use app\common\Service\commission\RegionalCommission;
 use app\common\model\commission\Order as OrderModel;
 use app\common\model\commission\Reward as RewardModel;
 use app\common\model\commission\Log as LogModel;
@@ -228,8 +229,24 @@ class Order
                 $parentAgent = new Agent($parentUserId);
                 $this->runCommissionPlan($commissionOrder, $parentAgent, $currentCommissionLevel);
             } else {
+                // 传统分佣结束后,执行区域分佣
+                $this->executeRegionalCommission($commissionOrder);
                 return true;
             }
         }
     }
+
+    /**
+     * 执行区域代理商分佣
+     */
+    protected function executeRegionalCommission($commissionOrder)
+    {
+        try {
+            $regionalService = new RegionalCommission($commissionOrder, $this->goods, $this->config);
+            $regionalService->executeRegionalCommission();
+        } catch (\Exception $e) {
+            // 记录错误但不影响主流程
+            \think\Log::error('区域分佣执行失败: ' . $e->getMessage());
+        }
+    }
 }

+ 205 - 0
application/common/Service/Commission/RegionalCommission.php

@@ -0,0 +1,205 @@
+<?php
+
+namespace app\common\Service\commission;
+
+use app\common\model\commission\Agent as AgentModel;
+use app\common\model\commission\Reward as RewardModel;
+use app\common\model\commission\Log as LogModel;
+use app\common\model\User as UserModel;
+use think\Db;
+
+/**
+ * 区域代理商分佣服务
+ */
+class RegionalCommission
+{
+    protected $commissionOrder;
+    protected $orderUser;
+    protected $goods;
+    protected $config;
+
+    public function __construct($commissionOrder, $goods, $config)
+    {
+        $this->commissionOrder = $commissionOrder;
+        $this->goods = $goods;
+        $this->config = $config;
+        
+        // 获取下单用户信息
+        $this->orderUser = UserModel::where('id', $commissionOrder->buyer_id)->find();
+    }
+
+    /**
+     * 执行区域代理商分佣
+     */
+    public function executeRegionalCommission()
+    {
+        if (!$this->orderUser) {
+            return false;
+        }
+
+        // 获取下单用户的地区信息
+        $userArea = $this->getUserArea();
+        if (!$userArea) {
+            return false;
+        }
+
+        // 查找管辖该区域的区域代理商
+        $regionalAgents = $this->getRegionalAgents($userArea);
+        
+        foreach ($regionalAgents as $agent) {
+            $this->processRegionalAgent($agent, $userArea);
+        }
+
+        return true;
+    }
+
+    /**
+     * 获取用户地区信息
+     */
+    protected function getUserArea()
+    {
+        // 假设用户表有地区字段,或者从用户地址中获取
+        // 这里需要根据实际业务逻辑调整
+        return [
+            'province_id' => $this->orderUser->province_id ?? 0,
+            'city_id' => $this->orderUser->city_id ?? 0,
+            'district_id' => $this->orderUser->district_id ?? 0
+        ];
+    }
+
+    /**
+     * 获取管辖该区域的区域代理商
+     */
+    protected function getRegionalAgents($userArea)
+    {
+        $agents = [];
+
+        // 查找区域代理商:按管辖范围从小到大查找(区 -> 市 -> 省)
+        
+        // 1. 查找管辖该区的代理商
+        if ($userArea['district_id'] > 0) {
+            $districtAgents = AgentModel::where('agent_type', 'regional')
+                                       ->where('manage_district_id', $userArea['district_id'])
+                                       ->where('status', AgentModel::AGENT_STATUS_NORMAL)
+                                       ->with(['identity'])
+                                       ->select();
+            $agents = array_merge($agents, $districtAgents->toArray());
+        }
+
+        // 2. 查找管辖该市的代理商(如果还没有区级代理商)
+        if ($userArea['city_id'] > 0) {
+            $cityAgents = AgentModel::where('agent_type', 'regional')
+                                   ->where('manage_city_id', $userArea['city_id'])
+                                   ->where('manage_district_id', 0) // 市级代理商不管具体区
+                                   ->where('status', AgentModel::AGENT_STATUS_NORMAL)
+                                   ->with(['identity'])
+                                   ->select();
+            $agents = array_merge($agents, $cityAgents->toArray());
+        }
+
+        // 3. 查找管辖该省的代理商(如果还没有市级代理商)
+        if ($userArea['province_id'] > 0) {
+            $provinceAgents = AgentModel::where('agent_type', 'regional')
+                                       ->where('manage_province_id', $userArea['province_id'])
+                                       ->where('manage_city_id', 0)
+                                       ->where('manage_district_id', 0)
+                                       ->where('status', AgentModel::AGENT_STATUS_NORMAL)
+                                       ->with(['identity'])
+                                       ->select();
+            $agents = array_merge($agents, $provinceAgents->toArray());
+        }
+
+        return $agents;
+    }
+
+    /**
+     * 处理单个区域代理商分佣
+     */
+    protected function processRegionalAgent($agent, $userArea)
+    {
+        // 防止重复分佣
+        $existingReward = RewardModel::where([
+            'commission_order_id' => $this->commissionOrder->id,
+            'agent_id' => $agent['user_id'],
+            'reward_type' => 'regional'
+        ])->find();
+
+        if ($existingReward) {
+            return false;
+        }
+
+        // 获取区域分佣比例
+        $commissionRate = $agent['identity']['regional_commission_rate'] ?? 0;
+        if ($commissionRate <= 0) {
+            return false;
+        }
+
+        // 计算佣金
+        $commission = round($this->commissionOrder->amount * $commissionRate / 100, 2);
+        if ($commission <= 0) {
+            return false;
+        }
+
+        // 创建区域分佣记录
+        $rewardData = [
+            'order_id' => $this->commissionOrder->order_id,
+            'order_item_id' => $this->commissionOrder->order_item_id,
+            'buyer_id' => $this->commissionOrder->buyer_id,
+            'commission_order_id' => $this->commissionOrder->id,
+            'agent_id' => $agent['user_id'],
+            'type' => 'commission',
+            'reward_type' => 'regional', // 区域分佣标识
+            'commission' => $commission,
+            'original_commission' => $commission,
+            'commission_level' => 0, // 区域分佣不按层级
+            'agent_level' => $agent['level'],
+            'commission_rules' => json_encode([
+                'type' => 'regional',
+                'rate' => $commissionRate,
+                'area' => $userArea,
+                'agent_area' => [
+                    'province' => $agent['manage_province_id'],
+                    'city' => $agent['manage_city_id'], 
+                    'district' => $agent['manage_district_id']
+                ]
+            ]),
+            'status' => 0 // 待入账
+        ];
+
+        $reward = RewardModel::create($rewardData);
+
+        // 记录日志
+        LogModel::add($agent['user_id'], 'reward', [
+            'type' => 'regional_commission',
+            'reward' => $reward,
+            'area' => $userArea
+        ], $this->orderUser);
+
+        return $reward;
+    }
+
+    /**
+     * 检查区域冲突 - 确保同一区域只有一个代理商获得分佣
+     */
+    protected function checkAreaConflict($agents, $userArea)
+    {
+        // 按管辖范围精确度排序:区 > 市 > 省
+        usort($agents, function($a, $b) {
+            $aScore = 0;
+            $bScore = 0;
+            
+            if ($a['manage_district_id'] > 0) $aScore = 3;
+            elseif ($a['manage_city_id'] > 0) $aScore = 2;  
+            elseif ($a['manage_province_id'] > 0) $aScore = 1;
+            
+            if ($b['manage_district_id'] > 0) $bScore = 3;
+            elseif ($b['manage_city_id'] > 0) $bScore = 2;
+            elseif ($b['manage_province_id'] > 0) $bScore = 1;
+            
+            return $bScore - $aScore; // 降序,精确度高的优先
+        });
+
+        // 只返回最精确的代理商
+        return array_slice($agents, 0, 1);
+    }
+}

+ 20 - 0
application/common/model/commission/Apply.php

@@ -18,6 +18,7 @@ class Apply extends Model
     // 追加属性
     protected $append = [
         'apply_type_text',
+        'agent_type_text',
         'status_text'
     ];
 
@@ -25,6 +26,10 @@ class Apply extends Model
     const APPLY_TYPE_PERSONAL = 'personal';
     const APPLY_TYPE_COMPANY = 'company';
 
+    // 代理商类型
+    const AGENT_TYPE_NORMAL = 'normal';
+    const AGENT_TYPE_REGIONAL = 'regional';
+
     // 申请状态
     const STATUS_PENDING = 'pending';
     const STATUS_APPROVED = 'approved';
@@ -38,6 +43,14 @@ class Apply extends Model
         ];
     }
 
+    public function getAgentTypeList()
+    {
+        return [
+            self::AGENT_TYPE_NORMAL => '普通代理商',
+            self::AGENT_TYPE_REGIONAL => '区域代理商'
+        ];
+    }
+
     public function getStatusList()
     {
         return [
@@ -54,6 +67,13 @@ class Apply extends Model
         return isset($list[$value]) ? $list[$value] : '';
     }
 
+    public function getAgentTypeTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['agent_type']) ? $data['agent_type'] : '');
+        $list = $this->getAgentTypeList();
+        return isset($list[$value]) ? $list[$value] : '';
+    }
+
     public function getStatusTextAttr($value, $data)
     {
         $value = $value ? $value : (isset($data['status']) ? $data['status'] : '');

+ 21 - 1
application/common/model/commission/Identity.php

@@ -17,13 +17,18 @@ class Identity extends Model
 
     // 追加属性
     protected $append = [
-        'status_text'
+        'status_text',
+        'agent_type_text'
     ];
 
     // 状态
     const STATUS_DISABLED = 0;
     const STATUS_ENABLED = 1;
 
+    // 代理商类型
+    const AGENT_TYPE_NORMAL = 'normal';
+    const AGENT_TYPE_REGIONAL = 'regional';
+
     public function getStatusList()
     {
         return [
@@ -32,6 +37,14 @@ class Identity extends Model
         ];
     }
 
+    public function getAgentTypeList()
+    {
+        return [
+            self::AGENT_TYPE_NORMAL => '普通代理商',
+            self::AGENT_TYPE_REGIONAL => '区域代理商'
+        ];
+    }
+
     public function getStatusTextAttr($value, $data)
     {
         $value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
@@ -39,6 +52,13 @@ class Identity extends Model
         return isset($list[$value]) ? $list[$value] : '';
     }
 
+    public function getAgentTypeTextAttr($value, $data)
+    {
+        $value = $value ? $value : (isset($data['agent_type']) ? $data['agent_type'] : '');
+        $list = $this->getAgentTypeList();
+        return isset($list[$value]) ? $list[$value] : '';
+    }
+
     /**
      * 获取启用的身份列表
      */

+ 8 - 1
public/assets/js/backend/commission/apply.js

@@ -9,7 +9,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                     detail_url: 'commission/apply/detail',
                     approve_url: 'commission/apply/approve',
                     reject_url: 'commission/apply/reject',
-                    table: 'shopro_commission_apply',
+                    table: 'shop_commission_apply',
                 }
             });
 
@@ -33,6 +33,13 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                                 return '<span class="label label-info">' + value + '</span>';
                             }
                         }},
+                        {field: 'agent_type_text', title: __('Agent type'), searchList: {"normal": "普通代理商", "regional": "区域代理商"}, formatter: function(value, row, index) {
+                            if (row.agent_type === 'regional') {
+                                return '<span class="label label-warning">' + value + '</span>';
+                            } else {
+                                return '<span class="label label-default">' + value + '</span>';
+                            }
+                        }},
                         {field: 'identity.name', title: __('Identity'), operate: false},
                         {field: 'real_name', title: __('Real name'), visible: false},
                         {field: 'company_name', title: __('Company name'), visible: false},

+ 39 - 1
public/assets/js/backend/commission/identity.js

@@ -10,7 +10,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                     edit_url: 'commission/identity/edit',
                     del_url: 'commission/identity/del',
                     multi_url: 'commission/identity/multi',
-                    table: 'shopro_commission_identity',
+                    table: 'shop_commission_identity',
                 }
             });
 
@@ -28,6 +28,19 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         {field: 'id', title: __('Id'), sortable: true},
                         {field: 'name', title: __('Name'), operate: 'LIKE', formatter: Table.api.formatter.search},
                         {field: 'level', title: __('Level'), sortable: true},
+                        {field: 'agent_type_text', title: __('Agent type'), searchList: {"normal": "普通代理商", "regional": "区域代理商"}, formatter: function(value, row, index) {
+                            if (row.agent_type === 'regional') {
+                                return '<span class="label label-warning">' + value + '</span>';
+                            } else {
+                                return '<span class="label label-default">' + value + '</span>';
+                            }
+                        }},
+                        {field: 'regional_commission_rate', title: __('Regional rate'), operate: false, formatter: function(value, row, index) {
+                            if (row.agent_type === 'regional' && value > 0) {
+                                return value + '%';
+                            }
+                            return row.agent_type === 'regional' ? '0.00%' : '-';
+                        }},
                         {field: 'description', title: __('Description'), operate: 'LIKE'},
                         {field: 'status', title: __('Status'), searchList: {"0": __('Hidden'), "1": __('Shown')}, formatter: Table.api.formatter.status},
                         {field: 'weigh', title: __('Weigh'), sortable: true, operate: false},
@@ -49,6 +62,31 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
         api: {
             bindevent: function () {
                 Form.api.bindevent($("form[role=form]"));
+                
+                // 代理商类型切换事件
+                $(document).on('change', 'input[name="row[agent_type]"]', function() {
+                    var agentType = $(this).val();
+                    var rateGroup = $('#regional-rate-group');
+                    
+                    if (agentType === 'regional') {
+                        rateGroup.show();
+                        rateGroup.find('input').attr('data-rule', 'required;range(0~100)');
+                    } else {
+                        rateGroup.hide();
+                        rateGroup.find('input').removeAttr('data-rule');
+                        rateGroup.find('input').val('0.00');
+                    }
+                });
+                
+                // 页面加载时初始化显示状态
+                var checkedAgentType = $('input[name="row[agent_type]"]:checked').val();
+                if (checkedAgentType === 'regional') {
+                    $('#regional-rate-group').show();
+                    $('#regional-rate-group').find('input').attr('data-rule', 'required;range(0~100)');
+                } else {
+                    $('#regional-rate-group').hide();
+                    $('#regional-rate-group').find('input').removeAttr('data-rule');
+                }
             }
         }
     };