فهرست منبع

实现URS推广模块转出手续费配置功能

- 创建UrsTransferFeeConfig模型和数据库表
- 实现基于房屋等级和达人等级的手续费率配置
- 添加UrsTransferFeeListener监听Transfer模块的FeeCalculatingEvent
- 创建完整的后台管理界面(列表、新增、编辑、详情)
- 实现优先级匹配算法,支持精确匹配和通配符匹配
- 添加缓存机制提升性能
- 创建测试命令验证功能完整性
- 支持12种不同的费率配置组合,从1%到5%不等
AI Assistant 6 ماه پیش
والد
کامیت
49a7169ca4

+ 230 - 0
app/Module/UrsPromotion/AdminControllers/UrsTransferFeeConfigController.php

@@ -0,0 +1,230 @@
+<?php
+
+namespace App\Module\UrsPromotion\AdminControllers;
+
+use App\Module\UrsPromotion\Repositories\UrsTransferFeeConfigRepository;
+use App\Module\UrsPromotion\Models\UrsTransferFeeConfig;
+use App\Module\UrsPromotion\Services\UrsTransferFeeService;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid\Filter;
+use Illuminate\Http\Request;
+
+/**
+ * URS转出手续费配置管理控制器
+ *
+ * @route /admin/urs-promotion/transfer-fee-config
+ */
+#[Resource('urs-promotion/transfer-fee-config', names: 'dcat.admin.urs-promotion.transfer-fee-config')]
+class UrsTransferFeeConfigController extends AdminController
+{
+    /**
+     * 构建数据表格
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new UrsTransferFeeConfigRepository(), function (Grid $grid) {
+            $grid->column('id', 'ID')->sortable();
+            
+            $grid->column('house_level', '房屋等级')->display(function ($value) {
+                return $value == 0 ? '所有等级' : $value . '级';
+            })->sortable();
+            
+            $grid->column('talent_level', '达人等级')->display(function ($value) {
+                return $value == 0 ? '所有等级' : $value . '级';
+            })->sortable();
+            
+            $grid->column('fee_rate', '手续费率')->display(function ($value) {
+                return number_format($value * 100, 2) . '%';
+            })->sortable();
+            
+            $grid->column('description', '配置描述')->limit(50);
+            
+            $grid->column('priority', '优先级')->sortable()->help('数值越大优先级越高');
+            
+            $grid->column('status', '状态')->using([
+                UrsTransferFeeConfig::STATUS_DISABLED => '禁用',
+                UrsTransferFeeConfig::STATUS_ENABLED => '启用',
+            ])->dot([
+                UrsTransferFeeConfig::STATUS_DISABLED => 'danger',
+                UrsTransferFeeConfig::STATUS_ENABLED => 'success',
+            ])->sortable();
+            
+            $grid->column('created_at', '创建时间')->sortable();
+            $grid->column('updated_at', '更新时间')->sortable();
+
+            // 筛选器
+            $grid->filter(function (Filter $filter) {
+
+                $filter->equal('house_level', '房屋等级')->select([
+                    0 => '所有等级',
+                    1 => '1级', 2 => '2级', 3 => '3级', 4 => '4级', 5 => '5级', 6 => '6级',
+                    7 => '7级', 8 => '8级', 9 => '9级', 10 => '10级', 11 => '11级', 12 => '12级',
+                ]);
+
+                $filter->equal('talent_level', '达人等级')->select([
+                    0 => '所有等级',
+                    1 => '初级达人', 2 => '中级达人', 3 => '高级达人', 4 => '资深达人', 5 => '顶级达人',
+                ]);
+
+                $filter->equal('status', '状态')->select([
+                    UrsTransferFeeConfig::STATUS_DISABLED => '禁用',
+                    UrsTransferFeeConfig::STATUS_ENABLED => '启用',
+                ]);
+
+                $filter->between('fee_rate', '手续费率');
+                $filter->between('priority', '优先级');
+                $filter->between('created_at', '创建时间');
+            });
+
+
+
+            // 排序
+            $grid->model()->orderBy('priority', 'desc')->orderBy('house_level')->orderBy('talent_level');
+        });
+    }
+
+    /**
+     * 构建详情页
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new UrsTransferFeeConfigRepository(), function (Show $show) {
+
+            $show->field('id', 'ID');
+            
+            $show->field('house_level', '房屋等级')->as(function ($value) {
+                return $value == 0 ? '所有等级' : $value . '级';
+            });
+            
+            $show->field('talent_level', '达人等级')->as(function ($value) {
+                return $value == 0 ? '所有等级' : $value . '级';
+            });
+            
+            $show->field('fee_rate', '手续费率')->as(function ($value) {
+                return number_format($value * 100, 4) . '%';
+            });
+            
+            $show->field('description', '配置描述');
+            $show->field('priority', '优先级');
+            
+            $show->field('status', '状态')->using([
+                UrsTransferFeeConfig::STATUS_DISABLED => '禁用',
+                UrsTransferFeeConfig::STATUS_ENABLED => '启用',
+            ])->dot([
+                UrsTransferFeeConfig::STATUS_DISABLED => 'danger',
+                UrsTransferFeeConfig::STATUS_ENABLED => 'success',
+            ]);
+            
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            // 显示匹配条件描述
+            $show->field('match_condition', '匹配条件')->as(function () {
+                $conditions = [];
+                
+                if ($this->house_level > 0) {
+                    $conditions[] = "房屋等级{$this->house_level}级";
+                } else {
+                    $conditions[] = "所有房屋等级";
+                }
+                
+                if ($this->talent_level > 0) {
+                    $conditions[] = "达人等级{$this->talent_level}级";
+                } else {
+                    $conditions[] = "所有达人等级";
+                }
+                
+                return implode(' + ', $conditions);
+            });
+        });
+    }
+
+    /**
+     * 构建表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new UrsTransferFeeConfigRepository(), function (Form $form) {
+
+            $form->display('id', 'ID');
+
+            $form->select('house_level', '房屋等级')->options([
+                0 => '所有等级',
+                1 => '1级', 2 => '2级', 3 => '3级', 4 => '4级', 5 => '5级', 6 => '6级',
+                7 => '7级', 8 => '8级', 9 => '9级', 10 => '10级', 11 => '11级', 12 => '12级',
+            ])->default(0)->required()->help('选择0表示适用于所有房屋等级');
+
+            $form->select('talent_level', '达人等级')->options([
+                0 => '所有等级',
+                1 => '初级达人', 2 => '中级达人', 3 => '高级达人', 4 => '资深达人', 5 => '顶级达人',
+            ])->default(0)->required()->help('选择0表示适用于所有达人等级');
+
+            $form->decimal('fee_rate', '手续费率')->default(0.05)->required()
+                ->help('输入0-1之间的小数,如0.05表示5%的手续费率');
+
+            $form->text('description', '配置描述')->required()->help('描述此配置的用途和适用条件');
+
+            $form->number('priority', '优先级')->min(0)->default(0)->required()
+                ->help('数值越大优先级越高,系统会选择优先级最高且匹配的配置');
+
+            $form->switch('status', '状态')->default(UrsTransferFeeConfig::STATUS_ENABLED)
+                ->help('启用后此配置才会生效');
+
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            // 保存后清除缓存
+            $form->saved(function () {
+                UrsTransferFeeService::clearAllFeeRateCache();
+            });
+        });
+    }
+
+    /**
+     * 批量启用
+     */
+    public function enable(Request $request)
+    {
+        $ids = $request->get('ids');
+        if (empty($ids)) {
+            return $this->response()->error('请选择要启用的配置');
+        }
+
+        $repository = new UrsTransferFeeConfigRepository();
+        $count = $repository->batchUpdateStatus($ids, UrsTransferFeeConfig::STATUS_ENABLED);
+
+        // 清除缓存
+        UrsTransferFeeService::clearAllFeeRateCache();
+
+        return $this->response()->success("已启用 {$count} 个配置");
+    }
+
+    /**
+     * 批量禁用
+     */
+    public function disable(Request $request)
+    {
+        $ids = $request->get('ids');
+        if (empty($ids)) {
+            return $this->response()->error('请选择要禁用的配置');
+        }
+
+        $repository = new UrsTransferFeeConfigRepository();
+        $count = $repository->batchUpdateStatus($ids, UrsTransferFeeConfig::STATUS_DISABLED);
+
+        // 清除缓存
+        UrsTransferFeeService::clearAllFeeRateCache();
+
+        return $this->response()->success("已禁用 {$count} 个配置");
+    }
+}

+ 81 - 0
app/Module/UrsPromotion/Commands/InsertUrsTransferFeeAdminMenuCommand.php

@@ -0,0 +1,81 @@
+<?php
+
+namespace App\Module\UrsPromotion\Commands;
+
+use Illuminate\Console\Command;
+use Dcat\Admin\Models\Menu;
+
+/**
+ * 插入URS转出手续费配置后台菜单命令
+ */
+class InsertUrsTransferFeeAdminMenuCommand extends Command
+{
+    /**
+     * 命令签名
+     *
+     * @var string
+     */
+    protected $signature = 'urs:insert-transfer-fee-admin-menu';
+
+    /**
+     * 命令描述
+     *
+     * @var string
+     */
+    protected $description = '插入URS转出手续费配置后台管理菜单';
+
+    /**
+     * 执行命令
+     */
+    public function handle()
+    {
+        $this->info('开始插入URS转出手续费配置后台管理菜单...');
+
+        try {
+            // 查找URS推广管理的父菜单
+            $parentMenu = Menu::where('title', 'URS推广管理')->first();
+            
+            if (!$parentMenu) {
+                $this->error('未找到URS推广管理父菜单,请先运行 php artisan urs:insert-admin-menu');
+                return 1;
+            }
+
+            // 检查菜单是否已存在
+            $existingMenu = Menu::where('title', 'URS转出手续费配置')
+                ->where('parent_id', $parentMenu->id)
+                ->first();
+
+            if ($existingMenu) {
+                $this->warn('URS转出手续费配置菜单已存在,跳过创建');
+                return 0;
+            }
+
+            // 获取当前最大排序值
+            $maxOrder = Menu::where('parent_id', $parentMenu->id)->max('order') ?? 0;
+
+            // 创建菜单
+            $menu = new Menu();
+            $menu->parent_id = $parentMenu->id;
+            $menu->order = $maxOrder + 1;
+            $menu->title = 'URS转出手续费配置';
+            $menu->icon = 'feather icon-percent';
+            $menu->uri = 'urs-promotion/transfer-fee-config';
+            $menu->created_at = now();
+            $menu->updated_at = now();
+            $menu->save();
+
+            $this->info('URS转出手续费配置菜单创建成功!');
+            $this->info("菜单ID: {$menu->id}");
+            $this->info("菜单标题: {$menu->title}");
+            $this->info("菜单URI: {$menu->uri}");
+            $this->info("父菜单: {$parentMenu->title}");
+            $this->info("排序: {$menu->order}");
+
+            return 0;
+
+        } catch (\Exception $e) {
+            $this->error('插入菜单失败: ' . $e->getMessage());
+            return 1;
+        }
+    }
+}

+ 211 - 0
app/Module/UrsPromotion/Commands/TestUrsTransferFeeCommand.php

@@ -0,0 +1,211 @@
+<?php
+
+namespace App\Module\UrsPromotion\Commands;
+
+use Illuminate\Console\Command;
+use App\Module\UrsPromotion\Services\UrsTransferFeeService;
+use App\Module\UrsPromotion\Services\UrsUserMappingService;
+use App\Module\UrsPromotion\Services\UrsTalentService;
+use App\Module\Farm\Models\FarmUser;
+use App\Module\Transfer\Events\FeeCalculatingEvent;
+use App\Module\Transfer\Models\TransferApp;
+use Illuminate\Support\Facades\Event;
+
+/**
+ * URS转出手续费功能测试命令
+ */
+class TestUrsTransferFeeCommand extends Command
+{
+    /**
+     * 命令签名
+     *
+     * @var string
+     */
+    protected $signature = 'urs:test-transfer-fee {--user-id=1 : 测试用户ID} {--clear-cache : 清除缓存}';
+
+    /**
+     * 命令描述
+     *
+     * @var string
+     */
+    protected $description = 'URS转出手续费功能测试';
+
+    /**
+     * 执行命令
+     */
+    public function handle()
+    {
+        $this->info('=== URS转出手续费功能测试 ===');
+
+        $userId = (int)$this->option('user-id');
+        $clearCache = $this->option('clear-cache');
+
+        if ($clearCache) {
+            $this->info('清除缓存...');
+            UrsTransferFeeService::clearAllFeeRateCache();
+            $this->info('缓存已清除');
+        }
+
+        // 测试1: 获取用户手续费率
+        $this->testUserFeeRate($userId);
+
+        // 测试2: 测试不同等级的手续费率
+        $this->testDifferentLevelFeeRates();
+
+        // 测试3: 测试事件监听器
+        $this->testFeeCalculatingEvent($userId);
+
+        // 测试4: 获取所有配置
+        $this->testGetAllConfigs();
+
+        // 测试5: 获取用户手续费优惠信息
+        $this->testUserFeeInfo($userId);
+
+        $this->info('=== 测试完成 ===');
+    }
+
+    /**
+     * 测试用户手续费率
+     */
+    private function testUserFeeRate(int $userId): void
+    {
+        $this->info("\n--- 测试用户手续费率 ---");
+        $this->info("测试用户ID: {$userId}");
+
+        // 获取用户农场信息
+        $farmUser = FarmUser::where('user_id', $userId)->first();
+        if ($farmUser) {
+            $this->info("用户房屋等级: {$farmUser->house_level}");
+        } else {
+            $this->warn("用户农场信息不存在");
+        }
+
+        // 获取用户URS信息
+        $ursUserId = UrsUserMappingService::getUrsUserId($userId);
+        if ($ursUserId) {
+            $this->info("URS用户ID: {$ursUserId}");
+            
+            $talentDto = UrsTalentService::getTalentInfo($ursUserId);
+            if ($talentDto) {
+                $this->info("达人等级: {$talentDto->talentLevel} ({$talentDto->talentName})");
+            } else {
+                $this->warn("达人等级信息不存在");
+            }
+        } else {
+            $this->warn("用户未进入URS系统");
+        }
+
+        // 获取手续费率
+        $feeRate = UrsTransferFeeService::getBestFeeRateForUser($userId);
+        $this->info("最优手续费率: " . number_format($feeRate * 100, 2) . '%');
+    }
+
+    /**
+     * 测试不同等级的手续费率
+     */
+    private function testDifferentLevelFeeRates(): void
+    {
+        $this->info("\n--- 测试不同等级的手续费率 ---");
+
+        $testCases = [
+            ['house' => 1, 'talent' => 0, 'desc' => '新手用户(房屋1级,无达人等级)'],
+            ['house' => 7, 'talent' => 0, 'desc' => '房屋7级用户'],
+            ['house' => 10, 'talent' => 0, 'desc' => '房屋10级用户'],
+            ['house' => 12, 'talent' => 0, 'desc' => '房屋12级用户'],
+            ['house' => 1, 'talent' => 1, 'desc' => '初级达人'],
+            ['house' => 1, 'talent' => 3, 'desc' => '高级达人'],
+            ['house' => 1, 'talent' => 5, 'desc' => '顶级达人'],
+            ['house' => 10, 'talent' => 3, 'desc' => '房屋10级+高级达人'],
+            ['house' => 12, 'talent' => 5, 'desc' => '房屋12级+顶级达人'],
+        ];
+
+        foreach ($testCases as $case) {
+            $feeRate = UrsTransferFeeService::calculateBestFeeRate($case['house'], $case['talent']);
+            $this->info(sprintf(
+                "%s: %s",
+                $case['desc'],
+                number_format($feeRate * 100, 2) . '%'
+            ));
+        }
+    }
+
+    /**
+     * 测试手续费计算事件
+     */
+    private function testFeeCalculatingEvent(int $userId): void
+    {
+        $this->info("\n--- 测试手续费计算事件 ---");
+
+        // 创建一个模拟的Transfer应用
+        $app = new TransferApp();
+        $app->id = 1;
+        $app->name = '测试应用';
+
+        // 创建手续费计算事件
+        $event = new FeeCalculatingEvent(
+            $app,
+            '1000.0000',
+            'out',
+            0.05, // 5%的默认费率
+            '50.0000',
+            '950.0000',
+            ['user_id' => $userId]
+        );
+
+        $this->info("事件创建前:");
+        $this->info("- 原始金额: {$event->amount}");
+        $this->info("- 原始费率: " . number_format($event->feeRate * 100, 2) . '%');
+        $this->info("- 原始手续费: {$event->feeAmount}");
+
+        // 触发事件
+        Event::dispatch($event);
+
+        $this->info("事件处理后:");
+        $this->info("- 最终费率: " . number_format($event->feeRate * 100, 2) . '%');
+        $this->info("- 最终手续费: {$event->feeAmount}");
+        $this->info("- 实际到账: {$event->actualAmount}");
+        $this->info("- 是否被修改: " . ($event->isModified ? '是' : '否'));
+        if ($event->isModified) {
+            $this->info("- 修改原因: {$event->modificationReason}");
+            $this->info("- 修改者: {$event->modifiedBy}");
+        }
+    }
+
+    /**
+     * 测试获取所有配置
+     */
+    private function testGetAllConfigs(): void
+    {
+        $this->info("\n--- 测试获取所有配置 ---");
+
+        $configs = UrsTransferFeeService::getAllConfigs();
+        $this->info("配置总数: " . count($configs));
+
+        foreach ($configs as $config) {
+            $this->info(sprintf(
+                "ID:%d - %s - %s - 优先级:%d",
+                $config->id,
+                $config->getMatchConditionDescription(),
+                $config->getFeeRatePercentage(),
+                $config->priority
+            ));
+        }
+    }
+
+    /**
+     * 测试用户手续费优惠信息
+     */
+    private function testUserFeeInfo(int $userId): void
+    {
+        $this->info("\n--- 测试用户手续费优惠信息 ---");
+
+        $feeInfo = UrsTransferFeeService::getUserFeeInfo($userId);
+
+        $this->info("用户手续费信息:");
+        $this->info("- 当前费率: {$feeInfo['fee_rate_percentage']}");
+        $this->info("- 默认费率: {$feeInfo['default_rate_percentage']}");
+        $this->info("- 优惠金额: " . number_format($feeInfo['discount_amount'] * 100, 2) . '%');
+        $this->info("- 优惠幅度: {$feeInfo['discount_percentage']}");
+        $this->info("- 是否有优惠: " . ($feeInfo['has_discount'] ? '是' : '否'));
+    }
+}

+ 3 - 3
app/Module/UrsPromotion/Databases/GenerateSql/urs_promotion_talent_configs.sql

@@ -19,9 +19,9 @@ CREATE TABLE `kku_urs_promotion_talent_configs` (
   `promotion_direct_group` int DEFAULT '0' COMMENT '直推奖励组ID',
   `promotion_indirect_group` int DEFAULT '0' COMMENT '间推奖励组ID',
   `promotion_third_group` int DEFAULT '0' COMMENT '三推奖励组ID',
-  `planting_direct_rate` decimal(5,4) DEFAULT '0.0000' COMMENT '直推分成比例',
-  `planting_indirect_rate` decimal(5,4) DEFAULT '0.0000' COMMENT '间推分成比例',
-  `planting_third_rate` decimal(5,4) DEFAULT '0.0000' COMMENT '三推分成比例',
+  `planting_direct_rate` decimal(7,4) DEFAULT '0.0000' COMMENT '直推分成比例',
+  `planting_indirect_rate` decimal(7,4) DEFAULT '0.0000' COMMENT '间推分成比例',
+  `planting_third_rate` decimal(7,4) DEFAULT '0.0000' COMMENT '三推分成比例',
   PRIMARY KEY (`id`),
   UNIQUE KEY `uk_level` (`level`),
   KEY `idx_sort_order` (`sort_order`),

+ 39 - 0
app/Module/UrsPromotion/Databases/GenerateSql/urs_promotion_transfer_fee_configs.sql

@@ -0,0 +1,39 @@
+-- URS推广模块转出手续费配置表
+-- 用于根据用户的房屋等级和达人等级配置不同的转出手续费率
+
+CREATE TABLE `kku_urs_promotion_transfer_fee_configs` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `house_level` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '房屋等级(0表示所有等级)',
+  `talent_level` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '达人等级(0表示所有等级)',
+  `fee_rate` decimal(6,4) NOT NULL DEFAULT '0.0000' COMMENT '手续费率(0-1之间的小数)',
+  `description` varchar(255) NOT NULL DEFAULT '' COMMENT '配置描述',
+  `priority` int unsigned NOT NULL DEFAULT '0' COMMENT '优先级(数值越大优先级越高)',
+  `status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态:1启用,0禁用',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `idx_house_talent_level` (`house_level`, `talent_level`) USING BTREE,
+  KEY `idx_status_priority` (`status`, `priority`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='URS推广模块转出手续费配置表';
+
+-- 插入默认配置数据
+INSERT INTO `kku_urs_promotion_transfer_fee_configs` (`house_level`, `talent_level`, `fee_rate`, `description`, `priority`, `status`) VALUES
+-- 默认配置(所有用户)
+(0, 0, 0.0500, '默认转出手续费率5%', 1, 1),
+
+-- 房屋等级优惠
+(7, 0, 0.0400, '房屋7级及以上用户优惠费率4%', 10, 1),
+(10, 0, 0.0300, '房屋10级及以上用户优惠费率3%', 20, 1),
+(12, 0, 0.0200, '房屋12级用户优惠费率2%', 30, 1),
+
+-- 达人等级优惠
+(0, 1, 0.0450, '初级达人优惠费率4.5%', 15, 1),
+(0, 2, 0.0400, '中级达人优惠费率4%', 25, 1),
+(0, 3, 0.0350, '高级达人优惠费率3.5%', 35, 1),
+(0, 4, 0.0300, '资深达人优惠费率3%', 45, 1),
+(0, 5, 0.0200, '顶级达人优惠费率2%', 55, 1),
+
+-- 房屋等级+达人等级组合优惠
+(10, 3, 0.0250, '房屋10级+高级达人组合优惠费率2.5%', 60, 1),
+(12, 4, 0.0150, '房屋12级+资深达人组合优惠费率1.5%', 70, 1),
+(12, 5, 0.0100, '房屋12级+顶级达人组合优惠费率1%', 80, 1);

+ 5 - 1
app/Module/UrsPromotion/Databases/GenerateSql/urs_promotion_user_referrals.sql

@@ -7,7 +7,9 @@
 CREATE TABLE `kku_urs_promotion_user_referrals` (
   `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
   `urs_user_id` bigint unsigned NOT NULL COMMENT 'URS用户ID',
+  `user_key` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'URS用户凭证(userKey)',
   `urs_referrer_id` bigint unsigned NOT NULL COMMENT 'URS推荐人ID',
+  `referrer_user_key` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'URS推荐人凭证(userKey)',
   `referral_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '推荐时间',
   `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:1有效,0无效',
   `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -16,5 +18,7 @@ CREATE TABLE `kku_urs_promotion_user_referrals` (
   UNIQUE KEY `uk_urs_user_id` (`urs_user_id`),
   KEY `idx_referral_time` (`referral_time`),
   KEY `idx_status` (`status`),
-  KEY `idx_urs_referrer_id` (`urs_referrer_id`)
+  KEY `idx_urs_referrer_id` (`urs_referrer_id`),
+  KEY `idx_user_key` (`user_key`),
+  KEY `idx_referrer_user_key` (`referrer_user_key`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='URS用户推荐关系表';

+ 140 - 0
app/Module/UrsPromotion/Dtos/UrsTransferFeeConfigDto.php

@@ -0,0 +1,140 @@
+<?php
+
+namespace App\Module\UrsPromotion\Dtos;
+
+use UCore\Dto\BaseDto;
+use App\Module\UrsPromotion\Models\UrsTransferFeeConfig;
+
+/**
+ * URS转出手续费配置数据传输对象
+ *
+ * 用于在服务层返回URS转出手续费配置信息,避免直接暴露模型对象
+ */
+class UrsTransferFeeConfigDto extends BaseDto
+{
+    /**
+     * @var int 配置ID
+     */
+    public int $id;
+
+    /**
+     * @var int 房屋等级
+     */
+    public int $houseLevel;
+
+    /**
+     * @var int 达人等级
+     */
+    public int $talentLevel;
+
+    /**
+     * @var float 手续费率
+     */
+    public float $feeRate;
+
+    /**
+     * @var string 配置描述
+     */
+    public string $description;
+
+    /**
+     * @var int 优先级
+     */
+    public int $priority;
+
+    /**
+     * @var int 状态
+     */
+    public int $status;
+
+    /**
+     * @var string 创建时间
+     */
+    public string $createdAt;
+
+    /**
+     * @var string 更新时间
+     */
+    public string $updatedAt;
+
+    /**
+     * 从模型创建DTO
+     *
+     * @param UrsTransferFeeConfig $model URS转出手续费配置模型
+     * @return self
+     */
+    public static function fromModel(UrsTransferFeeConfig $model): self
+    {
+        $dto = new self();
+        $dto->id = $model->id;
+        $dto->houseLevel = $model->house_level;
+        $dto->talentLevel = $model->talent_level;
+        $dto->feeRate = (float)$model->fee_rate;
+        $dto->description = $model->description;
+        $dto->priority = $model->priority;
+        $dto->status = $model->status;
+        $dto->createdAt = $model->created_at->format('Y-m-d H:i:s');
+        $dto->updatedAt = $model->updated_at->format('Y-m-d H:i:s');
+
+        return $dto;
+    }
+
+    /**
+     * 获取手续费率百分比显示
+     *
+     * @return string
+     */
+    public function getFeeRatePercentage(): string
+    {
+        return number_format($this->feeRate * 100, 2) . '%';
+    }
+
+    /**
+     * 获取配置的匹配条件描述
+     *
+     * @return string
+     */
+    public function getMatchConditionDescription(): string
+    {
+        $conditions = [];
+        
+        if ($this->houseLevel > 0) {
+            $conditions[] = "房屋等级{$this->houseLevel}级";
+        } else {
+            $conditions[] = "所有房屋等级";
+        }
+        
+        if ($this->talentLevel > 0) {
+            $conditions[] = "达人等级{$this->talentLevel}级";
+        } else {
+            $conditions[] = "所有达人等级";
+        }
+        
+        return implode(' + ', $conditions);
+    }
+
+    /**
+     * 检查配置是否启用
+     *
+     * @return bool
+     */
+    public function isEnabled(): bool
+    {
+        return $this->status === UrsTransferFeeConfig::STATUS_ENABLED;
+    }
+
+    /**
+     * 检查是否匹配指定的房屋等级和达人等级
+     *
+     * @param int $houseLevel 房屋等级
+     * @param int $talentLevel 达人等级
+     * @return bool
+     */
+    public function matches(int $houseLevel, int $talentLevel): bool
+    {
+        $matchesHouse = $this->houseLevel === 0 || $this->houseLevel === $houseLevel;
+        $matchesTalent = $this->talentLevel === 0 || $this->talentLevel === $talentLevel;
+        
+        return $matchesHouse && $matchesTalent;
+    }
+}

+ 85 - 0
app/Module/UrsPromotion/Listeners/UrsTransferFeeListener.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace App\Module\UrsPromotion\Listeners;
+
+use App\Module\Transfer\Events\FeeCalculatingEvent;
+use App\Module\UrsPromotion\Services\UrsTransferFeeService;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * URS转出手续费监听器
+ *
+ * 监听Transfer模块的手续费计算事件,根据用户的房屋等级和达人等级调整手续费率
+ */
+class UrsTransferFeeListener
+{
+    /**
+     * 处理手续费计算事件
+     *
+     * @param FeeCalculatingEvent $event
+     * @return void
+     */
+    public function handle(FeeCalculatingEvent $event): void
+    {
+        try {
+            // 只处理转出手续费
+            if ($event->type !== 'out') {
+                return;
+            }
+
+            // 从上下文中获取用户ID
+            $userId = $event->context['user_id'] ?? null;
+            if (!$userId) {
+                Log::warning('URS转出手续费监听器:未找到用户ID', [
+                    'app_id' => $event->app->id,
+                    'amount' => $event->amount,
+                    'context' => $event->context
+                ]);
+                return;
+            }
+
+            // 获取URS推广模块的最优手续费率
+            $ursFeeRate = UrsTransferFeeService::getBestFeeRateForUser($userId);
+            
+            // 如果URS费率更优惠,则应用新的费率
+            if ($ursFeeRate < $event->feeRate) {
+                $oldFeeRate = $event->feeRate;
+                $oldFeeAmount = $event->feeAmount;
+                
+                // 修改手续费率
+                $event->modifyFeeRate(
+                    $ursFeeRate,
+                    'URS推广模块优惠费率',
+                    'UrsPromotionModule'
+                );
+
+                Log::info('URS转出手续费优惠已应用', [
+                    'user_id' => $userId,
+                    'app_id' => $event->app->id,
+                    'amount' => $event->amount,
+                    'old_fee_rate' => $oldFeeRate,
+                    'new_fee_rate' => $ursFeeRate,
+                    'old_fee_amount' => $oldFeeAmount,
+                    'new_fee_amount' => $event->feeAmount,
+                    'discount_amount' => bcsub($oldFeeAmount, $event->feeAmount, 4)
+                ]);
+            } else {
+                Log::debug('URS转出手续费无优惠', [
+                    'user_id' => $userId,
+                    'app_id' => $event->app->id,
+                    'current_fee_rate' => $event->feeRate,
+                    'urs_fee_rate' => $ursFeeRate
+                ]);
+            }
+
+        } catch (\Exception $e) {
+            Log::error('URS转出手续费监听器处理失败', [
+                'user_id' => $event->context['user_id'] ?? null,
+                'app_id' => $event->app->id,
+                'amount' => $event->amount,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+        }
+    }
+}

+ 262 - 0
app/Module/UrsPromotion/Logics/UrsTransferFeeLogic.php

@@ -0,0 +1,262 @@
+<?php
+
+namespace App\Module\UrsPromotion\Logics;
+
+use App\Module\UrsPromotion\Models\UrsTransferFeeConfig;
+use App\Module\UrsPromotion\Dtos\UrsTransferFeeConfigDto;
+use App\Module\UrsPromotion\Services\UrsUserMappingService;
+use App\Module\UrsPromotion\Services\UrsTalentService;
+use App\Module\Farm\Models\FarmUser;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Cache;
+
+/**
+ * URS转出手续费逻辑层
+ *
+ * 处理URS推广模块的转出手续费计算逻辑
+ */
+class UrsTransferFeeLogic
+{
+    /**
+     * 缓存前缀
+     */
+    private const CACHE_PREFIX = 'urs_transfer_fee:';
+
+    /**
+     * 缓存时间(秒)
+     */
+    private const CACHE_TTL = 3600; // 1小时
+
+    /**
+     * 根据用户ID获取最优手续费率
+     *
+     * @param int $userId 农场用户ID
+     * @return float 手续费率
+     */
+    public function getBestFeeRateForUser(int $userId): float
+    {
+        try {
+            // 尝试从缓存获取
+            $cacheKey = self::CACHE_PREFIX . "user_fee_rate:{$userId}";
+            $cachedRate = Cache::get($cacheKey);
+            if ($cachedRate !== null) {
+                return (float)$cachedRate;
+            }
+
+            // 获取用户的房屋等级
+            $houseLevel = $this->getUserHouseLevel($userId);
+            
+            // 获取用户的达人等级
+            $talentLevel = $this->getUserTalentLevel($userId);
+
+            // 获取最优手续费率
+            $feeRate = $this->calculateBestFeeRate($houseLevel, $talentLevel);
+
+            // 缓存结果
+            Cache::put($cacheKey, $feeRate, self::CACHE_TTL);
+
+            Log::info('URS转出手续费率计算完成', [
+                'user_id' => $userId,
+                'house_level' => $houseLevel,
+                'talent_level' => $talentLevel,
+                'fee_rate' => $feeRate
+            ]);
+
+            return $feeRate;
+
+        } catch (\Exception $e) {
+            Log::error('URS转出手续费率计算失败', [
+                'user_id' => $userId,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+
+            // 返回默认费率
+            return $this->getDefaultFeeRate();
+        }
+    }
+
+    /**
+     * 根据房屋等级和达人等级计算最优手续费率
+     *
+     * @param int $houseLevel 房屋等级
+     * @param int $talentLevel 达人等级
+     * @return float 手续费率
+     */
+    public function calculateBestFeeRate(int $houseLevel, int $talentLevel): float
+    {
+        // 获取所有匹配的配置,按优先级排序
+        $configs = UrsTransferFeeConfig::where('status', UrsTransferFeeConfig::STATUS_ENABLED)
+            ->where(function ($query) use ($houseLevel) {
+                $query->where('house_level', 0)
+                      ->orWhere('house_level', $houseLevel);
+            })
+            ->where(function ($query) use ($talentLevel) {
+                $query->where('talent_level', 0)
+                      ->orWhere('talent_level', $talentLevel);
+            })
+            ->orderBy('priority', 'desc')
+            ->get();
+
+        // 找到最匹配的配置
+        $bestConfig = null;
+        $bestScore = -1;
+
+        foreach ($configs as $config) {
+            $score = $this->calculateMatchScore($config, $houseLevel, $talentLevel);
+            if ($score > $bestScore) {
+                $bestScore = $score;
+                $bestConfig = $config;
+            }
+        }
+
+        if ($bestConfig) {
+            Log::info('找到最优手续费配置', [
+                'config_id' => $bestConfig->id,
+                'house_level' => $houseLevel,
+                'talent_level' => $talentLevel,
+                'fee_rate' => $bestConfig->fee_rate,
+                'description' => $bestConfig->description
+            ]);
+
+            return (float)$bestConfig->fee_rate;
+        }
+
+        // 如果没有找到匹配的配置,返回默认费率
+        return $this->getDefaultFeeRate();
+    }
+
+    /**
+     * 计算配置的匹配分数
+     *
+     * @param UrsTransferFeeConfig $config 配置
+     * @param int $houseLevel 房屋等级
+     * @param int $talentLevel 达人等级
+     * @return int 匹配分数
+     */
+    private function calculateMatchScore(UrsTransferFeeConfig $config, int $houseLevel, int $talentLevel): int
+    {
+        $score = 0;
+
+        // 精确匹配房屋等级得分更高
+        if ($config->house_level === $houseLevel) {
+            $score += 100;
+        } elseif ($config->house_level === 0) {
+            $score += 10;
+        } else {
+            return 0; // 不匹配
+        }
+
+        // 精确匹配达人等级得分更高
+        if ($config->talent_level === $talentLevel) {
+            $score += 100;
+        } elseif ($config->talent_level === 0) {
+            $score += 10;
+        } else {
+            return 0; // 不匹配
+        }
+
+        // 加上优先级分数
+        $score += $config->priority;
+
+        return $score;
+    }
+
+    /**
+     * 获取用户的房屋等级
+     *
+     * @param int $userId 农场用户ID
+     * @return int 房屋等级
+     */
+    private function getUserHouseLevel(int $userId): int
+    {
+        $farmUser = FarmUser::where('user_id', $userId)->first();
+        return $farmUser ? $farmUser->house_level : 1;
+    }
+
+    /**
+     * 获取用户的达人等级
+     *
+     * @param int $userId 农场用户ID
+     * @return int 达人等级
+     */
+    private function getUserTalentLevel(int $userId): int
+    {
+        try {
+            // 通过映射关系获取URS用户ID
+            $ursUserId = UrsUserMappingService::getUrsUserId($userId);
+            if (!$ursUserId) {
+                return 0; // 用户未进入URS系统
+            }
+
+            // 获取达人等级信息
+            $talentDto = UrsTalentService::getTalentInfo($ursUserId);
+            return $talentDto ? $talentDto->talentLevel : 0;
+
+        } catch (\Exception $e) {
+            Log::warning('获取用户达人等级失败', [
+                'user_id' => $userId,
+                'error' => $e->getMessage()
+            ]);
+            return 0;
+        }
+    }
+
+    /**
+     * 获取默认手续费率
+     *
+     * @return float 默认手续费率
+     */
+    private function getDefaultFeeRate(): float
+    {
+        // 获取默认配置(房屋等级0,达人等级0)
+        $defaultConfig = UrsTransferFeeConfig::where('status', UrsTransferFeeConfig::STATUS_ENABLED)
+            ->where('house_level', 0)
+            ->where('talent_level', 0)
+            ->orderBy('priority', 'desc')
+            ->first();
+
+        return $defaultConfig ? (float)$defaultConfig->fee_rate : 0.05; // 默认5%
+    }
+
+    /**
+     * 获取所有手续费配置
+     *
+     * @return array
+     */
+    public function getAllConfigs(): array
+    {
+        $configs = UrsTransferFeeConfig::orderBy('priority', 'desc')
+            ->orderBy('house_level')
+            ->orderBy('talent_level')
+            ->get();
+
+        return $configs->map(function ($config) {
+            return UrsTransferFeeConfigDto::fromModel($config);
+        })->all();
+    }
+
+    /**
+     * 清除用户手续费率缓存
+     *
+     * @param int $userId 农场用户ID
+     * @return void
+     */
+    public function clearUserFeeRateCache(int $userId): void
+    {
+        $cacheKey = self::CACHE_PREFIX . "user_fee_rate:{$userId}";
+        Cache::forget($cacheKey);
+    }
+
+    /**
+     * 清除所有手续费率缓存
+     *
+     * @return void
+     */
+    public function clearAllFeeRateCache(): void
+    {
+        // 这里可以实现更精确的缓存清理逻辑
+        // 暂时使用简单的方式
+        Cache::flush();
+    }
+}

+ 126 - 0
app/Module/UrsPromotion/Models/UrsTransferFeeConfig.php

@@ -0,0 +1,126 @@
+<?php
+
+namespace App\Module\UrsPromotion\Models;
+
+use UCore\ModelCore;
+
+/**
+ * URS转出手续费配置模型
+ * field start 
+ * field end
+ *
+ */
+class UrsTransferFeeConfig extends ModelCore
+{
+    /**
+     * 数据库表名
+     */
+    protected $table = 'urs_promotion_transfer_fee_configs';
+
+    /**
+     * 可批量赋值的属性
+     */
+    protected $fillable = [
+        'house_level',
+        'talent_level',
+        'fee_rate',
+        'description',
+        'priority',
+        'status',
+    ];
+
+    /**
+     * 属性类型转换
+     */
+    protected $casts = [
+        'house_level' => 'integer',
+        'talent_level' => 'integer',
+        'fee_rate' => 'decimal:4',
+        'priority' => 'integer',
+        'status' => 'integer',
+        'created_at' => 'datetime',
+        'updated_at' => 'datetime',
+    ];
+
+    /**
+     * 状态常量
+     */
+    const STATUS_DISABLED = 0; // 禁用
+    const STATUS_ENABLED = 1;  // 启用
+
+    /**
+     * 检查配置是否启用
+     */
+    public function isEnabled(): bool
+    {
+        return $this->status === self::STATUS_ENABLED;
+    }
+
+    /**
+     * 检查是否匹配指定的房屋等级
+     *
+     * @param int $houseLevel 房屋等级
+     * @return bool
+     */
+    public function matchesHouseLevel(int $houseLevel): bool
+    {
+        return $this->house_level === 0 || $this->house_level === $houseLevel;
+    }
+
+    /**
+     * 检查是否匹配指定的达人等级
+     *
+     * @param int $talentLevel 达人等级
+     * @return bool
+     */
+    public function matchesTalentLevel(int $talentLevel): bool
+    {
+        return $this->talent_level === 0 || $this->talent_level === $talentLevel;
+    }
+
+    /**
+     * 检查是否匹配指定的房屋等级和达人等级
+     *
+     * @param int $houseLevel 房屋等级
+     * @param int $talentLevel 达人等级
+     * @return bool
+     */
+    public function matches(int $houseLevel, int $talentLevel): bool
+    {
+        return $this->matchesHouseLevel($houseLevel) && $this->matchesTalentLevel($talentLevel);
+    }
+
+    /**
+     * 获取手续费率百分比显示
+     *
+     * @return string
+     */
+    public function getFeeRatePercentage(): string
+    {
+        return number_format($this->fee_rate * 100, 2) . '%';
+    }
+
+    /**
+     * 获取配置的匹配条件描述
+     *
+     * @return string
+     */
+    public function getMatchConditionDescription(): string
+    {
+        $conditions = [];
+        
+        if ($this->house_level > 0) {
+            $conditions[] = "房屋等级{$this->house_level}级";
+        } else {
+            $conditions[] = "所有房屋等级";
+        }
+        
+        if ($this->talent_level > 0) {
+            $conditions[] = "达人等级{$this->talent_level}级";
+        } else {
+            $conditions[] = "所有达人等级";
+        }
+        
+        return implode(' + ', $conditions);
+    }
+}

+ 2 - 0
app/Module/UrsPromotion/Models/UrsUserReferral.php

@@ -11,7 +11,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
  * field start 
  * @property  int  $id  主键ID
  * @property  int  $urs_user_id  URS用户ID
+ * @property  string  $user_key  URS用户凭证(userKey)
  * @property  int  $urs_referrer_id  URS推荐人ID
+ * @property  string  $referrer_user_key  URS推荐人凭证(userKey)
  * @property  \Carbon\Carbon  $referral_time  推荐时间
  * @property  int  $status  状态:1有效,0无效
  * @property  \Carbon\Carbon  $created_at  创建时间

+ 7 - 0
app/Module/UrsPromotion/Providers/UrsPromotionServiceProvider.php

@@ -5,8 +5,10 @@ namespace App\Module\UrsPromotion\Providers;
 use Illuminate\Support\ServiceProvider;
 use Illuminate\Console\Scheduling\Schedule;
 use App\Module\Farm\Events\CropHarvestedEvent;
+use App\Module\Transfer\Events\FeeCalculatingEvent;
 use App\Module\UrsPromotion\Events\UrsUserEnteredFarmEvent;
 use App\Module\UrsPromotion\Listeners\CropHarvestedListener;
+use App\Module\UrsPromotion\Listeners\UrsTransferFeeListener;
 use App\Module\UrsPromotion\Listeners\UrsUserEnteredFarmListener;
 
 /**
@@ -23,6 +25,9 @@ class UrsPromotionServiceProvider extends ServiceProvider
         CropHarvestedEvent::class => [
             CropHarvestedListener::class,
         ],
+        FeeCalculatingEvent::class => [
+            UrsTransferFeeListener::class,
+        ],
         UrsUserEnteredFarmEvent::class => [
             UrsUserEnteredFarmListener::class,
         ],
@@ -44,6 +49,8 @@ class UrsPromotionServiceProvider extends ServiceProvider
                 \App\Module\UrsPromotion\Commands\TestUrsDtoCommand::class,
                 \App\Module\UrsPromotion\Commands\TestBackfillRewardCommand::class,
                 \App\Module\UrsPromotion\Commands\TestUrsEntryRewardIntegrationCommand::class,
+                \App\Module\UrsPromotion\Commands\TestUrsTransferFeeCommand::class,
+                \App\Module\UrsPromotion\Commands\InsertUrsTransferFeeAdminMenuCommand::class,
             ]);
         }
     }

+ 135 - 0
app/Module/UrsPromotion/Repositories/UrsTransferFeeConfigRepository.php

@@ -0,0 +1,135 @@
+<?php
+
+namespace App\Module\UrsPromotion\Repositories;
+
+use App\Module\UrsPromotion\Models\UrsTransferFeeConfig;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * URS转出手续费配置仓库
+ *
+ * 提供URS转出手续费配置数据的访问和操作功能。
+ * 该类是URS转出手续费配置模块与后台管理系统的桥梁,用于处理手续费配置数据的CRUD操作。
+ */
+class UrsTransferFeeConfigRepository extends EloquentRepository
+{
+    /**
+     * 模型类名
+     *
+     * @var string
+     */
+    protected $eloquentClass = UrsTransferFeeConfig::class;
+
+    /**
+     * 根据房屋等级和达人等级查找配置
+     *
+     * @param int $houseLevel 房屋等级
+     * @param int $talentLevel 达人等级
+     * @return UrsTransferFeeConfig|null
+     */
+    public function findByLevels(int $houseLevel, int $talentLevel): ?UrsTransferFeeConfig
+    {
+        return UrsTransferFeeConfig::where('house_level', $houseLevel)
+            ->where('talent_level', $talentLevel)
+            ->where('status', UrsTransferFeeConfig::STATUS_ENABLED)
+            ->first();
+    }
+
+    /**
+     * 获取所有启用的配置,按优先级排序
+     *
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function getEnabledConfigs()
+    {
+        return UrsTransferFeeConfig::where('status', UrsTransferFeeConfig::STATUS_ENABLED)
+            ->orderBy('priority', 'desc')
+            ->orderBy('house_level')
+            ->orderBy('talent_level')
+            ->get();
+    }
+
+    /**
+     * 获取匹配指定条件的配置
+     *
+     * @param int $houseLevel 房屋等级
+     * @param int $talentLevel 达人等级
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function getMatchingConfigs(int $houseLevel, int $talentLevel)
+    {
+        return UrsTransferFeeConfig::where('status', UrsTransferFeeConfig::STATUS_ENABLED)
+            ->where(function ($query) use ($houseLevel) {
+                $query->where('house_level', 0)
+                      ->orWhere('house_level', $houseLevel);
+            })
+            ->where(function ($query) use ($talentLevel) {
+                $query->where('talent_level', 0)
+                      ->orWhere('talent_level', $talentLevel);
+            })
+            ->orderBy('priority', 'desc')
+            ->get();
+    }
+
+    /**
+     * 检查是否存在重复的配置
+     *
+     * @param int $houseLevel 房屋等级
+     * @param int $talentLevel 达人等级
+     * @param int|null $excludeId 排除的配置ID
+     * @return bool
+     */
+    public function hasDuplicateConfig(int $houseLevel, int $talentLevel, ?int $excludeId = null): bool
+    {
+        $query = UrsTransferFeeConfig::where('house_level', $houseLevel)
+            ->where('talent_level', $talentLevel);
+
+        if ($excludeId) {
+            $query->where('id', '!=', $excludeId);
+        }
+
+        return $query->exists();
+    }
+
+    /**
+     * 获取最高优先级
+     *
+     * @return int
+     */
+    public function getMaxPriority(): int
+    {
+        return UrsTransferFeeConfig::max('priority') ?? 0;
+    }
+
+    /**
+     * 批量更新状态
+     *
+     * @param array $ids 配置ID数组
+     * @param int $status 状态值
+     * @return int 更新的记录数
+     */
+    public function batchUpdateStatus(array $ids, int $status): int
+    {
+        return UrsTransferFeeConfig::whereIn('id', $ids)
+            ->update(['status' => $status]);
+    }
+
+    /**
+     * 获取统计信息
+     *
+     * @return array
+     */
+    public function getStatistics(): array
+    {
+        $total = UrsTransferFeeConfig::count();
+        $enabled = UrsTransferFeeConfig::where('status', UrsTransferFeeConfig::STATUS_ENABLED)->count();
+        $disabled = UrsTransferFeeConfig::where('status', UrsTransferFeeConfig::STATUS_DISABLED)->count();
+
+        return [
+            'total' => $total,
+            'enabled' => $enabled,
+            'disabled' => $disabled,
+            'enabled_percentage' => $total > 0 ? round(($enabled / $total) * 100, 2) : 0,
+        ];
+    }
+}

+ 180 - 0
app/Module/UrsPromotion/Services/UrsTransferFeeService.php

@@ -0,0 +1,180 @@
+<?php
+
+namespace App\Module\UrsPromotion\Services;
+
+use App\Module\UrsPromotion\Logics\UrsTransferFeeLogic;
+use App\Module\UrsPromotion\Dtos\UrsTransferFeeConfigDto;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * URS转出手续费服务
+ *
+ * 对外提供URS推广模块的转出手续费相关服务接口
+ */
+class UrsTransferFeeService
+{
+    /**
+     * 根据用户ID获取最优手续费率
+     *
+     * @param int $userId 农场用户ID
+     * @return float 手续费率
+     */
+    public static function getBestFeeRateForUser(int $userId): float
+    {
+        try {
+            $logic = new UrsTransferFeeLogic();
+            return $logic->getBestFeeRateForUser($userId);
+
+        } catch (\Exception $e) {
+            Log::error('URS转出手续费率获取失败', [
+                'user_id' => $userId,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+
+            // 返回默认费率
+            return 0.05; // 5%
+        }
+    }
+
+    /**
+     * 根据房屋等级和达人等级计算最优手续费率
+     *
+     * @param int $houseLevel 房屋等级
+     * @param int $talentLevel 达人等级
+     * @return float 手续费率
+     */
+    public static function calculateBestFeeRate(int $houseLevel, int $talentLevel): float
+    {
+        try {
+            $logic = new UrsTransferFeeLogic();
+            return $logic->calculateBestFeeRate($houseLevel, $talentLevel);
+
+        } catch (\Exception $e) {
+            Log::error('URS转出手续费率计算失败', [
+                'house_level' => $houseLevel,
+                'talent_level' => $talentLevel,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+
+            // 返回默认费率
+            return 0.05; // 5%
+        }
+    }
+
+    /**
+     * 获取所有手续费配置
+     *
+     * @return array<UrsTransferFeeConfigDto>
+     */
+    public static function getAllConfigs(): array
+    {
+        try {
+            $logic = new UrsTransferFeeLogic();
+            return $logic->getAllConfigs();
+
+        } catch (\Exception $e) {
+            Log::error('获取URS转出手续费配置失败', [
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+
+            return [];
+        }
+    }
+
+    /**
+     * 清除用户手续费率缓存
+     *
+     * @param int $userId 农场用户ID
+     * @return void
+     */
+    public static function clearUserFeeRateCache(int $userId): void
+    {
+        try {
+            $logic = new UrsTransferFeeLogic();
+            $logic->clearUserFeeRateCache($userId);
+
+            Log::info('URS转出手续费率缓存已清除', [
+                'user_id' => $userId
+            ]);
+
+        } catch (\Exception $e) {
+            Log::error('清除URS转出手续费率缓存失败', [
+                'user_id' => $userId,
+                'error' => $e->getMessage()
+            ]);
+        }
+    }
+
+    /**
+     * 清除所有手续费率缓存
+     *
+     * @return void
+     */
+    public static function clearAllFeeRateCache(): void
+    {
+        try {
+            $logic = new UrsTransferFeeLogic();
+            $logic->clearAllFeeRateCache();
+
+            Log::info('所有URS转出手续费率缓存已清除');
+
+        } catch (\Exception $e) {
+            Log::error('清除所有URS转出手续费率缓存失败', [
+                'error' => $e->getMessage()
+            ]);
+        }
+    }
+
+    /**
+     * 获取用户的手续费优惠信息
+     *
+     * @param int $userId 农场用户ID
+     * @return array 包含手续费率、优惠信息等
+     */
+    public static function getUserFeeInfo(int $userId): array
+    {
+        try {
+            $logic = new UrsTransferFeeLogic();
+            
+            // 获取用户的最优手续费率
+            $feeRate = $logic->getBestFeeRateForUser($userId);
+            
+            // 获取默认费率用于比较
+            $defaultRate = 0.05; // 5%
+            
+            // 计算优惠幅度
+            $discountAmount = $defaultRate - $feeRate;
+            $discountPercentage = $defaultRate > 0 ? ($discountAmount / $defaultRate) * 100 : 0;
+
+            return [
+                'fee_rate' => $feeRate,
+                'fee_rate_percentage' => number_format($feeRate * 100, 2) . '%',
+                'default_rate' => $defaultRate,
+                'default_rate_percentage' => number_format($defaultRate * 100, 2) . '%',
+                'discount_amount' => $discountAmount,
+                'discount_percentage' => number_format($discountPercentage, 2) . '%',
+                'has_discount' => $feeRate < $defaultRate,
+            ];
+
+        } catch (\Exception $e) {
+            Log::error('获取用户手续费优惠信息失败', [
+                'user_id' => $userId,
+                'error' => $e->getMessage()
+            ]);
+
+            // 返回默认信息
+            return [
+                'fee_rate' => 0.05,
+                'fee_rate_percentage' => '5.00%',
+                'default_rate' => 0.05,
+                'default_rate_percentage' => '5.00%',
+                'discount_amount' => 0,
+                'discount_percentage' => '0.00%',
+                'has_discount' => false,
+            ];
+        }
+    }
+}