Parcourir la source

feat(promotion): 添加团队推广相关控制器和功能

- 新增邀请奖励记录管理控制器
- 新增团队收益记录管理控制器
- 新增收益分成规则管理控制器
- 新增推荐关系修改记录管理控制器
- 添加相关辅助类和特性,提高代码复用性
- 实现列表、详情、表单等页面功能,支持数据筛选和导出
notfff il y a 7 mois
Parent
commit
798a00d279
91 fichiers modifiés avec 12492 ajouts et 1 suppressions
  1. 171 0
      AiWork/202506/141403-修复ThirdParty报告页面错误.md
  2. 11 1
      AiWork/WORK.md
  3. 15 0
      app/Module/Promotionurs/AdminControllers/Helper/FilterHelper.php
  4. 64 0
      app/Module/Promotionurs/AdminControllers/Helper/FilterHelperTrait.php
  5. 15 0
      app/Module/Promotionurs/AdminControllers/Helper/FormHelper.php
  6. 76 0
      app/Module/Promotionurs/AdminControllers/Helper/FormHelperTrait.php
  7. 15 0
      app/Module/Promotionurs/AdminControllers/Helper/GridHelper.php
  8. 64 0
      app/Module/Promotionurs/AdminControllers/Helper/GridHelperTrait.php
  9. 15 0
      app/Module/Promotionurs/AdminControllers/Helper/ShowHelper.php
  10. 64 0
      app/Module/Promotionurs/AdminControllers/Helper/ShowHelperTrait.php
  11. 133 0
      app/Module/Promotionurs/AdminControllers/TeamInviteRewardController.php
  12. 139 0
      app/Module/Promotionurs/AdminControllers/TeamProfitController.php
  13. 113 0
      app/Module/Promotionurs/AdminControllers/TeamProfitRuleController.php
  14. 127 0
      app/Module/Promotionurs/AdminControllers/TeamReferralChangeController.php
  15. 114 0
      app/Module/Promotionurs/AdminControllers/TeamReferralCodeController.php
  16. 129 0
      app/Module/Promotionurs/AdminControllers/TeamReferralCodeUsageController.php
  17. 127 0
      app/Module/Promotionurs/AdminControllers/TeamTalentConfigController.php
  18. 107 0
      app/Module/Promotionurs/AdminControllers/TeamUserReferralController.php
  19. 111 0
      app/Module/Promotionurs/AdminControllers/TeamUserRelationCacheController.php
  20. 110 0
      app/Module/Promotionurs/AdminControllers/TeamUserTalentController.php
  21. 60 0
      app/Module/Promotionurs/Commands/CleanExpiredReferralCodesCommand.php
  22. 114 0
      app/Module/Promotionurs/Commands/RebuildRelationCacheCommand.php
  23. 120 0
      app/Module/Promotionurs/Commands/UpdateTalentLevelsCommand.php
  24. 6 0
      app/Module/Promotionurs/Databases/GenerateSql/README.md
  25. 26 0
      app/Module/Promotionurs/Databases/GenerateSql/team_invite_rewards.sql
  26. 19 0
      app/Module/Promotionurs/Databases/GenerateSql/team_profit_rules.sql
  27. 24 0
      app/Module/Promotionurs/Databases/GenerateSql/team_profits.sql
  28. 19 0
      app/Module/Promotionurs/Databases/GenerateSql/team_referral_changes.sql
  29. 25 0
      app/Module/Promotionurs/Databases/GenerateSql/team_referral_code_usages.sql
  30. 20 0
      app/Module/Promotionurs/Databases/GenerateSql/team_referral_codes.sql
  31. 20 0
      app/Module/Promotionurs/Databases/GenerateSql/team_talent_configs.sql
  32. 16 0
      app/Module/Promotionurs/Databases/GenerateSql/team_user_referrals.sql
  33. 22 0
      app/Module/Promotionurs/Databases/GenerateSql/team_user_relation_cache.sql
  34. 18 0
      app/Module/Promotionurs/Databases/GenerateSql/team_user_talents.sql
  35. 61 0
      app/Module/Promotionurs/Docs/README.md
  36. 197 0
      app/Module/Promotionurs/Docs/create.sql
  37. 699 0
      app/Module/Promotionurs/Docs/与Farm模块集成.md
  38. 1424 0
      app/Module/Promotionurs/Docs/推荐关系系统.md
  39. 548 0
      app/Module/Promotionurs/Docs/数据库设计.md
  40. 342 0
      app/Module/Promotionurs/Docs/枚举定义.md
  41. 828 0
      app/Module/Promotionurs/Docs/模块接口.md
  42. 129 0
      app/Module/Promotionurs/Docs/直间推收益机制.md
  43. 793 0
      app/Module/Promotionurs/Docs/缓存策略.md
  44. 348 0
      app/Module/Promotionurs/Docs/设计概述.md
  45. 27 0
      app/Module/Promotionurs/Enums/PROFIT_RECORD_STATUS.php
  46. 27 0
      app/Module/Promotionurs/Enums/PROFIT_SOURCE_TYPE.php
  47. 27 0
      app/Module/Promotionurs/Enums/REFERRAL_CODE_STATUS.php
  48. 21 0
      app/Module/Promotionurs/Enums/REFERRAL_LEVEL.php
  49. 45 0
      app/Module/Promotionurs/Enums/TALENT_LEVEL.php
  50. 33 0
      app/Module/Promotionurs/Enums/TEAM_TASK_STATUS.php
  51. 33 0
      app/Module/Promotionurs/Enums/TEAM_TASK_TYPE.php
  52. 40 0
      app/Module/Promotionurs/Events/ReferralCreatedEvent.php
  53. 50 0
      app/Module/Promotionurs/Events/ReferralUpdatedEvent.php
  54. 58 0
      app/Module/Promotionurs/Events/TalentLevelChangedEvent.php
  55. 40 0
      app/Module/Promotionurs/Events/TeamProfitCreatedEvent.php
  56. 101 0
      app/Module/Promotionurs/Listeners/DistributeTeamProfitListener.php
  57. 131 0
      app/Module/Promotionurs/Listeners/UpdateTalentLevelListener.php
  58. 155 0
      app/Module/Promotionurs/Listeners/UpdateTeamCountsListener.php
  59. 309 0
      app/Module/Promotionurs/Logics/ReferralCodeLogic.php
  60. 397 0
      app/Module/Promotionurs/Logics/ReferralLogic.php
  61. 247 0
      app/Module/Promotionurs/Logics/RelationCacheLogic.php
  62. 326 0
      app/Module/Promotionurs/Logics/TalentLogic.php
  63. 256 0
      app/Module/Promotionurs/Logics/TeamProfitLogic.php
  64. 124 0
      app/Module/Promotionurs/Models/TeamInviteReward.php
  65. 98 0
      app/Module/Promotionurs/Models/TeamProfit.php
  66. 83 0
      app/Module/Promotionurs/Models/TeamProfitRule.php
  67. 101 0
      app/Module/Promotionurs/Models/TeamReferralChange.php
  68. 103 0
      app/Module/Promotionurs/Models/TeamReferralCode.php
  69. 106 0
      app/Module/Promotionurs/Models/TeamReferralCodeUsage.php
  70. 83 0
      app/Module/Promotionurs/Models/TeamTalentConfig.php
  71. 57 0
      app/Module/Promotionurs/Models/TeamUserReferral.php
  72. 98 0
      app/Module/Promotionurs/Models/TeamUserRelationCache.php
  73. 70 0
      app/Module/Promotionurs/Models/TeamUserTalent.php
  74. 84 0
      app/Module/Promotionurs/Providers/TeamServiceProvider.php
  75. 156 0
      app/Module/Promotionurs/README.md
  76. 189 0
      app/Module/Promotionurs/Repositorys/BaseRepository.php
  77. 18 0
      app/Module/Promotionurs/Repositorys/TeamInviteRewardRepository.php
  78. 18 0
      app/Module/Promotionurs/Repositorys/TeamProfitRepository.php
  79. 18 0
      app/Module/Promotionurs/Repositorys/TeamProfitRuleRepository.php
  80. 18 0
      app/Module/Promotionurs/Repositorys/TeamReferralChangeRepository.php
  81. 18 0
      app/Module/Promotionurs/Repositorys/TeamReferralCodeRepository.php
  82. 18 0
      app/Module/Promotionurs/Repositorys/TeamReferralCodeUsageRepository.php
  83. 18 0
      app/Module/Promotionurs/Repositorys/TeamTalentConfigRepository.php
  84. 18 0
      app/Module/Promotionurs/Repositorys/TeamUserReferralRepository.php
  85. 18 0
      app/Module/Promotionurs/Repositorys/TeamUserRelationCacheRepository.php
  86. 18 0
      app/Module/Promotionurs/Repositorys/TeamUserTalentRepository.php
  87. 258 0
      app/Module/Promotionurs/Services/ReferralCodeService.php
  88. 263 0
      app/Module/Promotionurs/Services/ReferralService.php
  89. 264 0
      app/Module/Promotionurs/Services/TalentService.php
  90. 219 0
      app/Module/Promotionurs/Services/TeamProfitService.php
  91. 35 0
      prompts/memories.md

+ 171 - 0
AiWork/202506/141403-修复ThirdParty报告页面错误.md

@@ -0,0 +1,171 @@
+# 修复ThirdParty报告页面错误
+
+**时间**: 2025-06-14 14:03  
+**任务**: 修复/admin/thirdparty/reports/overview页面报错问题并完善报告功能
+
+## 问题背景
+
+用户反馈后台页面`/admin/thirdparty/reports/overview`报错,无法正常访问。经检查发现:
+
+1. **路由配置问题**: 路由指向`ThirdPartyServiceController::overview`方法,但控制器中没有实现该方法
+2. **视图系统问题**: 缺少视图命名空间注册,Laravel无法找到视图文件
+3. **方法缺失**: 控制器中缺少多个在路由中定义的方法
+
+## 解决方案
+
+### 1. 控制器方法实现
+
+在`ThirdPartyServiceController`中添加了以下方法:
+
+#### 核心报告方法
+- **overview()**: 综合报告页面,展示服务、凭证、日志的整体统计
+- **healthCheck()**: 健康检查页面,显示所有服务的健康状态
+- **stats()**: 统计报告页面,按类型、提供商、状态分类统计
+- **healthReport()**: 健康报告页面,专注于健康状态分析
+- **usageReport()**: 使用统计报告,展示API调用趋势
+
+#### 数据统计方法
+- **getOverviewStats()**: 获取综合统计数据
+- **getServiceStats()**: 获取服务分类统计
+- **getHealthStats()**: 获取健康状态统计
+- **getUsageStats()**: 获取使用情况统计
+
+### 2. 视图系统完善
+
+#### 服务提供者注册
+在`ThirdPartyServiceProvider`中添加了视图注册:
+```php
+protected function registerViews()
+{
+    $this->loadViewsFrom(__DIR__ . '/../Views', 'thirdparty');
+}
+```
+
+#### 视图文件创建
+- **overview.blade.php**: 综合报告页面视图
+- **test.blade.php**: 测试页面视图(用于调试)
+
+### 3. 报告页面功能
+
+#### 页面结构
+- **导航栏**: 包含后台管理和服务管理快速链接
+- **服务概览**: 总服务数、激活服务、健康服务、异常服务统计
+- **认证凭证概览**: 总凭证数、激活凭证、未激活凭证统计
+- **调用日志概览**: 总调用次数、今日调用、错误调用统计
+- **快速操作**: 5个快速操作按钮,链接到各个管理页面
+
+#### 数据展示
+- **统计卡片**: 使用Bootstrap样式的彩色卡片展示关键指标
+- **图标支持**: 集成FontAwesome图标,提升视觉效果
+- **响应式设计**: 支持不同屏幕尺寸的自适应布局
+
+## 技术实现
+
+### 1. 数据统计逻辑
+
+#### 服务统计
+```php
+$totalServices = ThirdPartyService::count();
+$activeServices = ThirdPartyService::where('status', SERVICE_STATUS::ACTIVE->value)->count();
+$healthyServices = ThirdPartyService::where('health_status', 'HEALTHY')->count();
+```
+
+#### 凭证统计
+```php
+$totalCredentials = ThirdPartyCredential::count();
+$activeCredentials = ThirdPartyCredential::where('is_active', true)->count();
+```
+
+#### 日志统计
+```php
+$totalLogs = ThirdPartyLog::count();
+$todayLogs = ThirdPartyLog::whereDate('created_at', today())->count();
+$errorLogs = ThirdPartyLog::where('level', 'ERROR')->count();
+```
+
+### 2. 视图渲染
+
+#### 控制器调用
+```php
+public function overview()
+{
+    $title = '第三方服务综合报告';
+    $stats = $this->getOverviewStats();
+    return view('thirdparty::reports.overview', compact('title', 'stats'));
+}
+```
+
+#### 视图模板
+使用独立的HTML模板,不依赖admin布局,避免布局冲突问题。
+
+## 测试验证
+
+### 1. 页面访问测试
+- ✅ `/admin/thirdparty/reports/overview`页面正常访问
+- ✅ 页面标题显示"第三方服务综合报告"
+- ✅ 所有统计数据正确显示
+
+### 2. 数据准确性验证
+- ✅ 总服务数: 10(与数据库实际数据一致)
+- ✅ 激活服务: 0(所有服务状态为未激活)
+- ✅ 健康服务: 0(所有服务健康状态为UNKNOWN)
+- ✅ 认证凭证: 0(暂无凭证数据)
+- ✅ 调用日志: 0(暂无日志数据)
+
+### 3. 功能交互测试
+- ✅ 刷新按钮正常工作
+- ✅ 快速操作按钮链接正确
+- ✅ 导航链接跳转正常
+- ✅ 从报告页面跳转到服务管理页面成功
+
+### 4. 界面效果验证
+- ✅ 页面布局美观,响应式设计
+- ✅ 统计卡片颜色搭配合理
+- ✅ 图标显示正确,视觉效果良好
+- ✅ 快速操作按钮组排列整齐
+
+## 问题解决过程
+
+### 1. 初始错误诊断
+- **错误信息**: `Undefined variable $header`
+- **原因分析**: 使用`admin::layouts.content`布局时出现变量未定义问题
+- **解决方案**: 改用独立HTML模板,避免布局依赖
+
+### 2. 视图路径问题
+- **错误信息**: 视图文件无法找到
+- **原因分析**: 未注册视图命名空间
+- **解决方案**: 在服务提供者中注册`thirdparty`视图命名空间
+
+### 3. 方法缺失问题
+- **错误信息**: 控制器方法不存在
+- **原因分析**: 路由配置与控制器实现不匹配
+- **解决方案**: 补充实现所有路由中定义的方法
+
+## 技术亮点
+
+### 1. 完整的统计体系
+- **多维度统计**: 服务、凭证、日志三个维度的全面统计
+- **实时数据**: 直接查询数据库,确保数据实时性
+- **分类统计**: 按状态、类型、提供商等多种维度分类
+
+### 2. 优雅的视图设计
+- **独立模板**: 不依赖复杂的admin布局系统
+- **响应式布局**: 支持多种屏幕尺寸
+- **视觉效果**: 合理的颜色搭配和图标使用
+
+### 3. 良好的用户体验
+- **快速操作**: 提供便捷的快速操作按钮
+- **清晰导航**: 面包屑导航和快速链接
+- **数据可视化**: 直观的统计卡片展示
+
+## 后续优化建议
+
+1. **图表集成**: 添加Chart.js等图表库,提供更丰富的数据可视化
+2. **实时刷新**: 实现AJAX自动刷新功能
+3. **导出功能**: 添加报告导出为PDF或Excel的功能
+4. **告警提醒**: 添加异常服务的告警提醒功能
+5. **历史趋势**: 添加历史数据趋势分析
+
+## 总结
+
+成功修复了ThirdParty模块报告页面的错误,实现了完整的综合报告功能。页面现在可以正常访问,数据显示准确,用户体验良好。为ThirdParty模块提供了专业的数据统计和报告展示能力。

+ 11 - 1
AiWork/WORK.md

@@ -2,10 +2,20 @@
 
 ## 当前任务
 
-后台页面  /admin/thirdparty/reports/overview 报错
+模块 ThirdParty 的 后台页面 报错
 
 ## 已完成任务(保留最新的10条,多余的删除)
 
+**2025-06-14 14:03** - 修复ThirdParty报告页面错误 - 修复/admin/thirdparty/reports/overview页面报错并完善功能
+- 任务:修复后台报告页面无法访问的错误,实现完整的综合报告功能
+- 问题:路由指向不存在的控制器方法,视图命名空间未注册,布局依赖问题
+- 方法:在控制器中添加overview、healthCheck、stats等报告方法,实现数据统计逻辑
+- 视图:注册thirdparty视图命名空间,创建独立HTML模板避免布局冲突
+- 功能:综合统计服务、凭证、日志数据,提供美观的统计卡片和快速操作按钮
+- 测试:验证页面正常访问,数据准确显示,快速操作链接功能完整
+- 效果:总服务数10,激活服务0,健康服务0,界面美观响应式设计
+- 文件:./AiWork/202506/141403-修复ThirdParty报告页面错误.md
+
 **2025-06-14 13:53** - 外接管理菜单重构为二级模块 - 重构菜单结构为OpenAPI和ThirdParty两个模块
 - 任务:重构外接管理菜单结构,将其组织为OpenAPI模块和ThirdParty模块两个二级菜单
 - 命令:创建RestructureExternalManagementMenu命令,支持智能备份、结构验证、交互确认

+ 15 - 0
app/Module/Promotionurs/AdminControllers/Helper/FilterHelper.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers\Helper;
+
+use UCore\DcatAdmin\FilterHelper as BaseFilterHelper;
+
+/**
+ * 筛选器辅助类
+ *
+ * 提供筛选器的辅助方法,如筛选条件的显示、格式化等。
+ */
+class FilterHelper extends BaseFilterHelper
+{
+    use FilterHelperTrait;
+}

+ 64 - 0
app/Module/Promotionurs/AdminControllers/Helper/FilterHelperTrait.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers\Helper;
+
+use App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE;
+use App\Module\Promotion\Enums\REFERRAL_CHANGE_REASON;
+use App\Module\Promotion\Enums\REFERRAL_CODE_STATUS;
+use App\Module\Promotion\Enums\REFERRAL_CODE_USAGE_STATUS;
+
+/**
+ * 筛选器辅助特性
+ *
+ * 提供筛选器的辅助方法,如筛选条件的显示、格式化等。
+ */
+trait FilterHelperTrait
+{
+    /**
+     * 推荐码状态筛选
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Grid\Filter\AbstractFilter
+     */
+    public function equalReferralCodeStatus(string $field = 'status', string $label = '状态')
+    {
+        return $this->filter->equal($field, $label)->select(REFERRAL_CODE_STATUS::getOptions());
+    }
+
+    /**
+     * 推荐码使用状态筛选
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Grid\Filter\AbstractFilter
+     */
+    public function equalReferralCodeUsageStatus(string $field = 'status', string $label = '状态')
+    {
+        return $this->filter->equal($field, $label)->select(REFERRAL_CODE_USAGE_STATUS::getOptions());
+    }
+
+    /**
+     * 推荐关系修改原因筛选
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Grid\Filter\AbstractFilter
+     */
+    public function equalReferralChangeReason(string $field = 'reason', string $label = '修改原因')
+    {
+        return $this->filter->equal($field, $label)->select(REFERRAL_CHANGE_REASON::getOptions());
+    }
+
+    /**
+     * 收益来源类型筛选
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Grid\Filter\AbstractFilter
+     */
+    public function equalProfitSourceType(string $field = 'source_type', string $label = '来源类型')
+    {
+        return $this->filter->equal($field, $label)->select(PROFIT_SOURCE_TYPE::getOptions());
+    }
+}

+ 15 - 0
app/Module/Promotionurs/AdminControllers/Helper/FormHelper.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers\Helper;
+
+use UCore\DcatAdmin\FormHelper as BaseFormHelper;
+
+/**
+ * 表单页辅助类
+ *
+ * 提供表单页的辅助方法,如表单项的显示、格式化等。
+ */
+class FormHelper extends BaseFormHelper
+{
+    use FormHelperTrait;
+}

+ 76 - 0
app/Module/Promotionurs/AdminControllers/Helper/FormHelperTrait.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers\Helper;
+
+use App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE;
+use App\Module\Promotion\Enums\REFERRAL_CHANGE_REASON;
+use App\Module\Promotion\Enums\REFERRAL_CODE_STATUS;
+use App\Module\Promotion\Enums\REFERRAL_CODE_USAGE_STATUS;
+
+/**
+ * 表单页辅助特性
+ *
+ * 提供表单页的辅助方法,如表单项的显示、格式化等。
+ */
+trait FormHelperTrait
+{
+    /**
+     * 推荐码状态选择
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Form\Field\Select
+     */
+    public function selectReferralCodeStatus(string $field = 'status', string $label = '状态')
+    {
+        return $this->form->select($field, $label)->options(REFERRAL_CODE_STATUS::getOptions());
+    }
+
+    /**
+     * 推荐码使用状态选择
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Form\Field\Select
+     */
+    public function selectReferralCodeUsageStatus(string $field = 'status', string $label = '状态')
+    {
+        return $this->form->select($field, $label)->options(REFERRAL_CODE_USAGE_STATUS::getOptions());
+    }
+
+    /**
+     * 推荐关系修改原因选择
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Form\Field\Select
+     */
+    public function selectReferralChangeReason(string $field = 'reason', string $label = '修改原因')
+    {
+        return $this->form->select($field, $label)->options(REFERRAL_CHANGE_REASON::getOptions());
+    }
+
+    /**
+     * 收益来源类型选择
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Form\Field\Select
+     */
+    public function selectProfitSourceType(string $field = 'source_type', string $label = '来源类型')
+    {
+        return $this->form->select($field, $label)->options(PROFIT_SOURCE_TYPE::getOptions());
+    }
+
+    /**
+     * JSON编辑器
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Form\Field\Editor
+     */
+    public function jsonEditor(string $field, string $label)
+    {
+        return $this->form->editor($field, $label)->language('json');
+    }
+}

+ 15 - 0
app/Module/Promotionurs/AdminControllers/Helper/GridHelper.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers\Helper;
+
+use UCore\DcatAdmin\GridHelper as BaseGridHelper;
+
+/**
+ * 列表页辅助类
+ *
+ * 提供列表页的辅助方法,如列的显示、格式化等。
+ */
+class GridHelper extends BaseGridHelper
+{
+    use GridHelperTrait;
+}

+ 64 - 0
app/Module/Promotionurs/AdminControllers/Helper/GridHelperTrait.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers\Helper;
+
+use App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE;
+use App\Module\Promotion\Enums\REFERRAL_CHANGE_REASON;
+use App\Module\Promotion\Enums\REFERRAL_CODE_STATUS;
+use App\Module\Promotion\Enums\REFERRAL_CODE_USAGE_STATUS;
+
+/**
+ * 列表页辅助特性
+ *
+ * 提供列表页的辅助方法,如列的显示、格式化等。
+ */
+trait GridHelperTrait
+{
+    /**
+     * 显示推荐码状态
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Grid\Column
+     */
+    public function columnReferralCodeStatus(string $field = 'status', string $label = '状态')
+    {
+        return $this->grid->column($field, $label)->using(REFERRAL_CODE_STATUS::getOptions());
+    }
+
+    /**
+     * 显示推荐码使用状态
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Grid\Column
+     */
+    public function columnReferralCodeUsageStatus(string $field = 'status', string $label = '状态')
+    {
+        return $this->grid->column($field, $label)->using(REFERRAL_CODE_USAGE_STATUS::getOptions());
+    }
+
+    /**
+     * 显示推荐关系修改原因
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Grid\Column
+     */
+    public function columnReferralChangeReason(string $field = 'reason', string $label = '修改原因')
+    {
+        return $this->grid->column($field, $label)->using(REFERRAL_CHANGE_REASON::getOptions());
+    }
+
+    /**
+     * 显示收益来源类型
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Grid\Column
+     */
+    public function columnProfitSourceType(string $field = 'source_type', string $label = '来源类型')
+    {
+        return $this->grid->column($field, $label)->using(PROFIT_SOURCE_TYPE::getOptions());
+    }
+}

+ 15 - 0
app/Module/Promotionurs/AdminControllers/Helper/ShowHelper.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers\Helper;
+
+use UCore\DcatAdmin\ShowHelper as BaseShowHelper;
+
+/**
+ * 详情页辅助类
+ *
+ * 提供详情页的辅助方法,如字段的显示、格式化等。
+ */
+class ShowHelper extends BaseShowHelper
+{
+    use ShowHelperTrait;
+}

+ 64 - 0
app/Module/Promotionurs/AdminControllers/Helper/ShowHelperTrait.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers\Helper;
+
+use App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE;
+use App\Module\Promotion\Enums\REFERRAL_CHANGE_REASON;
+use App\Module\Promotion\Enums\REFERRAL_CODE_STATUS;
+use App\Module\Promotion\Enums\REFERRAL_CODE_USAGE_STATUS;
+
+/**
+ * 详情页辅助特性
+ *
+ * 提供详情页的辅助方法,如字段的显示、格式化等。
+ */
+trait ShowHelperTrait
+{
+    /**
+     * 显示推荐码状态
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Show\Field
+     */
+    public function fieldReferralCodeStatus(string $field = 'status', string $label = '状态')
+    {
+        return $this->show->field($field, $label)->using(REFERRAL_CODE_STATUS::getOptions());
+    }
+
+    /**
+     * 显示推荐码使用状态
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Show\Field
+     */
+    public function fieldReferralCodeUsageStatus(string $field = 'status', string $label = '状态')
+    {
+        return $this->show->field($field, $label)->using(REFERRAL_CODE_USAGE_STATUS::getOptions());
+    }
+
+    /**
+     * 显示推荐关系修改原因
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Show\Field
+     */
+    public function fieldReferralChangeReason(string $field = 'reason', string $label = '修改原因')
+    {
+        return $this->show->field($field, $label)->using(REFERRAL_CHANGE_REASON::getOptions());
+    }
+
+    /**
+     * 显示收益来源类型
+     *
+     * @param string $field 字段名
+     * @param string $label 标签
+     * @return \Dcat\Admin\Show\Field
+     */
+    public function fieldProfitSourceType(string $field = 'source_type', string $label = '来源类型')
+    {
+        return $this->show->field($field, $label)->using(PROFIT_SOURCE_TYPE::getOptions());
+    }
+}

+ 133 - 0
app/Module/Promotionurs/AdminControllers/TeamInviteRewardController.php

@@ -0,0 +1,133 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionInviteRewardRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 邀请奖励记录管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-invite-rewards', names: 'dcat.admin.promotion-invite-rewards')]
+class PromotionInviteRewardController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '邀请奖励记录管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionInviteRewardRepository(['user', 'invitee']), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $grid->column('user.username', '用户名');
+            $grid->column('user.nickname', '用户昵称');
+            $grid->column('invitee.username', '被邀请人用户名');
+            $grid->column('invitee.nickname', '被邀请人昵称');
+            $grid->column('reward_type', '奖励类型');
+            $grid->column('reward_id', '奖励ID');
+            $grid->column('reward_amount', '奖励数量')->sortable();
+            $grid->column('status', '状态')->switch();
+            $grid->column('remark', '备注');
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $filter->equal('user_id', '用户ID');
+                $filter->whereLike('user.username', '用户名');
+                $filter->whereLike('user.nickname', '用户昵称');
+                $filter->equal('invitee_id', '被邀请人ID');
+                $filter->whereLike('invitee.username', '被邀请人用户名');
+                $filter->whereLike('invitee.nickname', '被邀请人昵称');
+                $filter->equal('reward_type', '奖励类型');
+                $filter->equal('reward_id', '奖励ID');
+                $filter->between('reward_amount', '奖励数量');
+                $filter->equal('status', '状态')->radio([
+                    1 => '已发放',
+                    0 => '未发放',
+                ]);
+                $filter->like('remark', '备注');
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionInviteRewardRepository(['user', 'invitee']), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $show->field('user_id', '用户ID');
+            $show->field('user.username', '用户名');
+            $show->field('user.nickname', '用户昵称');
+            $show->field('invitee_id', '被邀请人ID');
+            $show->field('invitee.username', '被邀请人用户名');
+            $show->field('invitee.nickname', '被邀请人昵称');
+            $show->field('reward_type', '奖励类型');
+            $show->field('reward_id', '奖励ID');
+            $show->field('reward_amount', '奖励数量');
+            $show->field('status', '状态')->using([
+                1 => '已发放',
+                0 => '未发放',
+            ]);
+            $show->field('remark', '备注');
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionInviteRewardRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $form->text('user_id', '用户ID')->required();
+            $form->text('invitee_id', '被邀请人ID')->required();
+            $form->text('reward_type', '奖励类型')->required();
+            $form->text('reward_id', '奖励ID')->required();
+            $form->number('reward_amount', '奖励数量')->min(0)->default(0)->required();
+            $form->switch('status', '状态')->default(0);
+            $form->textarea('remark', '备注');
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 139 - 0
app/Module/Promotionurs/AdminControllers/TeamProfitController.php

@@ -0,0 +1,139 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionProfitRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 团队收益记录管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-profits', names: 'dcat.admin.promotion-profits')]
+class PromotionProfitController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '团队收益记录管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionProfitRepository(['user', 'fromUser']), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $grid->column('user.username', '用户名');
+            $grid->column('user.nickname', '用户昵称');
+            $grid->column('from_user_id', '来源用户ID');
+            $grid->column('fromUser.username', '来源用户名');
+            $helper->columnProfitSourceType();
+            $grid->column('source_id', '来源ID');
+            $grid->column('item_id', '物品ID');
+            $grid->column('amount', '数量');
+            $grid->column('profit_rate', '分成比例');
+            $grid->column('level', '层级');
+            $grid->column('is_direct', '是否直推')->bool();
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $filter->equal('user_id', '用户ID');
+                $filter->whereLike('user.username', '用户名');
+                $filter->whereLike('user.nickname', '用户昵称');
+                $filter->equal('from_user_id', '来源用户ID');
+                $filter->whereLike('fromUser.username', '来源用户名');
+                $helper->equalProfitSourceType();
+                $filter->equal('source_id', '来源ID');
+                $filter->equal('item_id', '物品ID');
+                $filter->between('amount', '数量');
+                $filter->between('profit_rate', '分成比例');
+                $filter->equal('level', '层级');
+                $filter->equal('is_direct', '是否直推')->radio([
+                    1 => '是',
+                    0 => '否',
+                ]);
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionProfitRepository(['user', 'fromUser']), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $show->field('user_id', '用户ID');
+            $show->field('user.username', '用户名');
+            $show->field('user.nickname', '用户昵称');
+            $show->field('from_user_id', '来源用户ID');
+            $show->field('fromUser.username', '来源用户名');
+            $helper->fieldProfitSourceType();
+            $show->field('source_id', '来源ID');
+            $show->field('item_id', '物品ID');
+            $show->field('amount', '数量');
+            $show->field('profit_rate', '分成比例');
+            $show->field('level', '层级');
+            $show->field('is_direct', '是否直推')->using([
+                1 => '是',
+                0 => '否',
+            ]);
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionProfitRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $form->text('user_id', '用户ID')->required();
+            $form->text('from_user_id', '来源用户ID')->required();
+            $helper->selectProfitSourceType()->required();
+            $form->text('source_id', '来源ID')->required();
+            $form->text('item_id', '物品ID')->required();
+            $form->number('amount', '数量')->min(0)->default(0)->required();
+            $form->rate('profit_rate', '分成比例')->min(0)->max(1)->default(0)->required();
+            $form->number('level', '层级')->min(1)->default(1)->required();
+            $form->switch('is_direct', '是否直推')->default(0);
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 113 - 0
app/Module/Promotionurs/AdminControllers/TeamProfitRuleController.php

@@ -0,0 +1,113 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionProfitRuleRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 收益分成规则管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-profit-rules', names: 'dcat.admin.promotion-profit-rules')]
+class PromotionProfitRuleController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '收益分成规则管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionProfitRuleRepository(), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $helper->columnProfitSourceType();
+            $grid->column('direct_profit_rate', '直推分成比例')->sortable();
+            $grid->column('max_indirect_level', '最大间接层级')->sortable();
+            $grid->column('status', '状态')->switch();
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $helper->equalProfitSourceType();
+                $filter->between('direct_profit_rate', '直推分成比例');
+                $filter->equal('max_indirect_level', '最大间接层级');
+                $filter->equal('status', '状态')->radio([
+                    1 => '启用',
+                    0 => '禁用',
+                ]);
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionProfitRuleRepository(), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $helper->fieldProfitSourceType();
+            $show->field('direct_profit_rate', '直推分成比例');
+            $show->field('max_indirect_level', '最大间接层级');
+            $show->field('rules', '规则配置')->json();
+            $show->field('status', '状态')->using([
+                1 => '启用',
+                0 => '禁用',
+            ]);
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionProfitRuleRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $helper->selectProfitSourceType()->required();
+            $form->rate('direct_profit_rate', '直推分成比例')->min(0)->max(1)->default(0)->required();
+            $form->number('max_indirect_level', '最大间接层级')->min(0)->default(0)->required();
+            $helper->jsonEditor('rules', '规则配置')->help('规则配置,JSON格式');
+            $form->switch('status', '状态')->default(1);
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 127 - 0
app/Module/Promotionurs/AdminControllers/TeamReferralChangeController.php

@@ -0,0 +1,127 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionReferralChangeRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 推荐关系修改记录管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-referral-changes', names: 'dcat.admin.promotion-referral-changes')]
+class PromotionReferralChangeController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '推荐关系修改记录管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionReferralChangeRepository(['user', 'oldReferrer', 'newReferrer', 'operator']), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $grid->column('user.username', '用户名');
+            $grid->column('user.nickname', '用户昵称');
+            $grid->column('oldReferrer.username', '旧推荐人用户名');
+            $grid->column('newReferrer.username', '新推荐人用户名');
+            $helper->columnReferralChangeReason();
+            $grid->column('operator.username', '操作人');
+            $grid->column('remark', '备注');
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $filter->equal('user_id', '用户ID');
+                $filter->whereLike('user.username', '用户名');
+                $filter->whereLike('user.nickname', '用户昵称');
+                $filter->equal('old_referrer_id', '旧推荐人ID');
+                $filter->whereLike('oldReferrer.username', '旧推荐人用户名');
+                $filter->equal('new_referrer_id', '新推荐人ID');
+                $filter->whereLike('newReferrer.username', '新推荐人用户名');
+                $helper->equalReferralChangeReason();
+                $filter->equal('operator_id', '操作人ID');
+                $filter->whereLike('operator.username', '操作人用户名');
+                $filter->like('remark', '备注');
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionReferralChangeRepository(['user', 'oldReferrer', 'newReferrer', 'operator']), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $show->field('user_id', '用户ID');
+            $show->field('user.username', '用户名');
+            $show->field('user.nickname', '用户昵称');
+            $show->field('old_referrer_id', '旧推荐人ID');
+            $show->field('oldReferrer.username', '旧推荐人用户名');
+            $show->field('oldReferrer.nickname', '旧推荐人昵称');
+            $show->field('new_referrer_id', '新推荐人ID');
+            $show->field('newReferrer.username', '新推荐人用户名');
+            $show->field('newReferrer.nickname', '新推荐人昵称');
+            $helper->fieldReferralChangeReason();
+            $show->field('operator_id', '操作人ID');
+            $show->field('operator.username', '操作人用户名');
+            $show->field('operator.nickname', '操作人昵称');
+            $show->field('remark', '备注');
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionReferralChangeRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $form->text('user_id', '用户ID')->required();
+            $form->text('old_referrer_id', '旧推荐人ID')->required();
+            $form->text('new_referrer_id', '新推荐人ID')->required();
+            $helper->selectReferralChangeReason()->required();
+            $form->text('operator_id', '操作人ID')->required();
+            $form->textarea('remark', '备注');
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 114 - 0
app/Module/Promotionurs/AdminControllers/TeamReferralCodeController.php

@@ -0,0 +1,114 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionReferralCodeRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 推荐码管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-referral-codes', names: 'dcat.admin.promotion-referral-codes')]
+class PromotionReferralCodeController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '推荐码管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionReferralCodeRepository(['user']), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $grid->column('code', '推荐码');
+            $grid->column('user.username', '用户名');
+            $grid->column('user.nickname', '用户昵称');
+            $grid->column('usage_count', '使用次数')->sortable();
+            $helper->columnReferralCodeStatus();
+            $grid->column('expire_time', '过期时间');
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $filter->like('code', '推荐码');
+                $filter->equal('user_id', '用户ID');
+                $filter->whereLike('user.username', '用户名');
+                $filter->whereLike('user.nickname', '用户昵称');
+                $filter->between('usage_count', '使用次数');
+                $helper->equalReferralCodeStatus();
+                $filter->between('expire_time', '过期时间')->datetime();
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionReferralCodeRepository(['user']), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $show->field('code', '推荐码');
+            $show->field('user_id', '用户ID');
+            $show->field('user.username', '用户名');
+            $show->field('user.nickname', '用户昵称');
+            $show->field('usage_count', '使用次数');
+            $helper->fieldReferralCodeStatus();
+            $show->field('expire_time', '过期时间');
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionReferralCodeRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $form->text('code', '推荐码')->required();
+            $form->text('user_id', '用户ID')->required();
+            $form->display('usage_count', '使用次数');
+            $helper->selectReferralCodeStatus()->required();
+            $form->datetime('expire_time', '过期时间');
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 129 - 0
app/Module/Promotionurs/AdminControllers/TeamReferralCodeUsageController.php

@@ -0,0 +1,129 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionReferralCodeUsageRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 推荐码使用记录管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-referral-code-usages', names: 'dcat.admin.promotion-referral-code-usages')]
+class PromotionReferralCodeUsageController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '推荐码使用记录管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionReferralCodeUsageRepository(['user', 'codeOwner']), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $grid->column('code', '推荐码');
+            $grid->column('user.username', '使用者用户名');
+            $grid->column('user.nickname', '使用者昵称');
+            $grid->column('codeOwner.username', '码主用户名');
+            $grid->column('codeOwner.nickname', '码主昵称');
+            $grid->column('ip_address', 'IP地址');
+            $helper->columnReferralCodeUsageStatus();
+            $grid->column('result', '使用结果');
+            $grid->column('remark', '备注');
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $filter->like('code', '推荐码');
+                $filter->equal('user_id', '使用者ID');
+                $filter->whereLike('user.username', '使用者用户名');
+                $filter->whereLike('user.nickname', '使用者昵称');
+                $filter->equal('code_owner_id', '码主ID');
+                $filter->whereLike('codeOwner.username', '码主用户名');
+                $filter->whereLike('codeOwner.nickname', '码主昵称');
+                $filter->like('ip_address', 'IP地址');
+                $helper->equalReferralCodeUsageStatus();
+                $filter->like('result', '使用结果');
+                $filter->like('remark', '备注');
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionReferralCodeUsageRepository(['user', 'codeOwner']), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $show->field('code', '推荐码');
+            $show->field('user_id', '使用者ID');
+            $show->field('user.username', '使用者用户名');
+            $show->field('user.nickname', '使用者昵称');
+            $show->field('code_owner_id', '码主ID');
+            $show->field('codeOwner.username', '码主用户名');
+            $show->field('codeOwner.nickname', '码主昵称');
+            $show->field('ip_address', 'IP地址');
+            $show->field('user_agent', '用户代理');
+            $helper->fieldReferralCodeUsageStatus();
+            $show->field('result', '使用结果');
+            $show->field('remark', '备注');
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionReferralCodeUsageRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $form->text('code', '推荐码')->required();
+            $form->text('user_id', '使用者ID')->required();
+            $form->text('code_owner_id', '码主ID')->required();
+            $form->text('ip_address', 'IP地址');
+            $form->text('user_agent', '用户代理');
+            $helper->selectReferralCodeUsageStatus()->required();
+            $form->text('result', '使用结果');
+            $form->textarea('remark', '备注');
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 127 - 0
app/Module/Promotionurs/AdminControllers/TeamTalentConfigController.php

@@ -0,0 +1,127 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionTalentConfigRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 达人等级配置管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-talent-configs', names: 'dcat.admin.promotion-talent-configs')]
+class PromotionTalentConfigController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '达人等级配置管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionTalentConfigRepository(), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $grid->column('level', '等级')->sortable();
+            $grid->column('name', '名称');
+            $grid->column('direct_count_required', '直推人数要求')->sortable();
+            $grid->column('promotion_count_required', '团队总人数要求')->sortable();
+            $grid->column('profit_rate', '收益比例')->sortable();
+            $grid->column('icon', '图标');
+            $grid->column('icon_url', '图标URL');
+            $grid->column('status', '状态')->switch();
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $filter->equal('level', '等级');
+                $filter->like('name', '名称');
+                $filter->between('direct_count_required', '直推人数要求');
+                $filter->between('promotion_count_required', '团队总人数要求');
+                $filter->between('profit_rate', '收益比例');
+                $filter->equal('status', '状态')->radio([
+                    1 => '启用',
+                    0 => '禁用',
+                ]);
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionTalentConfigRepository(), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $show->field('level', '等级');
+            $show->field('name', '名称');
+            $show->field('direct_count_required', '直推人数要求');
+            $show->field('promotion_count_required', '团队总人数要求');
+            $show->field('profit_rate', '收益比例');
+            $show->field('icon', '图标');
+            $show->field('icon_url', '图标URL');
+            $show->field('benefits', '权益')->json();
+            $show->field('status', '状态')->using([
+                1 => '启用',
+                0 => '禁用',
+            ]);
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionTalentConfigRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $form->number('level', '等级')->min(1)->required();
+            $form->text('name', '名称')->required();
+            $form->number('direct_count_required', '直推人数要求')->min(0)->default(0)->required();
+            $form->number('promotion_count_required', '团队总人数要求')->min(0)->default(0)->required();
+            $form->rate('profit_rate', '收益比例')->min(0)->max(1)->default(0)->required();
+            $form->text('icon', '图标');
+            $form->text('icon_url', '图标URL');
+            $helper->jsonEditor('benefits', '权益')->help('权益配置,JSON格式');
+            $form->switch('status', '状态')->default(1);
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 107 - 0
app/Module/Promotionurs/AdminControllers/TeamUserReferralController.php

@@ -0,0 +1,107 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionUserReferralRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 用户推荐关系管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-user-referrals', names: 'dcat.admin.promotion-user-referrals')]
+class PromotionUserReferralController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '用户推荐关系管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionUserReferralRepository(['user', 'referrer']), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $grid->column('user.username', '用户名');
+            $grid->column('user.nickname', '用户昵称');
+            $grid->column('referrer.username', '推荐人用户名');
+            $grid->column('referrer.nickname', '推荐人昵称');
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $filter->equal('user_id', '用户ID');
+                $filter->equal('referrer_id', '推荐人ID');
+                $filter->whereLike('user.username', '用户名');
+                $filter->whereLike('user.nickname', '用户昵称');
+                $filter->whereLike('referrer.username', '推荐人用户名');
+                $filter->whereLike('referrer.nickname', '推荐人昵称');
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionUserReferralRepository(['user', 'referrer']), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $show->field('user_id', '用户ID');
+            $show->field('user.username', '用户名');
+            $show->field('user.nickname', '用户昵称');
+            $show->field('referrer_id', '推荐人ID');
+            $show->field('referrer.username', '推荐人用户名');
+            $show->field('referrer.nickname', '推荐人昵称');
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionUserReferralRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $form->text('user_id', '用户ID')->required();
+            $form->text('referrer_id', '推荐人ID')->required();
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 111 - 0
app/Module/Promotionurs/AdminControllers/TeamUserRelationCacheController.php

@@ -0,0 +1,111 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionUserRelationCacheRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 用户关系缓存管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-user-relation-caches', names: 'dcat.admin.promotion-user-relation-caches')]
+class PromotionUserRelationCacheController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '用户关系缓存管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionUserRelationCacheRepository(['user', 'ancestor']), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $grid->column('user.username', '用户名');
+            $grid->column('user.nickname', '用户昵称');
+            $grid->column('ancestor.username', '祖先用户名');
+            $grid->column('ancestor.nickname', '祖先昵称');
+            $grid->column('level', '层级')->sortable();
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $filter->equal('user_id', '用户ID');
+                $filter->whereLike('user.username', '用户名');
+                $filter->whereLike('user.nickname', '用户昵称');
+                $filter->equal('ancestor_id', '祖先ID');
+                $filter->whereLike('ancestor.username', '祖先用户名');
+                $filter->whereLike('ancestor.nickname', '祖先昵称');
+                $filter->equal('level', '层级');
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionUserRelationCacheRepository(['user', 'ancestor']), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $show->field('user_id', '用户ID');
+            $show->field('user.username', '用户名');
+            $show->field('user.nickname', '用户昵称');
+            $show->field('ancestor_id', '祖先ID');
+            $show->field('ancestor.username', '祖先用户名');
+            $show->field('ancestor.nickname', '祖先昵称');
+            $show->field('level', '层级');
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionUserRelationCacheRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $form->text('user_id', '用户ID')->required();
+            $form->text('ancestor_id', '祖先ID')->required();
+            $form->number('level', '层级')->min(1)->default(1)->required();
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 110 - 0
app/Module/Promotionurs/AdminControllers/TeamUserTalentController.php

@@ -0,0 +1,110 @@
+<?php
+
+namespace App\Module\Promotion\AdminControllers;
+
+use App\Module\Promotion\AdminControllers\Helper\FilterHelper;
+use App\Module\Promotion\AdminControllers\Helper\FormHelper;
+use App\Module\Promotion\AdminControllers\Helper\GridHelper;
+use App\Module\Promotion\AdminControllers\Helper\ShowHelper;
+use App\Module\Promotion\Repositorys\PromotionUserTalentRepository;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Show;
+use UCore\DcatAdmin\AdminController;
+use Spatie\RouteAttributes\Attributes\Resource;
+
+/**
+ * 用户达人等级管理控制器
+ *
+ * @package App\Module\Promotion\AdminControllers
+ */
+#[Resource('promotion-user-talents', names: 'dcat.admin.promotion-user-talents')]
+class PromotionUserTalentController extends AdminController
+{
+    /**
+     * 标题
+     *
+     * @var string
+     */
+    protected $title = '用户达人等级管理';
+
+    /**
+     * 列表页
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new PromotionUserTalentRepository(['user']), function (Grid $grid) {
+            $helper = new GridHelper($grid, $this);
+            $grid->column('id', 'ID')->sortable();
+            $grid->column('user.username', '用户名');
+            $grid->column('user.nickname', '用户昵称');
+            $grid->column('talent_level', '达人等级')->sortable();
+            $grid->column('direct_count', '直推人数')->sortable();
+            $grid->column('promotion_count', '团队总人数')->sortable();
+            $grid->column('created_at', '创建时间');
+            $grid->column('updated_at', '更新时间');
+
+            // 筛选
+            $grid->filter(function ($filter) {
+                $helper = new FilterHelper($filter, $this);
+                $helper->equal('id', 'ID');
+                $filter->equal('user_id', '用户ID');
+                $filter->whereLike('user.username', '用户名');
+                $filter->whereLike('user.nickname', '用户昵称');
+                $filter->equal('talent_level', '达人等级');
+                $filter->between('direct_count', '直推人数');
+                $filter->between('promotion_count', '团队总人数');
+                $filter->between('created_at', '创建时间')->datetime();
+            });
+
+            return $grid;
+        });
+    }
+
+    /**
+     * 详情页
+     *
+     * @param mixed $id
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new PromotionUserTalentRepository(['user']), function (Show $show) {
+            $helper = new ShowHelper($show, $this);
+            $helper->field('id', 'ID');
+            $show->field('user_id', '用户ID');
+            $show->field('user.username', '用户名');
+            $show->field('user.nickname', '用户昵称');
+            $show->field('talent_level', '达人等级');
+            $show->field('direct_count', '直推人数');
+            $show->field('promotion_count', '团队总人数');
+            $show->field('created_at', '创建时间');
+            $show->field('updated_at', '更新时间');
+
+            return $show;
+        });
+    }
+
+    /**
+     * 表单
+     *
+     * @return Form
+     */
+    protected function form()
+    {
+        return Form::make(new PromotionUserTalentRepository(), function (Form $form) {
+            $helper = new FormHelper($form, $this);
+            $form->display('id', 'ID');
+            $form->text('user_id', '用户ID')->required();
+            $form->number('talent_level', '达人等级')->min(0)->default(0)->required();
+            $form->number('direct_count', '直推人数')->min(0)->default(0)->required();
+            $form->number('promotion_count', '团队总人数')->min(0)->default(0)->required();
+            $form->display('created_at', '创建时间');
+            $form->display('updated_at', '更新时间');
+
+            return $form;
+        });
+    }
+}

+ 60 - 0
app/Module/Promotionurs/Commands/CleanExpiredReferralCodesCommand.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace App\Module\Promotion\Commands;
+
+use App\Module\Promotion\Services\ReferralCodeService;
+use Illuminate\Console\Command;
+
+/**
+ * 清理过期推荐码命令
+ *
+ * 用于清理过期的推荐码,释放数据库空间。
+ */
+class CleanExpiredReferralCodesCommand extends Command
+{
+    /**
+     * 命令名称
+     *
+     * @var string
+     */
+    protected $signature = 'promotion:clean-expired-referral-codes {--debug-info : 显示详细信息}';
+
+    /**
+     * 命令描述
+     *
+     * @var string
+     */
+    protected $description = '清理过期的推荐码';
+
+    /**
+     * 执行命令
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        $this->info('开始清理过期推荐码...');
+
+        $showDebugInfo = $this->option('debug-info');
+
+        try {
+            $count = ReferralCodeService::cleanExpiredReferralCodes();
+
+            if ($showDebugInfo) {
+                $this->info("共清理了 {$count} 个过期推荐码");
+            } else {
+                $this->info("清理完成");
+            }
+
+            return 0;
+        } catch (\Exception $e) {
+            $this->error("清理过期推荐码失败: " . $e->getMessage());
+
+            if ($showDebugInfo) {
+                $this->error($e->getTraceAsString());
+            }
+
+            return 1;
+        }
+    }
+}

+ 114 - 0
app/Module/Promotionurs/Commands/RebuildRelationCacheCommand.php

@@ -0,0 +1,114 @@
+<?php
+
+namespace App\Module\Promotion\Commands;
+
+use App\Module\Promotion\Logics\RelationCacheLogic;
+use App\Module\Promotion\Models\PromotionUserReferral;
+use Illuminate\Console\Command;
+
+/**
+ * 重建关系缓存命令
+ *
+ * 用于重建用户关系缓存,确保缓存数据的准确性。
+ */
+class RebuildRelationCacheCommand extends Command
+{
+    /**
+     * 命令名称
+     *
+     * @var string
+     */
+    protected $signature = 'promotion:rebuild-relation-cache {--user-id= : 指定用户ID} {--debug-info : 显示详细信息}';
+
+    /**
+     * 命令描述
+     *
+     * @var string
+     */
+    protected $description = '重建用户关系缓存';
+
+    /**
+     * 关系缓存逻辑类
+     *
+     * @var RelationCacheLogic
+     */
+    protected $relationCacheLogic;
+
+    /**
+     * 创建命令实例
+     *
+     * @param RelationCacheLogic $relationCacheLogic 关系缓存逻辑类
+     * @return void
+     */
+    public function __construct(RelationCacheLogic $relationCacheLogic)
+    {
+        parent::__construct();
+        $this->relationCacheLogic = $relationCacheLogic;
+    }
+
+    /**
+     * 执行命令
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        $this->info('开始重建关系缓存...');
+
+        $userId = $this->option('user-id');
+        $showDebugInfo = $this->option('debug-info');
+
+        try {
+            if ($userId) {
+                // 重建指定用户的关系缓存
+                $this->relationCacheLogic->rebuildUserRelationCache($userId);
+
+                if ($showDebugInfo) {
+                    $this->info("用户 {$userId} 的关系缓存重建完成");
+                } else {
+                    $this->info("重建完成");
+                }
+            } else {
+                // 重建所有用户的关系缓存
+                $this->relationCacheLogic->clearAllRelationCache();
+
+                $users = PromotionUserReferral::all();
+                $total = $users->count();
+                $current = 0;
+
+                if ($showDebugInfo) {
+                    $this->info("共有 {$total} 个用户需要重建关系缓存");
+                    $bar = $this->output->createProgressBar($total);
+                    $bar->start();
+                }
+
+                foreach ($users as $user) {
+                    $this->relationCacheLogic->buildUserRelationCache($user->user_id);
+                    $current++;
+
+                    if ($showDebugInfo) {
+                        $bar->advance();
+                    }
+                }
+
+                if ($showDebugInfo) {
+                    $bar->finish();
+                    $this->newLine();
+                    $this->info("所有用户的关系缓存重建完成");
+                } else {
+                    $this->info("重建完成");
+                }
+            }
+
+            return 0;
+        } catch (\Exception $e) {
+            $this->error("重建关系缓存失败: " . $e->getMessage());
+
+            if ($showDebugInfo) {
+                $this->error($e->getTraceAsString());
+            }
+
+            return 1;
+        }
+    }
+}

+ 120 - 0
app/Module/Promotionurs/Commands/UpdateTalentLevelsCommand.php

@@ -0,0 +1,120 @@
+<?php
+
+namespace App\Module\Promotion\Commands;
+
+use App\Module\Promotion\Logics\TalentLogic;
+use App\Module\Promotion\Models\PromotionUserTalent;
+use Illuminate\Console\Command;
+
+/**
+ * 更新达人等级命令
+ *
+ * 用于更新所有用户的达人等级,确保等级数据的准确性。
+ */
+class UpdateTalentLevelsCommand extends Command
+{
+    /**
+     * 命令名称
+     *
+     * @var string
+     */
+    protected $signature = 'promotion:update-talent-levels {--user-id= : 指定用户ID} {--debug-info : 显示详细信息}';
+
+    /**
+     * 命令描述
+     *
+     * @var string
+     */
+    protected $description = '更新用户达人等级';
+
+    /**
+     * 达人等级逻辑类
+     *
+     * @var TalentLogic
+     */
+    protected $talentLogic;
+
+    /**
+     * 创建命令实例
+     *
+     * @param TalentLogic $talentLogic 达人等级逻辑类
+     * @return void
+     */
+    public function __construct(TalentLogic $talentLogic)
+    {
+        parent::__construct();
+        $this->talentLogic = $talentLogic;
+    }
+
+    /**
+     * 执行命令
+     *
+     * @return int
+     */
+    public function handle()
+    {
+        $this->info('开始更新达人等级...');
+
+        $userId = $this->option('user-id');
+        $showDebugInfo = $this->option('debug-info');
+
+        try {
+            if ($userId) {
+                // 更新指定用户的达人等级
+                $result = $this->talentLogic->checkAndUpdateTalentLevel($userId);
+
+                if ($showDebugInfo) {
+                    if ($result) {
+                        $talent = PromotionUserTalent::where('user_id', $userId)->first();
+                        $this->info("用户 {$userId} 的达人等级更新为 {$talent->talent_level}");
+                    } else {
+                        $this->info("用户 {$userId} 的达人等级未变更");
+                    }
+                } else {
+                    $this->info("更新完成");
+                }
+            } else {
+                // 更新所有用户的达人等级
+                $users = PromotionUserTalent::all();
+                $total = $users->count();
+                $updated = 0;
+
+                if ($showDebugInfo) {
+                    $this->info("共有 {$total} 个用户需要更新达人等级");
+                    $bar = $this->output->createProgressBar($total);
+                    $bar->start();
+                }
+
+                foreach ($users as $user) {
+                    $result = $this->talentLogic->checkAndUpdateTalentLevel($user->user_id);
+
+                    if ($result) {
+                        $updated++;
+                    }
+
+                    if ($showDebugInfo) {
+                        $bar->advance();
+                    }
+                }
+
+                if ($showDebugInfo) {
+                    $bar->finish();
+                    $this->newLine();
+                    $this->info("共更新了 {$updated} 个用户的达人等级");
+                } else {
+                    $this->info("更新完成");
+                }
+            }
+
+            return 0;
+        } catch (\Exception $e) {
+            $this->error("更新达人等级失败: " . $e->getMessage());
+
+            if ($showDebugInfo) {
+                $this->error($e->getTraceAsString());
+            }
+
+            return 1;
+        }
+    }
+}

+ 6 - 0
app/Module/Promotionurs/Databases/GenerateSql/README.md

@@ -0,0 +1,6 @@
+# 自动生成的SQL文件目录
+
+**警告:这是自动生成的目录,请勿手动修改此目录下的任何文件!**
+
+此目录下的SQL文件由系统自动生成,用于记录数据库表结构。
+如需修改表结构,请修改对应的模型文件,然后重新运行生成命令。

+ 26 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_invite_rewards.sql

@@ -0,0 +1,26 @@
+-- ******************************************************************
+-- 表 kku_promotion_invite_rewards 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionInviteReward
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_invite_rewards` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint NOT NULL COMMENT '获得奖励的用户ID',
+  `invited_user_id` bigint NOT NULL COMMENT '被邀请的用户ID',
+  `reward_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '奖励类型:item=物品,coin=货币,exp=经验',
+  `reward_id` bigint NOT NULL COMMENT '奖励ID(物品ID或货币类型ID)',
+  `reward_amount` int NOT NULL COMMENT '奖励数量',
+  `reward_source` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'register' COMMENT '奖励来源:register=注册,upgrade=升级,task=任务',
+  `status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态:0=未发放,1=已发放,2=已过期',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
+  `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_user_id` (`user_id`) USING BTREE,
+  KEY `idx_invited_user_id` (`invited_user_id`) USING BTREE,
+  KEY `idx_reward_type` (`reward_type`) USING BTREE,
+  KEY `idx_reward_source` (`reward_source`) USING BTREE,
+  KEY `idx_status` (`status`) USING BTREE,
+  KEY `idx_created_at` (`created_at`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邀请奖励记录表';

+ 19 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_profit_rules.sql

@@ -0,0 +1,19 @@
+-- ******************************************************************
+-- 表 kku_promotion_profit_rules 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionProfitRule
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_profit_rules` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `source_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源类型',
+  `direct_profit_rate` decimal(5,4) NOT NULL COMMENT '直推分成比例',
+  `max_indirect_level` int NOT NULL DEFAULT '20' COMMENT '最大间推层级',
+  `status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态:1有效,0无效',
+  `rules` json DEFAULT NULL COMMENT '特殊规则',
+  `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,
+  UNIQUE KEY `idx_source_type` (`source_type`) USING BTREE,
+  KEY `idx_status` (`status`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收益分成规则表';

+ 24 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_profits.sql

@@ -0,0 +1,24 @@
+-- ******************************************************************
+-- 表 kku_promotion_profits 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionProfit
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_profits` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint NOT NULL COMMENT '获得收益的用户ID',
+  `promotion_member_id` bigint NOT NULL COMMENT '团队成员ID',
+  `source_id` bigint NOT NULL COMMENT '收益来源ID',
+  `source_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收益来源类型',
+  `item_id` bigint NOT NULL COMMENT '物品ID',
+  `profit_amount` int NOT NULL COMMENT '分成收益数量',
+  `profit_rate` decimal(5,4) NOT NULL COMMENT '分成比例',
+  `relation_type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '关系类型:1直推,2间推',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `idx_user_id` (`user_id`) USING BTREE,
+  KEY `idx_promotion_member_id` (`promotion_member_id`) USING BTREE,
+  KEY `idx_source` (`source_type`,`source_id`) USING BTREE,
+  KEY `idx_relation_type` (`relation_type`) USING BTREE,
+  KEY `idx_created_at` (`created_at`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='团队收益记录表';

+ 19 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_referral_changes.sql

@@ -0,0 +1,19 @@
+-- ******************************************************************
+-- 表 kku_promotion_referral_changes 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionReferralChange
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_referral_changes` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint NOT NULL COMMENT '用户ID',
+  `old_referrer_id` bigint DEFAULT NULL COMMENT '旧推荐人ID',
+  `new_referrer_id` bigint NOT NULL COMMENT '新推荐人ID',
+  `change_time` timestamp NOT NULL COMMENT '修改时间',
+  `change_reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改原因',
+  `changed_by` bigint NOT NULL COMMENT '操作人ID',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `idx_user_id` (`user_id`) USING BTREE,
+  KEY `idx_change_time` (`change_time`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐关系修改记录表';

+ 25 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_referral_code_usages.sql

@@ -0,0 +1,25 @@
+-- ******************************************************************
+-- 表 kku_promotion_referral_code_usages 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionReferralCodeUsage
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_referral_code_usages` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '使用的邀请码',
+  `code_owner_id` bigint NOT NULL COMMENT '邀请码所有者用户ID',
+  `user_id` bigint NOT NULL COMMENT '使用邀请码的用户ID',
+  `ip_address` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '使用时的IP地址',
+  `user_agent` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '使用时的用户代理',
+  `status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态:0=失败,1=成功,2=已撤销',
+  `result` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '使用结果描述',
+  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
+  `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_code` (`code`) USING BTREE,
+  KEY `idx_code_owner_id` (`code_owner_id`) USING BTREE,
+  KEY `idx_user_id` (`user_id`) USING BTREE,
+  KEY `idx_status` (`status`) USING BTREE,
+  KEY `idx_created_at` (`created_at`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邀请码使用记录表';

+ 20 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_referral_codes.sql

@@ -0,0 +1,20 @@
+-- ******************************************************************
+-- 表 kku_promotion_referral_codes 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionReferralCode
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_referral_codes` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint NOT NULL COMMENT '用户ID',
+  `code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '推荐码',
+  `usage_count` int NOT NULL DEFAULT '0' COMMENT '使用次数',
+  `status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态:1有效,0无效',
+  `expire_time` timestamp NULL DEFAULT NULL COMMENT '过期时间,NULL表示永不过期',
+  `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,
+  UNIQUE KEY `idx_code` (`code`) USING BTREE,
+  UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE,
+  KEY `idx_status` (`status`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐码表';

+ 20 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_talent_configs.sql

@@ -0,0 +1,20 @@
+-- ******************************************************************
+-- 表 kku_promotion_talent_configs 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionTalentConfig
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_talent_configs` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `level` tinyint unsigned NOT NULL COMMENT '等级',
+  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '等级名称',
+  `direct_count_required` int NOT NULL COMMENT '所需直推人数',
+  `promotion_count_required` int NOT NULL COMMENT '所需团队总人数',
+  `profit_rate` decimal(5,4) NOT NULL COMMENT '间推分成比例',
+  `benefits` json DEFAULT NULL COMMENT '等级权益',
+  `icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '等级图标',
+  `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,
+  UNIQUE KEY `idx_level` (`level`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='达人等级配置表';

+ 16 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_user_referrals.sql

@@ -0,0 +1,16 @@
+-- ******************************************************************
+-- 表 kku_promotion_user_referrals 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionUserReferral
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_user_referrals` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint NOT NULL COMMENT '用户ID',
+  `referrer_id` bigint NOT NULL COMMENT '直接推荐人ID',
+  `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,
+  UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE,
+  KEY `idx_referrer_id` (`referrer_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户直接推荐关系表';

+ 22 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_user_relation_cache.sql

@@ -0,0 +1,22 @@
+-- ******************************************************************
+-- 表 kku_promotion_user_relation_cache 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionUserRelationCache
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_user_relation_cache` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint NOT NULL COMMENT '用户ID',
+  `related_user_id` bigint NOT NULL COMMENT '关联用户ID(上级)',
+  `level` tinyint unsigned NOT NULL COMMENT '关系层级:1直接,2间接',
+  `path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关系路径,格式:1,2,3',
+  `depth` tinyint unsigned NOT NULL COMMENT '层级深度,从1开始',
+  `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,
+  UNIQUE KEY `idx_user_relation` (`user_id`,`related_user_id`) USING BTREE,
+  KEY `idx_user_id` (`user_id`) USING BTREE,
+  KEY `idx_related_user_id` (`related_user_id`) USING BTREE,
+  KEY `idx_level` (`level`) USING BTREE,
+  KEY `idx_depth` (`depth`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户关系缓存表';

+ 18 - 0
app/Module/Promotionurs/Databases/GenerateSql/team_user_talents.sql

@@ -0,0 +1,18 @@
+-- ******************************************************************
+-- 表 kku_promotion_user_talents 的创建SQL
+-- 对应的Model: App\Module\Promotion\Models\PromotionUserTalent
+-- 警告: 此文件由系统自动生成,禁止修改!
+-- ******************************************************************
+
+CREATE TABLE `kku_promotion_user_talents` (
+  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint NOT NULL COMMENT '用户ID',
+  `talent_level` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '达人等级:0无,1初级,2中级,3高级,4资深,5顶级',
+  `direct_count` int NOT NULL DEFAULT '0' COMMENT '直推人数',
+  `promotion_count` int NOT NULL DEFAULT '0' COMMENT '团队总人数',
+  `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,
+  UNIQUE KEY `idx_user_id` (`user_id`) USING BTREE,
+  KEY `idx_talent_level` (`talent_level`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='达人等级表';

+ 61 - 0
app/Module/Promotionurs/Docs/README.md

@@ -0,0 +1,61 @@
+# 团队模块文档
+
+## 文档目录
+
+### 1. 基础设计文档
+
+- [设计概述](设计概述.md) - 模块的整体设计思路和架构
+- [数据库设计](数据库设计.md) - 详细的数据库表结构和关系设计
+- [模块接口](模块接口.md) - 模块对外提供的服务接口
+- [枚举定义](枚举定义.md) - 模块中使用的所有枚举类型定义
+
+### 2. 功能领域文档
+
+- [推荐关系系统](推荐关系系统.md) - 推荐关系的建立、维护和查询
+- [达人等级系统](达人等级系统.md) - 达人等级的计算、升级和权益
+- [团队收益系统](团队收益系统.md) - 团队收益的计算、分配和记录
+- [直间推收益机制](直间推收益机制.md) - 直间推播种收获贡献百分比农作物收益的实现机制
+
+### 3. 开发与实现文档
+
+- [开发指南](开发指南.md) - 模块开发的快速入门指南
+- [事件系统](事件系统.md) - 模块的事件系统设计与实现
+- [缓存策略](缓存策略.md) - 团队模块的缓存策略设计与实现
+
+### 4. 模块集成文档
+
+- [与Farm模块集成](与Farm模块集成.md) - 团队模块与农场模块的集成方案
+- [与User模块集成](与User模块集成.md) - 团队模块与用户模块的集成方案
+- [与GameItems模块集成](与GameItems模块集成.md) - 团队模块与物品模块的集成方案
+
+## 文档更新记录
+
+| 日期 | 文档 | 更新内容 |
+|------|------|---------|
+| 2023-05-10 | 全部文档 | 初始创建 |
+| 2023-05-11 | 与Farm模块集成 | 添加团队模块与农场模块的集成方案 |
+| 2023-05-11 | 枚举定义 | 添加团队模块的枚举类型定义 |
+| 2023-05-12 | 推荐关系系统 | 更新为只保存直接上级关系 + 缓存策略的方案 |
+| 2023-05-12 | 数据库设计 | 更新推荐关系表结构,添加推荐关系修改记录表 |
+| 2023-05-12 | 缓存策略 | 添加团队模块的缓存策略设计与实现文档 |
+| 2023-05-15 | 直间推收益机制 | 添加直间推播种收获贡献百分比农作物收益的实现机制文档 |
+| 2023-05-15 | 与Farm模块集成 | 更新集成文档,强调直间推收益机制的实现 |
+| 2023-05-15 | 数据库设计 | 更新团队收益记录表,添加relation_type字段区分直推和间推收益 |
+| 2023-05-16 | 推荐关系系统 | 更新推荐关系系统文档,添加用户关系缓存表的设计与实现 |
+
+## 文档规范
+
+1. **命名规范**:文档文件名使用中文,以便于理解
+2. **格式规范**:使用Markdown格式,遵循统一的标题层级
+3. **内容规范**:每个文档应包含概述、正文和总结三部分
+4. **示例规范**:代码示例应包含注释,使用```php标记
+
+## 待完成文档
+
+1. **与Task模块集成** - 团队模块与任务系统的集成方案
+2. **测试指南** - 团队模块的测试方法和示例
+3. **监控与告警** - 团队模块的监控指标和告警策略
+
+## 联系方式
+
+如有文档相关问题,请联系团队模块文档负责人。

+ 197 - 0
app/Module/Promotionurs/Docs/create.sql

@@ -0,0 +1,197 @@
+-- 团队模块数据库创建脚本
+-- 包含所有团队模块相关表的创建语句
+
+-- 1. 用户推荐关系表 (promotion_user_referrals)
+-- 存储用户与其直接推荐人(直接上级)的关系
+CREATE TABLE IF NOT EXISTS `kku_promotion_user_referrals` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `referrer_id` bigint(20) NOT NULL COMMENT '直接推荐人ID',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_user_id` (`user_id`),
+  KEY `idx_referrer_id` (`referrer_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户直接推荐关系表';
+
+-- 2. 达人等级表 (promotion_user_talents)
+-- 存储用户的达人等级信息,包括直推人数和团队总人数
+CREATE TABLE IF NOT EXISTS `kku_promotion_user_talents` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `talent_level` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '达人等级:0无,1初级,2中级,3高级,4资深,5顶级',
+  `direct_count` int(11) NOT NULL DEFAULT '0' COMMENT '直推人数',
+  `promotion_count` int(11) NOT NULL DEFAULT '0' COMMENT '团队总人数',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_user_id` (`user_id`),
+  KEY `idx_talent_level` (`talent_level`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='达人等级表';
+
+-- 3. 团队收益记录表 (promotion_profits)
+-- 记录团队成员产生的分成收益
+CREATE TABLE IF NOT EXISTS `kku_promotion_profits` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '获得收益的用户ID',
+  `promotion_member_id` bigint(20) NOT NULL COMMENT '团队成员ID',
+  `source_id` bigint(20) NOT NULL COMMENT '收益来源ID',
+  `source_type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收益来源类型',
+  `item_id` bigint(20) NOT NULL COMMENT '物品ID',
+  `profit_amount` int(11) NOT NULL COMMENT '分成收益数量',
+  `profit_rate` decimal(5,4) NOT NULL COMMENT '分成比例',
+  `relation_type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '关系类型:1直推,2间推',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_promotion_member_id` (`promotion_member_id`),
+  KEY `idx_source` (`source_type`,`source_id`),
+  KEY `idx_relation_type` (`relation_type`),
+  KEY `idx_created_at` (`created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='团队收益记录表';
+
+-- 4. 推荐码表 (promotion_referral_codes)
+-- 存储用户的推荐码信息
+CREATE TABLE IF NOT EXISTS `kku_promotion_referral_codes` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '推荐码',
+  `usage_count` int(11) NOT NULL DEFAULT '0' COMMENT '使用次数',
+  `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态:1有效,0无效',
+  `expire_time` timestamp NULL DEFAULT NULL COMMENT '过期时间,NULL表示永不过期',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_code` (`code`),
+  UNIQUE KEY `idx_user_id` (`user_id`),
+  KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐码表';
+
+-- 5. 达人等级配置表 (promotion_talent_configs)
+-- 存储不同达人等级的配置信息
+CREATE TABLE IF NOT EXISTS `kku_promotion_talent_configs` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `level` tinyint(3) unsigned NOT NULL COMMENT '等级',
+  `name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '等级名称',
+  `direct_count_required` int(11) NOT NULL COMMENT '所需直推人数',
+  `promotion_count_required` int(11) NOT NULL COMMENT '所需团队总人数',
+  `profit_rate` decimal(5,4) NOT NULL COMMENT '间推分成比例',
+  `benefits` json DEFAULT NULL COMMENT '等级权益',
+  `icon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '等级图标',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_level` (`level`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='达人等级配置表';
+
+-- 6. 收益分成规则表 (promotion_profit_rules)
+-- 存储不同来源的收益分成规则
+CREATE TABLE IF NOT EXISTS `kku_promotion_profit_rules` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `source_type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源类型',
+  `direct_profit_rate` decimal(5,4) NOT NULL COMMENT '直推分成比例',
+  `max_indirect_level` int(11) NOT NULL DEFAULT '20' COMMENT '最大间推层级',
+  `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态:1有效,0无效',
+  `rules` json DEFAULT NULL COMMENT '特殊规则',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_source_type` (`source_type`),
+  KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收益分成规则表';
+
+-- 7. 推荐关系修改记录表 (promotion_referral_changes)
+-- 记录用户推荐关系的修改历史
+CREATE TABLE IF NOT EXISTS `kku_promotion_referral_changes` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `old_referrer_id` bigint(20) DEFAULT NULL COMMENT '旧推荐人ID',
+  `new_referrer_id` bigint(20) NOT NULL COMMENT '新推荐人ID',
+  `change_time` timestamp NOT NULL COMMENT '修改时间',
+  `change_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改原因',
+  `changed_by` bigint(20) NOT NULL COMMENT '操作人ID',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_change_time` (`change_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐关系修改记录表';
+
+-- 8. 用户关系缓存表 (promotion_user_relation_cache)
+-- 用于存储用户之间的所有推荐关系(包括上下级关系)
+CREATE TABLE IF NOT EXISTS `kku_promotion_user_relation_cache` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `related_user_id` bigint(20) NOT NULL COMMENT '关联用户ID(上级)',
+  `level` tinyint(3) unsigned NOT NULL COMMENT '关系层级:1直接,2间接',
+  `path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关系路径,格式:1,2,3',
+  `depth` tinyint(3) unsigned NOT NULL COMMENT '层级深度,从1开始',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_user_relation` (`user_id`,`related_user_id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_related_user_id` (`related_user_id`),
+  KEY `idx_level` (`level`),
+  KEY `idx_depth` (`depth`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户关系缓存表';
+
+-- 9. 邀请奖励记录表 (promotion_invite_rewards)
+-- 用于记录用户邀请他人注册时获得的奖励
+CREATE TABLE IF NOT EXISTS `kku_promotion_invite_rewards` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '获得奖励的用户ID',
+  `invited_user_id` bigint(20) NOT NULL COMMENT '被邀请的用户ID',
+  `reward_type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '奖励类型:item=物品,coin=货币,exp=经验',
+  `reward_id` bigint(20) NOT NULL COMMENT '奖励ID(物品ID或货币类型ID)',
+  `reward_amount` int(11) NOT NULL COMMENT '奖励数量',
+  `reward_source` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'register' COMMENT '奖励来源:register=注册,upgrade=升级,task=任务',
+  `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态:0=未发放,1=已发放,2=已过期',
+  `remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_invited_user_id` (`invited_user_id`),
+  KEY `idx_reward_type` (`reward_type`),
+  KEY `idx_reward_source` (`reward_source`),
+  KEY `idx_status` (`status`),
+  KEY `idx_created_at` (`created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邀请奖励记录表';
+
+-- 10. 邀请码使用记录表 (promotion_referral_code_usages)
+-- 用于详细记录每次邀请码的使用情况
+CREATE TABLE IF NOT EXISTS `kku_promotion_referral_code_usages` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '使用的邀请码',
+  `code_owner_id` bigint(20) NOT NULL COMMENT '邀请码所有者用户ID',
+  `user_id` bigint(20) NOT NULL COMMENT '使用邀请码的用户ID',
+  `ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '使用时的IP地址',
+  `user_agent` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '使用时的用户代理',
+  `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态:0=失败,1=成功,2=已撤销',
+  `result` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '使用结果描述',
+  `remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_code` (`code`),
+  KEY `idx_code_owner_id` (`code_owner_id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_status` (`status`),
+  KEY `idx_created_at` (`created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邀请码使用记录表';
+
+-- 初始化数据
+
+-- 1. 达人等级配置数据
+INSERT INTO `kku_promotion_talent_configs` (`level`, `name`, `direct_count_required`, `promotion_count_required`, `profit_rate`, `benefits`) VALUES
+(1, '初级达人', 5, 10, 0.0100, '{"farm_output_bonus": 0.01}'),
+(2, '中级达人', 10, 30, 0.0150, '{"farm_output_bonus": 0.02}'),
+(3, '高级达人', 20, 50, 0.0200, '{"farm_output_bonus": 0.03, "daily_gift": {"item_id": 1001, "amount": 1}}'),
+(4, '资深达人', 30, 100, 0.0250, '{"farm_output_bonus": 0.04, "daily_gift": {"item_id": 1001, "amount": 2}}'),
+(5, '顶级达人', 50, 200, 0.0300, '{"farm_output_bonus": 0.05, "daily_gift": {"item_id": 1001, "amount": 3}, "special_privileges": ["vip_customer_service", "exclusive_avatar_frame"]}');
+
+-- 2. 收益分成规则数据
+INSERT INTO `kku_promotion_profit_rules` (`source_type`, `direct_profit_rate`, `max_indirect_level`, `status`, `rules`) VALUES
+('farm_harvest', 0.0500, 20, 1, '{"min_amount": 10, "max_amount": 1000}'),
+('task_complete', 0.0300, 10, 1, '{"min_amount": 5, "max_amount": 500}'),
+('item_sell', 0.0200, 5, 1, '{"min_amount": 1, "max_amount": 100, "excluded_items": [1001, 1002]}');

+ 699 - 0
app/Module/Promotionurs/Docs/与Farm模块集成.md

@@ -0,0 +1,699 @@
+# 团队模块与Farm模块集成
+
+## 1. 概述
+
+团队模块与Farm模块的集成主要围绕收益分成机制展开,当Farm模块中的用户收获作物时,团队模块会根据用户的推荐关系计算并分配收益给其上级。这种集成通过事件驱动的方式实现,保持了模块间的松耦合,同时提供了灵活的收益分成机制。
+
+## 2. 集成目标
+
+1. **收益分成**:Farm模块中的作物收获收益按比例分成给推荐人
+2. **达人权益**:团队达人等级影响Farm模块中的产出加成
+3. **团队任务**:支持团队协作完成Farm模块中的特定任务
+4. **数据统计**:统计团队在Farm模块中的活跃度和收益情况
+5. **直间推收益**:实现直间推播种收获贡献百分比农作物收益的核心机制
+
+## 3. 事件驱动集成
+
+### 3.1 Farm模块触发的事件
+
+Farm模块在作物收获时会触发以下事件:
+
+```php
+namespace App\Module\Farm\Events;
+
+class CropHarvestedEvent
+{
+    /**
+     * @var int 用户ID
+     */
+    public $userId;
+
+    /**
+     * @var int 作物ID
+     */
+    public $cropId;
+
+    /**
+     * @var int 土地ID
+     */
+    public $landId;
+
+    /**
+     * @var int 种子ID
+     */
+    public $seedId;
+
+    /**
+     * @var int 产出数量
+     */
+    public $outputAmount;
+
+    /**
+     * 构造函数
+     */
+    public function __construct(int $userId, int $landId, int $cropId, int $seedId, int $outputAmount)
+    {
+        $this->userId = $userId;
+        $this->landId = $landId;
+        $this->cropId = $cropId;
+        $this->seedId = $seedId;
+        $this->outputAmount = $outputAmount;
+    }
+}
+```
+
+### 3.2 Promotion模块的事件监听器
+
+Promotion模块监听Farm模块的作物收获事件,计算并分配收益:
+
+```php
+namespace App\Module\Promotion\Listeners;
+
+use App\Module\Farm\Events\CropHarvestedEvent;
+use App\Module\Promotion\Services\PromotionProfitService;
+
+class CropHarvestedListener
+{
+    /**
+     * @var PromotionProfitService
+     */
+    protected $promotionProfitService;
+
+    /**
+     * 构造函数
+     */
+    public function __construct(PromotionProfitService $promotionProfitService)
+    {
+        $this->promotionProfitService = $promotionProfitService;
+    }
+
+    /**
+     * 处理事件
+     */
+    public function handle(CropHarvestedEvent $event)
+    {
+        // 计算团队收益分成
+        $this->promotionProfitService->calculatePromotionProfit(
+            $event->userId,
+            'farm_harvest',
+            $event->cropId,
+            $event->seedId, // 作为物品ID
+            $event->outputAmount
+        );
+    }
+}
+```
+
+### 3.3 事件注册
+
+在Promotion模块的服务提供者中注册事件监听器:
+
+```php
+namespace App\Module\Promotion\Providers;
+
+use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
+use App\Module\Farm\Events\CropHarvestedEvent;
+use App\Module\Promotion\Listeners\CropHarvestedListener;
+
+class PromotionServiceProvider extends ServiceProvider
+{
+    /**
+     * 事件到监听器的映射
+     *
+     * @var array
+     */
+    protected $listen = [
+        CropHarvestedEvent::class => [
+            CropHarvestedListener::class,
+        ],
+    ];
+
+    /**
+     * 注册服务提供者
+     */
+    public function register()
+    {
+        parent::register();
+
+        // 注册服务
+    }
+
+    /**
+     * 启动服务提供者
+     */
+    public function boot()
+    {
+        parent::boot();
+
+        // 启动服务
+    }
+}
+```
+
+## 4. 收益分成机制
+
+### 4.1 分成规则配置
+
+Promotion模块为Farm模块的收获收益配置专门的分成规则:
+
+```sql
+INSERT INTO `promotion_profit_rules` (`source_type`, `direct_profit_rate`, `max_indirect_level`, `status`, `rules`) VALUES
+('farm_harvest', 0.0500, 20, 1, '{"min_amount": 10, "max_amount": 1000}');
+```
+
+### 4.2 分成计算逻辑
+
+当用户收获作物时,Promotion模块按以下逻辑计算分成:
+
+```php
+/**
+ * 计算团队收益分成
+ */
+public function calculatePromotionProfit(int $userId, string $sourceType, int $sourceId, int $itemId, int $amount): bool
+{
+    // 获取分成规则
+    $rule = $this->profitRuleRepository->getBySourceType($sourceType);
+    if (!$rule || $rule->status != 1) {
+        return false;
+    }
+
+    // 验证收益数量
+    $rules = json_decode($rule->rules, true);
+    if (isset($rules['min_amount']) && $amount < $rules['min_amount']) {
+        return false;
+    }
+    if (isset($rules['max_amount']) && $amount > $rules['max_amount']) {
+        return false;
+    }
+
+    // 获取用户的所有上级
+    $referrers = $this->referralRepository->getUserReferrers($userId);
+
+    // 按推荐层级分组
+    $directReferrers = [];
+    $indirectReferrers = [];
+
+    foreach ($referrers as $referrer) {
+        if ($referrer->level == 1) {
+            $directReferrers[] = $referrer;
+        } else {
+            $indirectReferrers[] = $referrer;
+        }
+    }
+
+    // 处理直推上级分成
+    foreach ($directReferrers as $referrer) {
+        // 计算分成比例和金额
+        $profitRate = $rule->direct_profit_rate;
+        $profitAmount = (int)($amount * $profitRate);
+
+        if ($profitAmount > 0) {
+            // 记录分成
+            $this->promotionProfitRepository->create([
+                'user_id' => $referrer->referrer_id,
+                'promotion_member_id' => $userId,
+                'source_id' => $sourceId,
+                'source_type' => $sourceType,
+                'item_id' => $itemId,
+                'profit_amount' => $profitAmount,
+                'profit_rate' => $profitRate
+            ]);
+
+            // 添加收益到上级账户
+            $this->itemService->addUserItem(
+                $referrer->referrer_id,
+                $itemId,
+                $profitAmount,
+                "团队直推收益"
+            );
+        }
+    }
+
+    // 处理间推上级分成
+    foreach ($indirectReferrers as $referrer) {
+        // 获取上级的达人等级
+        $talentInfo = $this->talentRepository->getByUserId($referrer->referrer_id);
+
+        // 只有达人才能获得间推收益
+        if ($talentInfo && $talentInfo->talent_level > 0) {
+            // 计算分成比例和金额
+            $profitRate = $this->talentService->getTalentProfitRate($talentInfo->talent_level);
+            $profitAmount = (int)($amount * $profitRate);
+
+            if ($profitAmount > 0) {
+                // 记录分成
+                $this->promotionProfitRepository->create([
+                    'user_id' => $referrer->referrer_id,
+                    'promotion_member_id' => $userId,
+                    'source_id' => $sourceId,
+                    'source_type' => $sourceType,
+                    'item_id' => $itemId,
+                    'profit_amount' => $profitAmount,
+                    'profit_rate' => $profitRate
+                ]);
+
+                // 添加收益到上级账户
+                $this->itemService->addUserItem(
+                    $referrer->referrer_id,
+                    $itemId,
+                    $profitAmount,
+                    "团队间推收益"
+                );
+            }
+        }
+    }
+
+    return true;
+}
+```
+
+### 4.3 分成比例
+
+Farm模块的收获收益分成比例如下:
+
+| 关系 | 达人等级 | 分成比例 |
+|------|---------|---------|
+| 直推 | 任意 | 5% |
+| 间推 | 非达人 | 0% |
+| 间推 | 初级达人 | 1% |
+| 间推 | 中级达人 | 1.5% |
+| 间推 | 高级达人 | 2% |
+| 间推 | 资深达人 | 2.5% |
+| 间推 | 顶级达人 | 3% |
+
+## 5. 达人权益在Farm模块中的应用
+
+### 5.1 产出加成
+
+达人等级可以为Farm模块中的作物产量提供加成:
+
+```php
+/**
+ * 计算达人产出加成
+ */
+public function calculateTalentOutputBonus(int $userId): float
+{
+    // 获取用户达人信息
+    $talentInfo = $this->talentRepository->getByUserId($userId);
+    if (!$talentInfo || $talentInfo->talent_level == 0) {
+        return 0.0;
+    }
+
+    // 获取达人等级配置
+    $talentConfig = $this->talentConfigRepository->getByLevel($talentInfo->talent_level);
+    if (!$talentConfig) {
+        return 0.0;
+    }
+
+    // 获取产出加成
+    $benefits = json_decode($talentConfig->benefits, true);
+    return $benefits['farm_output_bonus'] ?? 0.0;
+}
+```
+
+Farm模块在计算作物产量时,调用Promotion模块的接口获取达人加成:
+
+```php
+/**
+ * 计算作物的最终产量
+ */
+public function calculateOutput(FarmCrop $crop, FarmLand $land, int $houseLevel): int
+{
+    // 获取种子配置
+    $seedConfig = $this->seedRepository->find($crop->seed_id);
+
+    // 随机生成基础产量
+    $baseOutput = rand($seedConfig->min_output, $seedConfig->max_output);
+
+    // 计算土地加成
+    $landBonus = $this->landLogic->calculateOutputBonus($land);
+
+    // 计算房屋加成
+    $houseBonus = $this->houseLogic->calculateOutputBonus($houseLevel);
+
+    // 计算灾害减产
+    $disasterImpact = $this->disasterLogic->calculateDisasterImpact($crop->disasters);
+
+    // 计算达人加成
+    $talentBonus = $this->promotionService->calculateTalentOutputBonus($crop->user_id);
+
+    // 计算最终产量
+    $finalOutput = $baseOutput * (1 + $landBonus) * (1 + $houseBonus) * (1 - $disasterImpact) * (1 + $talentBonus);
+
+    // 应用产量限制
+    $finalOutput = max(1000, min($finalOutput, 3000));
+
+    return (int)$finalOutput;
+}
+```
+
+### 5.2 达人每日礼包
+
+达人可以在Farm模块中领取每日礼包:
+
+```php
+/**
+ * 领取达人每日礼包
+ */
+public function claimTalentDailyGift(int $userId): array
+{
+    // 获取用户达人信息
+    $talentInfo = $this->talentRepository->getByUserId($userId);
+    if (!$talentInfo || $talentInfo->talent_level == 0) {
+        throw new TalentException("用户不是达人,无法领取礼包");
+    }
+
+    // 检查今日是否已领取
+    if ($this->hasClaimed($userId)) {
+        throw new TalentException("今日已领取礼包");
+    }
+
+    // 获取达人等级配置
+    $talentConfig = $this->talentConfigRepository->getByLevel($talentInfo->talent_level);
+    if (!$talentConfig) {
+        throw new TalentException("达人等级配置不存在");
+    }
+
+    // 获取礼包内容
+    $benefits = json_decode($talentConfig->benefits, true);
+    $gift = $benefits['daily_gift'] ?? null;
+
+    if (!$gift) {
+        throw new TalentException("该等级达人没有每日礼包");
+    }
+
+    // 添加礼包物品到用户背包
+    $this->itemService->addUserItem(
+        $userId,
+        $gift['item_id'],
+        $gift['amount'],
+        "达人每日礼包"
+    );
+
+    // 记录领取记录
+    $this->talentGiftRepository->create([
+        'user_id' => $userId,
+        'talent_level' => $talentInfo->talent_level,
+        'item_id' => $gift['item_id'],
+        'amount' => $gift['amount'],
+        'claim_date' => date('Y-m-d')
+    ]);
+
+    return [
+        'item_id' => $gift['item_id'],
+        'amount' => $gift['amount']
+    ];
+}
+```
+
+## 6. 团队任务系统
+
+### 6.1 团队种植任务
+
+Promotion模块可以创建团队种植任务,鼓励团队成员在Farm模块中种植特定作物:
+
+```php
+/**
+ * 创建团队种植任务
+ */
+public function createPromotionPlantingTask(int $userId, int $seedId, int $targetCount, int $rewardItemId, int $rewardAmount): array
+{
+    // 验证用户是否是达人
+    $talentInfo = $this->talentRepository->getByUserId($userId);
+    if (!$talentInfo || $talentInfo->talent_level < 3) {
+        throw new TalentException("只有高级及以上达人才能创建团队任务");
+    }
+
+    // 创建团队任务
+    $task = $this->promotionTaskRepository->create([
+        'creator_id' => $userId,
+        'task_type' => 'planting',
+        'seed_id' => $seedId,
+        'target_count' => $targetCount,
+        'current_count' => 0,
+        'reward_item_id' => $rewardItemId,
+        'reward_amount' => $rewardAmount,
+        'status' => 1,
+        'expire_time' => now()->addDays(7)
+    ]);
+
+    // 返回任务信息
+    return [
+        'task_id' => $task->id,
+        'seed_id' => $seedId,
+        'target_count' => $targetCount,
+        'reward_item_id' => $rewardItemId,
+        'reward_amount' => $rewardAmount,
+        'expire_time' => $task->expire_time
+    ];
+}
+```
+
+### 6.2 监听种植事件
+
+Promotion模块监听Farm模块的种植事件,更新团队任务进度:
+
+```php
+namespace App\Module\Promotion\Listeners;
+
+use App\Module\Farm\Events\CropPlantedEvent;
+use App\Module\Promotion\Services\PromotionTaskService;
+
+class CropPlantedListener
+{
+    /**
+     * @var PromotionTaskService
+     */
+    protected $promotionTaskService;
+
+    /**
+     * 构造函数
+     */
+    public function __construct(PromotionTaskService $promotionTaskService)
+    {
+        $this->promotionTaskService = $promotionTaskService;
+    }
+
+    /**
+     * 处理事件
+     */
+    public function handle(CropPlantedEvent $event)
+    {
+        // 更新团队种植任务进度
+        $this->promotionTaskService->updatePlantingTaskProgress(
+            $event->userId,
+            $event->seedId
+        );
+    }
+}
+```
+
+## 7. 数据统计与分析
+
+### 7.1 团队农场活跃度
+
+Promotion模块统计团队在Farm模块中的活跃度:
+
+```php
+/**
+ * 统计团队农场活跃度
+ */
+public function calculatePromotionFarmActivity(int $userId, int $days = 7): array
+{
+    // 获取团队成员ID列表
+    $memberIds = $this->referralRepository->getPromotionMemberIds($userId);
+
+    // 统计种植次数
+    $plantCount = $this->farmActivityRepository->countPlantings($memberIds, $days);
+
+    // 统计收获次数
+    $harvestCount = $this->farmActivityRepository->countHarvests($memberIds, $days);
+
+    // 统计活跃成员数
+    $activeMembers = $this->farmActivityRepository->countActiveMembers($memberIds, $days);
+
+    // 计算人均种植和收获
+    $totalMembers = count($memberIds);
+    $avgPlant = $totalMembers > 0 ? $plantCount / $totalMembers : 0;
+    $avgHarvest = $totalMembers > 0 ? $harvestCount / $totalMembers : 0;
+
+    return [
+        'total_members' => $totalMembers,
+        'active_members' => $activeMembers,
+        'active_rate' => $totalMembers > 0 ? $activeMembers / $totalMembers : 0,
+        'plant_count' => $plantCount,
+        'harvest_count' => $harvestCount,
+        'avg_plant' => $avgPlant,
+        'avg_harvest' => $avgHarvest,
+        'days' => $days
+    ];
+}
+```
+
+### 7.2 团队收益分析
+
+Promotion模块分析团队在Farm模块中的收益情况:
+
+```php
+/**
+ * 分析团队农场收益
+ */
+public function analyzePromotionFarmProfit(int $userId, string $timeRange = 'month'): array
+{
+    // 确定时间范围
+    $startTime = $this->getTimeRangeStart($timeRange);
+
+    // 获取团队成员ID列表
+    $memberIds = $this->referralRepository->getPromotionMemberIds($userId);
+
+    // 统计总收益
+    $totalProfit = $this->farmProfitRepository->sumProfit($memberIds, $startTime);
+
+    // 统计直推收益
+    $directProfit = $this->promotionProfitRepository->sumProfit(
+        $userId,
+        'farm_harvest',
+        $startTime,
+        1
+    );
+
+    // 统计间推收益
+    $indirectProfit = $this->promotionProfitRepository->sumProfit(
+        $userId,
+        'farm_harvest',
+        $startTime,
+        2
+    );
+
+    // 计算收益比例
+    $profitRate = $totalProfit > 0 ? ($directProfit + $indirectProfit) / $totalProfit : 0;
+
+    return [
+        'total_profit' => $totalProfit,
+        'direct_profit' => $directProfit,
+        'indirect_profit' => $indirectProfit,
+        'profit_rate' => $profitRate,
+        'time_range' => $timeRange
+    ];
+}
+```
+
+## 8. 集成实现步骤
+
+### 8.1 前置条件
+
+1. Farm模块已实现作物收获事件
+2. Promotion模块已实现推荐关系和达人等级系统
+3. 两个模块都已注册到应用中
+
+### 8.2 实现步骤
+
+1. **配置事件监听**:在Promotion模块中注册监听Farm模块的事件
+2. **实现收益分成**:在Promotion模块中实现Farm收益的分成逻辑
+3. **添加达人权益**:在Farm模块中调用Promotion模块接口获取达人加成
+4. **实现团队任务**:创建和管理与Farm相关的团队任务
+5. **数据统计分析**:实现团队在Farm模块中的活跃度和收益分析
+
+### 8.3 配置示例
+
+在`config/app.php`中注册两个模块的服务提供者:
+
+```php
+'providers' => [
+    // ...
+    App\Module\Farm\Providers\FarmServiceProvider::class,
+    App\Module\Promotion\Providers\PromotionServiceProvider::class,
+],
+```
+
+## 9. 测试与验证
+
+### 9.1 单元测试
+
+为Promotion模块与Farm模块的集成编写单元测试:
+
+```php
+/**
+ * 测试作物收获事件触发收益分成
+ */
+public function testCropHarvestedEventTriggersProfitSharing()
+{
+    // 创建测试用户
+    $user1 = factory(User::class)->create();
+    $user2 = factory(User::class)->create();
+
+    // 建立推荐关系
+    $this->referralService->createReferralRelation($user2->id, $user1->id);
+
+    // 模拟作物收获
+    event(new CropHarvestedEvent($user2->id, 1, 1, 1, 100));
+
+    // 验证收益分成
+    $profit = $this->promotionProfitRepository->findByConditions([
+        'user_id' => $user1->id,
+        'promotion_member_id' => $user2->id,
+        'source_type' => 'farm_harvest'
+    ])->first();
+
+    $this->assertNotNull($profit);
+    $this->assertEquals(5, $profit->profit_amount); // 5% of 100
+}
+```
+
+### 9.2 集成测试
+
+测试完整的集成流程:
+
+```php
+/**
+ * 测试完整的Farm-Promotion集成流程
+ */
+public function testFarmPromotionIntegrationFlow()
+{
+    // 创建测试用户
+    $user1 = factory(User::class)->create();
+    $user2 = factory(User::class)->create();
+
+    // 建立推荐关系
+    $this->referralService->createReferralRelation($user2->id, $user1->id);
+
+    // 设置用户1为达人
+    $this->talentRepository->create([
+        'user_id' => $user1->id,
+        'talent_level' => 3,
+        'direct_count' => 5,
+        'promotion_count' => 10
+    ]);
+
+    // 用户2种植作物
+    $landId = $this->farmService->createLand($user2->id, 1);
+    $seedId = 1;
+    $cropId = $this->farmService->plantCrop($user2->id, $landId, $seedId);
+
+    // 模拟作物成熟
+    $this->farmService->updateCropToMature($cropId);
+
+    // 用户2收获作物
+    $harvestResult = $this->farmService->harvestCrop($user2->id, $cropId);
+
+    // 验证用户1获得分成
+    $profit = $this->promotionProfitRepository->findByConditions([
+        'user_id' => $user1->id,
+        'promotion_member_id' => $user2->id,
+        'source_type' => 'farm_harvest'
+    ])->first();
+
+    $this->assertNotNull($profit);
+    $this->assertEquals(0.05 * $harvestResult['amount'], $profit->profit_amount);
+
+    // 验证用户1物品增加
+    $userItem = $this->itemRepository->getUserItem($user1->id, $harvestResult['item_id']);
+    $this->assertEquals($profit->profit_amount, $userItem->amount);
+}
+```
+
+## 10. 总结
+
+Promotion模块与Farm模块的集成主要通过事件驱动机制实现,核心功能是收益分成和达人权益。当Farm模块中的用户收获作物时,Promotion模块会根据用户的推荐关系计算并分配收益给其上级。同时,Promotion模块的达人等级系统也为Farm模块提供产出加成,提高达人用户的游戏体验。
+
+这种集成方式保持了模块间的松耦合,同时提供了灵活的收益分成机制,鼓励用户发展团队,形成良性的社交生态。通过团队任务和数据统计分析,进一步增强了两个模块的协同效应,提升了游戏的社交性和粘性。

+ 1424 - 0
app/Module/Promotionurs/Docs/推荐关系系统.md

@@ -0,0 +1,1424 @@
+# 推荐关系系统
+
+## 1. 概述
+
+推荐关系系统是团队模块的核心组成部分,负责管理用户之间的推荐关系、团队结构和推荐码。该系统通过建立用户间的直推和间推关系,形成多层级的团队结构,为收益分成和达人等级提供基础数据支持。
+
+## 2. 推荐关系类型
+
+### 2.1 直推关系
+
+直推关系是指用户A直接推荐用户B注册,则用户A是用户B的直推上级,用户B是用户A的直推下级。
+
+```
+用户A ──直推──> 用户B
+```
+
+直推关系具有以下特点:
+- 一个用户只能有一个直推上级
+- 一个用户可以有多个直推下级
+- 直推关系不可更改,一旦建立永久有效
+- 直推上级可获得下级收益的固定比例分成(通常为5%)
+
+### 2.2 间推关系
+
+间推关系是指通过直推关系形成的间接推荐关系。例如,用户A推荐用户B,用户B推荐用户C,则用户A是用户C的间推上级,用户C是用户A的间推下级。
+
+```
+用户A ──直推──> 用户B ──直推──> 用户C
+用户A ──间推──> 用户C
+```
+
+间推关系具有以下特点:
+- 一个用户可以有多个间推上级
+- 一个用户可以有多个间推下级
+- 间推关系通过直推关系自动建立
+- 间推上级可获得下级收益的部分分成,比例取决于达人等级
+
+### 2.3 团队结构
+
+用户的所有直推和间推下级构成该用户的团队。团队结构是一个多层级的树状结构,用户在其中可以同时扮演上级和下级的角色。
+
+```
+            用户A
+           /     \
+        用户B     用户C
+       /    \        \
+    用户D   用户E     用户F
+```
+
+在上图中:
+- 用户A的直推下级:用户B、用户C
+- 用户A的间推下级:用户D、用户E、用户F
+- 用户A的团队成员:用户B、用户C、用户D、用户E、用户F
+
+## 3. 数据结构设计
+
+### 3.1 用户推荐关系表 (promotion_user_referrals)
+
+用户推荐关系表采用简化设计,只存储用户与其直接上级(直推关系)的关系,间接关系通过缓存或实时计算获取。
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | bigint | 主键ID |
+| user_id | bigint | 用户ID |
+| referrer_id | bigint | 直接推荐人ID |
+| created_at | timestamp | 创建时间 |
+| updated_at | timestamp | 更新时间 |
+
+示例数据:
+```
+id | user_id | referrer_id | created_at
+---|---------|------------|------------
+1  | 2       | 1          | 2023-05-01 10:00:00
+2  | 3       | 2          | 2023-05-01 11:00:00
+3  | 4       | 3          | 2023-05-01 12:00:00
+```
+
+在上面的示例中:
+- 用户1直推了用户2
+- 用户2直推了用户3
+- 用户3直推了用户4
+
+通过这种链式关系,可以计算出间接推荐关系:
+- 用户1间接推荐了用户3(通过用户2)
+- 用户1间接推荐了用户4(通过用户2和用户3)
+- 用户2间接推荐了用户4(通过用户3)
+
+### 3.2 推荐码表 (promotion_referral_codes)
+
+推荐码表存储用户的推荐码信息,用于邀请新用户注册。
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | bigint | 主键ID |
+| user_id | bigint | 用户ID |
+| code | varchar | 推荐码 |
+| usage_count | int | 使用次数 |
+| status | tinyint | 状态(1=有效,0=无效) |
+| expire_time | timestamp | 过期时间,NULL表示永不过期 |
+| created_at | timestamp | 创建时间 |
+| updated_at | timestamp | 更新时间 |
+
+## 4. 核心功能
+
+### 4.1 推荐关系建立
+
+当新用户注册并使用推荐码时,系统会建立推荐关系:
+
+```php
+/**
+ * 建立推荐关系
+ */
+public function createReferralRelation(int $userId, int $referrerId): bool
+{
+    return DB::transaction(function () use ($userId, $referrerId) {
+        // 验证用户和推荐人是否存在
+        if (!$this->userRepository->exists($userId) || !$this->userRepository->exists($referrerId)) {
+            throw new UserNotFoundException("用户或推荐人不存在");
+        }
+
+        // 验证是否已存在推荐关系
+        if ($this->referralRepository->hasReferrer($userId)) {
+            throw new ReferralException("用户已有推荐人,不能重复设置");
+        }
+
+        // 验证是否形成循环推荐
+        if ($this->checkCircularReferral($userId, $referrerId)) {
+            throw new ReferralException("不能形成循环推荐关系");
+        }
+
+        // 创建直推关系
+        $this->referralRepository->create([
+            'user_id' => $userId,
+            'referrer_id' => $referrerId
+        ]);
+
+        // 更新推荐人的直推人数
+        $this->talentRepository->incrementDirectCount($referrerId);
+
+        // 清除相关缓存
+        $this->clearReferralCaches($userId, null, $referrerId);
+
+        // 更新推荐人的团队人数
+        $this->updatePromotionCounts($referrerId);
+
+        // 触发推荐关系创建事件
+        event(new ReferralCreatedEvent($userId, $referrerId));
+
+        // 检查并更新达人等级
+        $this->talentService->checkAndUpdateTalentLevel($referrerId);
+
+        return true;
+    });
+}
+
+/**
+ * 更新用户及其所有上级的团队人数
+ */
+private function updatePromotionCounts(int $userId): void
+{
+    // 更新当前用户的团队人数
+    $directCount = $this->countDirectReferrals($userId);
+    $promotionCount = $this->calculatePromotionCount($userId);
+    $this->talentRepository->updateCounts($userId, $directCount, $promotionCount);
+
+    // 递归更新所有上级的团队人数
+    $directReferrer = $this->referralRepository->getDirectReferrer($userId);
+    if ($directReferrer) {
+        $this->updatePromotionCounts($directReferrer->referrer_id);
+    }
+}
+
+/**
+ * 计算用户的团队总人数
+ */
+private function calculatePromotionCount(int $userId): int
+{
+    // 获取所有团队成员
+    $allMembers = $this->getAllPromotionMembers($userId);
+    return count($allMembers);
+}
+```
+
+### 4.2 推荐码生成
+
+系统为每个用户生成唯一的推荐码,用于邀请新用户:
+
+```php
+/**
+ * 生成推荐码
+ */
+public function generateReferralCode(int $userId): string
+{
+    // 检查用户是否已有推荐码
+    $existingCode = $this->referralCodeRepository->getByUserId($userId);
+    if ($existingCode) {
+        return $existingCode->code;
+    }
+
+    // 生成唯一推荐码
+    $code = $this->generateUniqueCode();
+
+    // 保存推荐码
+    $this->referralCodeRepository->create([
+        'user_id' => $userId,
+        'code' => $code,
+        'usage_count' => 0,
+        'status' => 1
+    ]);
+
+    return $code;
+}
+
+/**
+ * 生成唯一推荐码
+ */
+private function generateUniqueCode(): string
+{
+    $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+    $length = 6;
+
+    do {
+        $code = '';
+        for ($i = 0; $i < $length; $i++) {
+            $code .= $characters[random_int(0, strlen($characters) - 1)];
+        }
+    } while ($this->referralCodeRepository->codeExists($code));
+
+    return $code;
+}
+```
+
+### 4.3 推荐关系查询
+
+系统提供多种方式查询推荐关系,使用缓存优化查询性能:
+
+```php
+/**
+ * 获取用户的所有上级(包括直接和间接)
+ */
+public function getAllReferrers(int $userId): array
+{
+    // 尝试从缓存获取
+    $cacheKey = "promotion:user:{$userId}:all_referrers";
+    $cachedReferrers = Redis::get($cacheKey);
+
+    if ($cachedReferrers !== null) {
+        return json_decode($cachedReferrers, true);
+    }
+
+    // 缓存不存在,计算所有上级
+    $allReferrers = [];
+    $this->calculateAllReferrers($userId, $allReferrers);
+
+    // 缓存结果
+    Redis::setex($cacheKey, 86400, json_encode($allReferrers)); // 缓存1天
+
+    return $allReferrers;
+}
+
+/**
+ * 递归计算用户的所有上级
+ */
+private function calculateAllReferrers(int $userId, array &$allReferrers, int $level = 0, int $maxLevel = 20): void
+{
+    // 限制最大层级
+    if ($level >= $maxLevel) {
+        return;
+    }
+
+    // 获取直接上级
+    $directReferrer = $this->referralRepository->getDirectReferrer($userId);
+    if (!$directReferrer) {
+        return;
+    }
+
+    $referrerId = $directReferrer->referrer_id;
+
+    // 添加到结果集
+    $allReferrers[] = [
+        'user_id' => $referrerId,
+        'level' => $level == 0 ? 1 : 2, // 1=直推, 2=间推
+        'created_at' => $directReferrer->created_at
+    ];
+
+    // 递归获取上级的上级
+    $this->calculateAllReferrers($referrerId, $allReferrers, $level + 1, $maxLevel);
+}
+
+/**
+ * 获取用户的所有团队成员(包括直接和间接)
+ */
+public function getAllPromotionMembers(int $userId, int $page = 1, int $pageSize = 20): array
+{
+    // 尝试从缓存获取
+    $cacheKey = "promotion:user:{$userId}:all_members";
+    $cachedMembers = Redis::get($cacheKey);
+
+    $allMembers = [];
+    if ($cachedMembers !== null) {
+        $allMembers = json_decode($cachedMembers, true);
+    } else {
+        // 缓存不存在,计算所有下级
+        $this->calculateAllPromotionMembers($userId, $allMembers);
+
+        // 缓存结果
+        Redis::setex($cacheKey, 86400, json_encode($allMembers)); // 缓存1天
+    }
+
+    // 计算总数
+    $total = count($allMembers);
+
+    // 分页
+    $offset = ($page - 1) * $pageSize;
+    $pagedMembers = array_slice($allMembers, $offset, $pageSize);
+
+    // 获取成员详细信息
+    $members = [];
+    foreach ($pagedMembers as $member) {
+        $userInfo = $this->userRepository->find($member['user_id']);
+        if ($userInfo) {
+            $members[] = [
+                'user_id' => $userInfo->id,
+                'username' => $userInfo->username,
+                'avatar' => $userInfo->avatar,
+                'level' => $member['level'],
+                'created_at' => $member['created_at']
+            ];
+        }
+    }
+
+    // 返回结果
+    return [
+        'total' => $total,
+        'page' => $page,
+        'page_size' => $pageSize,
+        'total_pages' => ceil($total / $pageSize),
+        'members' => $members
+    ];
+}
+
+/**
+ * 递归计算用户的所有团队成员
+ */
+private function calculateAllPromotionMembers(int $userId, array &$allMembers, int $level = 0, int $maxLevel = 20): void
+{
+    // 限制最大层级
+    if ($level >= $maxLevel) {
+        return;
+    }
+
+    // 获取直接下级
+    $directMembers = $this->referralRepository->getDirectMembers($userId);
+
+    foreach ($directMembers as $member) {
+        $memberId = $member->user_id;
+
+        // 添加到结果集
+        $allMembers[] = [
+            'user_id' => $memberId,
+            'level' => $level == 0 ? 1 : 2, // 1=直推, 2=间推
+            'created_at' => $member->created_at
+        ];
+
+        // 递归获取下级的下级
+        $this->calculateAllPromotionMembers($memberId, $allMembers, $level + 1, $maxLevel);
+    }
+}
+
+/**
+ * 清除推荐关系相关的缓存
+ */
+private function clearReferralCaches(int $userId, ?int $oldReferrerId, int $newReferrerId): void
+{
+    // 清除用户自身的缓存
+    Redis::del("promotion:user:{$userId}:all_referrers");
+
+    // 清除旧推荐人相关的缓存
+    if ($oldReferrerId) {
+        Redis::del("promotion:user:{$oldReferrerId}:all_members");
+
+        // 获取旧推荐人的所有上级,清除他们的团队成员缓存
+        $oldUpperReferrers = $this->getAllReferrers($oldReferrerId);
+        foreach ($oldUpperReferrers as $referrer) {
+            Redis::del("promotion:user:{$referrer['user_id']}:all_members");
+        }
+    }
+
+    // 清除新推荐人相关的缓存
+    Redis::del("promotion:user:{$newReferrerId}:all_members");
+
+    // 获取新推荐人的所有上级,清除他们的团队成员缓存
+    $newUpperReferrers = $this->getAllReferrers($newReferrerId);
+    foreach ($newUpperReferrers as $referrer) {
+        Redis::del("promotion:user:{$referrer['user_id']}:all_members");
+    }
+
+    // 获取用户的所有下级,清除他们的上级缓存
+    $allMembers = $this->getAllPromotionMembers($userId);
+    foreach ($allMembers['members'] as $member) {
+        Redis::del("promotion:user:{$member['user_id']}:all_referrers");
+    }
+}
+```
+
+### 4.4 推荐码验证与使用记录
+
+用户注册时,系统验证推荐码并获取推荐人ID。验证过程包括:
+
+1. 查询推荐码是否存在
+2. 验证推荐码的有效性(状态是否为有效)
+3. 检查推荐码是否已过期
+4. 更新推荐码的使用次数
+5. 记录邀请码使用情况(无论成功与否)
+6. 返回推荐码对应的推荐人ID
+
+验证成功后,系统将使用返回的推荐人ID建立推荐关系。
+
+系统会详细记录每次邀请码的使用情况,包括:
+
+1. **使用的邀请码**:记录用户输入的邀请码
+2. **邀请码所有者**:记录邀请码属于哪个用户
+3. **使用者信息**:记录使用邀请码的用户ID、IP地址和用户代理
+4. **使用结果**:记录使用是否成功,以及失败原因
+5. **使用时间**:记录邀请码使用的时间
+
+邀请码使用记录可用于:
+
+1. **邀请统计**:统计用户成功邀请的人数
+2. **防止刷邀请**:检测同一IP地址短时间内多次使用邀请码的情况
+3. **邀请追踪**:追踪邀请链路,分析用户邀请行为
+4. **问题排查**:当用户反馈邀请码问题时,可查询使用记录进行排查
+
+### 4.5 邀请奖励发放
+
+当用户成功邀请他人注册或被邀请用户达成特定条件(如升级、完成任务等)时,系统会发放邀请奖励。奖励发放流程包括:
+
+1. **验证用户关系**:确认邀请人和被邀请人的推荐关系存在
+2. **获取奖励配置**:根据奖励来源(注册、升级、任务等)获取对应的奖励配置
+3. **检查重复发放**:确保同一来源的奖励不会重复发放(注册奖励除外)
+4. **创建奖励记录**:在邀请奖励记录表中创建奖励记录
+5. **发放实际奖励**:根据奖励类型(物品、货币、经验等)调用相应服务发放实际奖励
+6. **更新奖励状态**:将奖励记录状态更新为已发放
+7. **触发奖励事件**:触发邀请奖励发放事件,供其他模块响应
+
+系统支持多种奖励类型:
+- **物品奖励**:向用户背包添加指定物品
+- **货币奖励**:向用户账户添加指定类型的货币
+- **经验奖励**:为用户增加经验值
+
+### 4.6 邀请奖励查询
+
+系统提供多种方式查询邀请奖励记录,支持按用户、被邀请用户、奖励类型、来源和状态进行筛选:
+
+1. **获取用户的邀请奖励记录**:查询用户获得的所有邀请奖励,支持分页和多种筛选条件
+2. **获取被邀请用户产生的奖励记录**:查询特定被邀请用户为他人产生的所有奖励记录
+3. **统计用户邀请奖励总量**:统计用户获得的各类型奖励总量和邀请总人数
+
+查询结果包含详细的奖励信息,如奖励类型、数量、来源、状态等,以及相关用户的基本信息(用户名、头像等)。
+
+### 4.7 邀请码使用记录查询
+
+系统提供多种方式查询邀请码的使用记录,支持按邀请码、邀请码所有者、使用者和使用结果等条件进行筛选:
+
+1. **查询特定邀请码的使用记录**:查询某个邀请码的所有使用记录,包括成功和失败的记录
+2. **查询用户使用的邀请码记录**:查询特定用户使用过的所有邀请码记录
+3. **查询邀请码所有者的邀请记录**:查询特定用户的邀请码被他人使用的记录
+4. **查询特定状态的邀请码使用记录**:查询成功、失败或被撤销的邀请码使用记录
+5. **统计邀请码所有者的邀请人数**:统计用户成功邀请的总人数
+6. **查询特定时间段的邀请记录**:查询某个时间段内的邀请码使用情况
+7. **查询特定IP地址的邀请记录**:查询来自特定IP地址的邀请码使用记录,用于防止刷邀请
+
+查询结果包含详细的邀请码使用信息,如邀请码、使用者、使用时间、使用结果等,以及相关用户的基本信息。
+
+## 5. 推荐关系存储策略
+
+### 5.1 直接关系存储 + 缓存策略
+
+团队模块采用只存储直接推荐关系,通过缓存或实时计算处理间接关系的策略。这种设计有以下优点:
+
+1. **数据结构简化**:只需存储直接推荐关系,大幅减少数据表的记录数量
+2. **存储空间优化**:减少了数据库存储空间的使用
+3. **关系修改灵活**:修改推荐关系更加简单,只需更新一条记录
+4. **缓存加速查询**:通过缓存加速查询,保持查询性能
+5. **按需计算**:间接关系按需计算,减少不必要的计算
+
+### 5.2 存储空间与查询效率的权衡
+
+直接关系存储 + 缓存策略在存储空间和查询效率之间取得了良好的平衡:
+
+| 存储方式 | 存储空间 | 查询效率 | 维护成本 | 修改灵活性 |
+|---------|---------|---------|---------|----------|
+| 仅存储直推关系(无缓存) | 低 | 低(需递归查询) | 低 | 高 |
+| 仅存储直推关系(有缓存) | 低 + 缓存空间 | 高(缓存命中)/ 中(缓存未命中) | 中 | 高 |
+| 扁平化存储 | 高 | 高(一次查询) | 中 | 低 |
+| 直推关系 + 缓存表 | 中 | 高(一次查询) | 中 | 高 |
+
+对于团队模块,直接关系存储 + 缓存策略是最佳选择,因为:
+- 在保持较高查询效率的同时,大幅减少了存储空间
+- 提高了推荐关系修改的灵活性,支持用户更换上级
+- 缓存机制可以有效减轻递归查询的性能问题
+- 缓存策略可以根据业务需求灵活调整
+
+### 5.3 缓存策略
+
+为了提高查询效率,系统采用多层缓存策略:
+
+#### 5.3.1 内存缓存(Redis)
+
+1. **缓存内容**:
+   - 用户的所有上级(包括直接和间接)
+   - 用户的所有下级(包括直接和间接)
+
+2. **缓存键设计**:
+   ```
+   // 用户的所有上级
+   key: promotion:user:{userId}:all_referrers
+   value: [referrerId1, referrerId2, ...]
+
+   // 用户的所有下级
+   key: promotion:user:{userId}:all_members
+   value: [memberId1, memberId2, ...]
+   ```
+
+3. **缓存更新策略**:
+   - **懒加载更新**:当查询用户的上级或下级时,如果缓存不存在,则计算并缓存
+   - **主动更新**:当推荐关系发生变化时,主动更新相关用户的缓存
+   - **定期刷新**:定期任务刷新活跃用户的缓存,确保数据一致性
+
+4. **缓存过期时间**:
+   - 设置合理的缓存过期时间,如1天
+   - 对于活跃用户和大型团队的领导者,可以设置更长的缓存时间
+
+#### 5.3.2 数据库缓存表
+
+为了进一步提高查询效率,特别是在Redis缓存失效或需要复杂查询时,系统引入了用户关系缓存表:
+
+##### 5.3.2.1 用户关系缓存表 (promotion_user_relation_cache)
+
+用户关系缓存表是一个统一的缓存表,用于存储用户之间的所有推荐关系(包括上下级关系)。通过合理设计字段和索引,一张表可以同时满足查询用户上级和下级的需求,减少数据冗余,简化维护。
+
+**表结构设计**:
+
+```sql
+CREATE TABLE `promotion_user_relation_cache` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `related_user_id` bigint(20) NOT NULL COMMENT '关联用户ID(上级)',
+  `level` tinyint(3) unsigned NOT NULL COMMENT '关系层级:1直接,2间接',
+  `path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关系路径,格式:1,2,3',
+  `depth` tinyint(3) unsigned NOT NULL COMMENT '层级深度,从1开始',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_user_relation` (`user_id`,`related_user_id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_related_user_id` (`related_user_id`),
+  KEY `idx_level` (`level`),
+  KEY `idx_depth` (`depth`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户关系缓存表';
+```
+
+**字段说明**:
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | bigint | 主键ID |
+| user_id | bigint | 用户ID(下级) |
+| related_user_id | bigint | 关联用户ID(上级) |
+| level | tinyint | 关系层级:1直接,2间接 |
+| path | varchar | 关系路径,格式:1,2,3 |
+| depth | tinyint | 层级深度,从1开始 |
+| created_at | timestamp | 创建时间 |
+| updated_at | timestamp | 更新时间 |
+
+**主要功能**:
+- 快速查询用户的所有上级(直推和间推)
+- 快速查询用户的所有下级(直推和间推)
+- 支持按层级、深度等条件进行复杂查询
+- 为收益分成系统和团队统计提供高效的数据支持
+
+**示例数据**:
+```
+id | user_id | related_user_id | level | path      | depth
+---|---------|----------------|-------|-----------|------
+1  | 4       | 3              | 1     | 3         | 1
+2  | 4       | 2              | 2     | 3,2       | 2
+3  | 4       | 1              | 2     | 3,2,1     | 3
+4  | 3       | 2              | 1     | 2         | 1
+5  | 3       | 1              | 2     | 2,1       | 2
+6  | 2       | 1              | 1     | 1         | 1
+```
+
+在上面的示例中:
+- 用户4的直接上级是用户3(level=1)
+- 用户4的间接上级是用户2和用户1(level=2)
+- 用户3的直接上级是用户2(level=1)
+- 用户3的间接上级是用户1(level=2)
+- 用户2的直接上级是用户1(level=1)
+
+**查询示例**:
+
+1. 查询用户4的所有上级:
+```sql
+SELECT * FROM promotion_user_relation_cache WHERE user_id = 4;
+```
+
+2. 查询用户1的所有下级:
+```sql
+SELECT * FROM promotion_user_relation_cache WHERE related_user_id = 1;
+```
+
+3. 查询用户2的直接下级:
+```sql
+SELECT u.* FROM users u
+JOIN promotion_user_relation_cache r ON u.id = r.user_id
+WHERE r.related_user_id = 2 AND r.level = 1;
+```
+
+4. 查询用户1的第2代下级:
+```sql
+SELECT u.* FROM users u
+JOIN promotion_user_relation_cache r ON u.id = r.user_id
+WHERE r.related_user_id = 1 AND r.depth = 2;
+```
+
+##### 5.3.2.2 缓存表优势与应用场景
+
+**缓存表优势**:
+- **数据统一性**:单一表存储所有关系,减少数据冗余,简化维护
+- **持久化存储**:不受内存限制,数据持久保存
+- **复杂查询支持**:支持SQL的各种复杂查询条件和聚合函数
+- **关联查询能力**:可与其他表进行关联查询,扩展数据分析能力
+- **减轻内存压力**:减少对Redis等内存缓存的依赖,降低缓存失效的影响
+- **高效统计分析**:支持团队规模、层级分布等复杂统计分析
+- **存储空间优化**:相比维护两张独立的缓存表,减少了约50%的存储空间
+
+**应用场景**:
+- **收益分成计算**:快速查找用户的所有上级,计算分成
+- **团队规模统计**:快速统计用户的团队规模,评定达人等级
+- **层级分布分析**:分析团队的层级分布,了解团队结构
+- **特定代数查询**:查询用户的特定代数下级,如20代以内的团队成员
+- **团队关系可视化**:为团队树形图和关系图提供数据支持
+- **双向关系查询**:同时支持上级和下级的高效查询
+
+##### 5.3.2.3 缓存表数据维护机制
+
+**初始化机制**:
+- 用户注册并建立推荐关系时,初始化相关缓存数据
+- 系统启动或升级时,可执行全量初始化,确保数据完整性
+
+```php
+/**
+ * 初始化用户的关系缓存
+ */
+public function initializeUserRelationCache(int $userId): int
+{
+    // 删除用户现有缓存
+    $this->relationCacheRepository->deleteByConditions([
+        'user_id' => $userId
+    ]);
+
+    // 获取直接上级
+    $directReferrer = $this->referralRepository->getDirectReferrer($userId);
+    if (!$directReferrer) {
+        return 0;
+    }
+
+    $count = 0;
+    $referrerId = $directReferrer->referrer_id;
+
+    // 添加直接上级缓存
+    $this->relationCacheRepository->create([
+        'user_id' => $userId,
+        'related_user_id' => $referrerId,
+        'level' => 1,
+        'path' => (string)$referrerId,
+        'depth' => 1
+    ]);
+    $count++;
+
+    // 获取上级的所有上级
+    $upperReferrers = $this->relationCacheRepository->findByConditions([
+        'user_id' => $referrerId
+    ]);
+
+    // 添加间接上级缓存
+    foreach ($upperReferrers as $upperReferrer) {
+        $this->relationCacheRepository->create([
+            'user_id' => $userId,
+            'related_user_id' => $upperReferrer->related_user_id,
+            'level' => 2,
+            'path' => $referrerId . ',' . $upperReferrer->path,
+            'depth' => $upperReferrer->depth + 1
+        ]);
+        $count++;
+    }
+
+    return $count;
+}
+```
+
+**更新机制**:
+- 用户变更上级时,同步更新关系缓存
+- 推荐关系变化时,级联更新所有相关用户的缓存
+
+```php
+/**
+ * 更新用户关系缓存
+ */
+public function updateUserRelationCache(int $userId, int $oldReferrerId, int $newReferrerId): void
+{
+    DB::transaction(function () use ($userId, $oldReferrerId, $newReferrerId) {
+        // 1. 删除用户及其所有下级与旧上级及其所有上级的关系缓存
+        if ($oldReferrerId) {
+            // 获取用户的所有下级ID
+            $downlineIds = $this->getDownlineIds($userId);
+            $downlineIds[] = $userId; // 包括用户自身
+
+            // 获取旧上级的所有上级ID
+            $upperReferrerIds = $this->getUpperReferrerIds($oldReferrerId);
+            $upperReferrerIds[] = $oldReferrerId; // 包括直接上级
+
+            // 删除关系缓存
+            foreach ($downlineIds as $downlineId) {
+                foreach ($upperReferrerIds as $upperReferrerId) {
+                    $this->relationCacheRepository->deleteByConditions([
+                        'user_id' => $downlineId,
+                        'related_user_id' => $upperReferrerId
+                    ]);
+                }
+            }
+        }
+
+        // 2. 为用户及其所有下级创建与新上级及其所有上级的关系缓存
+        if ($newReferrerId) {
+            // 初始化用户的关系缓存
+            $this->initializeUserRelationCache($userId);
+
+            // 获取用户的所有下级
+            $downlines = $this->getDownlines($userId);
+
+            // 更新所有下级的关系缓存
+            foreach ($downlines as $downline) {
+                $this->initializeUserRelationCache($downline['user_id']);
+            }
+        }
+    });
+}
+```
+
+**校验与修复**:
+- 定期执行缓存数据校验,检查与实际推荐关系的一致性
+- 发现不一致时自动修复,确保数据准确性
+
+```php
+/**
+ * 验证用户关系缓存
+ */
+private function validateUserRelationCache(int $userId): bool
+{
+    // 获取直接上级
+    $directReferrer = $this->referralRepository->getDirectReferrer($userId);
+
+    if (!$directReferrer) {
+        // 用户没有上级,缓存应该为空
+        $cacheCount = $this->relationCacheRepository->countByConditions([
+            'user_id' => $userId
+        ]);
+        return $cacheCount === 0;
+    }
+
+    // 获取缓存的直接上级
+    $cachedDirectReferrer = $this->relationCacheRepository->findByConditions([
+        'user_id' => $userId,
+        'level' => 1
+    ])->first();
+
+    if (!$cachedDirectReferrer || $cachedDirectReferrer->related_user_id != $directReferrer->referrer_id) {
+        return false;
+    }
+
+    // 验证间接上级
+    // 递归计算实际的间接上级
+    $actualIndirectReferrers = $this->calculateIndirectReferrers($directReferrer->referrer_id);
+
+    // 获取缓存的间接上级
+    $cachedIndirectReferrers = $this->relationCacheRepository->findByConditions([
+        'user_id' => $userId,
+        'level' => 2
+    ]);
+
+    // 比较间接上级数量
+    if (count($actualIndirectReferrers) != count($cachedIndirectReferrers)) {
+        return false;
+    }
+
+    // 比较间接上级ID
+    $actualReferrerIds = collect($actualIndirectReferrers)->pluck('referrer_id')->toArray();
+    $cachedReferrerIds = $cachedIndirectReferrers->pluck('related_user_id')->toArray();
+
+    sort($actualReferrerIds);
+    sort($cachedReferrerIds);
+
+    return $actualReferrerIds == $cachedReferrerIds;
+}
+```
+
+**性能优化**:
+- 批量处理大规模数据更新
+- 异步处理非关键路径的缓存更新
+- 分片处理大型团队的缓存维护
+- 定期优化表和索引,保持查询性能
+
+##### 5.3.2.4 缓存表查询示例
+
+**查询用户的所有上级**:
+```php
+/**
+ * 获取用户的所有上级
+ */
+public function getAllReferrers(int $userId, int $level = 0, int $maxDepth = 20): array
+{
+    $conditions = ['user_id' => $userId];
+
+    if ($level > 0) {
+        $conditions['level'] = $level;
+    }
+
+    if ($maxDepth > 0) {
+        $conditions['depth'] = ['<=', $maxDepth];
+    }
+
+    $relations = $this->relationCacheRepository->findByConditions($conditions);
+
+    $referrers = [];
+    foreach ($relations as $relation) {
+        $referrers[] = [
+            'user_id' => $relation->related_user_id,
+            'level' => $relation->level,
+            'depth' => $relation->depth,
+            'path' => $relation->path
+        ];
+    }
+
+    return $referrers;
+}
+```
+
+**查询用户的所有团队成员**:
+```php
+/**
+ * 获取用户的所有团队成员
+ */
+public function getAllPromotionMembers(int $userId, int $level = 0, int $maxDepth = 20, int $page = 1, int $pageSize = 20): array
+{
+    $conditions = ['related_user_id' => $userId];
+
+    if ($level > 0) {
+        $conditions['level'] = $level;
+    }
+
+    if ($maxDepth > 0) {
+        $conditions['depth'] = ['<=', $maxDepth];
+    }
+
+    // 获取总数
+    $total = $this->relationCacheRepository->countByConditions($conditions);
+
+    // 分页查询
+    $offset = ($page - 1) * $pageSize;
+    $relations = $this->relationCacheRepository->findByConditionsPaginated($conditions, $offset, $pageSize);
+
+    // 获取成员详细信息
+    $members = [];
+    foreach ($relations as $relation) {
+        $userInfo = $this->userRepository->find($relation->user_id);
+        if ($userInfo) {
+            $members[] = [
+                'user_id' => $userInfo->id,
+                'username' => $userInfo->username,
+                'avatar' => $userInfo->avatar,
+                'level' => $relation->level,
+                'depth' => $relation->depth,
+                'path' => $relation->path,
+                'created_at' => $userInfo->created_at
+            ];
+        }
+    }
+
+    return [
+        'total' => $total,
+        'page' => $page,
+        'page_size' => $pageSize,
+        'total_pages' => ceil($total / $pageSize),
+        'members' => $members
+    ];
+}
+```
+
+**查询特定代数的团队成员**:
+```php
+/**
+ * 获取用户特定代数的团队成员
+ */
+public function getPromotionMembersByDepth(int $userId, int $depth): array
+{
+    $relations = $this->relationCacheRepository->findByConditions([
+        'related_user_id' => $userId,
+        'depth' => $depth
+    ]);
+
+    $members = [];
+    foreach ($relations as $relation) {
+        $userInfo = $this->userRepository->find($relation->user_id);
+        if ($userInfo) {
+            $members[] = [
+                'user_id' => $userInfo->id,
+                'username' => $userInfo->username,
+                'level' => $relation->level,
+                'depth' => $relation->depth,
+                'path' => $relation->path
+            ];
+        }
+    }
+
+    return $members;
+}
+```
+
+**统计团队规模**:
+```php
+/**
+ * 统计用户的团队规模
+ */
+public function countPromotionMembers(int $userId, int $level = 0): int
+{
+    $conditions = ['related_user_id' => $userId];
+
+    if ($level > 0) {
+        $conditions['level'] = $level;
+    }
+
+    return $this->relationCacheRepository->countByConditions($conditions);
+}
+```
+
+### 5.4 关系建立流程
+
+新用户注册并使用推荐码时,系统按以下流程建立推荐关系:
+
+1. 验证推荐码,获取直推推荐人ID
+2. 创建用户与直推推荐人的直推关系
+3. 清除相关缓存,包括用户自身、推荐人及其上级的缓存
+4. 更新推荐人及其上级的团队统计数据
+5. 触发推荐关系创建事件
+
+这种方式简化了关系建立流程,同时通过缓存机制保持了查询效率。
+
+## 6. 推荐码系统
+
+### 6.1 推荐码生成算法
+
+推荐码生成采用随机字符串算法,确保唯一性和安全性:
+
+1. **字符集**:使用数字和大写字母(0-9, A-Z)组成的36个字符
+2. **长度**:默认6位,可根据需求调整
+3. **唯一性检查**:生成后检查数据库中是否已存在,如存在则重新生成
+4. **安全性**:使用密码学安全的随机数生成器,避免可预测性
+
+### 6.2 推荐码使用流程
+
+推荐码的使用流程如下:
+
+1. 用户注册时输入推荐码
+2. 系统验证推荐码的有效性
+3. 获取推荐码对应的推荐人ID
+4. 建立用户与推荐人的推荐关系
+5. 更新推荐码的使用次数
+
+### 6.3 推荐链接和二维码
+
+除了纯文本推荐码外,系统还支持推荐链接和二维码:
+
+1. **推荐链接**:格式为`https://example.com/register?code=ABCDEF`
+2. **推荐二维码**:包含推荐链接的二维码图片
+
+用户可以分享推荐链接或二维码,新用户通过链接注册时自动填入推荐码。
+
+## 7. 推荐关系修改
+
+### 7.1 推荐关系修改功能
+
+在只存储直接推荐关系的设计下,系统可以支持修改用户的推荐关系(上级):
+
+```php
+/**
+ * 更新用户的推荐关系
+ */
+public function updateReferralRelation(int $userId, int $newReferrerId): bool
+{
+    return DB::transaction(function () use ($userId, $newReferrerId) {
+        // 验证用户和新推荐人是否存在
+        if (!$this->userRepository->exists($userId) || !$this->userRepository->exists($newReferrerId)) {
+            throw new UserNotFoundException("用户或新推荐人不存在");
+        }
+
+        // 验证是否形成循环推荐
+        if ($this->checkCircularReferral($userId, $newReferrerId)) {
+            throw new ReferralException("不能形成循环推荐关系");
+        }
+
+        // 获取旧的直接推荐人
+        $oldReferrer = $this->referralRepository->getDirectReferrer($userId);
+        $oldReferrerId = $oldReferrer ? $oldReferrer->referrer_id : null;
+
+        // 如果新旧推荐人相同,无需更新
+        if ($oldReferrerId == $newReferrerId) {
+            return true;
+        }
+
+        // 更新直接推荐关系
+        if ($oldReferrerId) {
+            $this->referralRepository->deleteDirectReferral($userId);
+        }
+
+        $this->referralRepository->createDirectReferral($userId, $newReferrerId);
+
+        // 清除相关缓存
+        $this->clearReferralCaches($userId, $oldReferrerId, $newReferrerId);
+
+        // 更新旧推荐人的直推人数和团队人数
+        if ($oldReferrerId) {
+            $this->updatePromotionCounts($oldReferrerId);
+            $this->talentService->checkAndUpdateTalentLevel($oldReferrerId);
+        }
+
+        // 更新新推荐人的直推人数和团队人数
+        $this->updatePromotionCounts($newReferrerId);
+        $this->talentService->checkAndUpdateTalentLevel($newReferrerId);
+
+        // 记录修改历史
+        $this->referralChangeRepository->create([
+            'user_id' => $userId,
+            'old_referrer_id' => $oldReferrerId,
+            'new_referrer_id' => $newReferrerId,
+            'change_time' => now(),
+            'change_reason' => request('reason', ''),
+            'changed_by' => Auth::id() ?? 0
+        ]);
+
+        // 触发推荐关系更新事件
+        event(new ReferralUpdatedEvent($userId, $oldReferrerId, $newReferrerId));
+
+        return true;
+    });
+}
+```
+
+### 7.2 推荐关系修改记录表
+
+为了记录推荐关系的修改历史,系统使用以下表结构:
+
+```sql
+CREATE TABLE `promotion_referral_changes` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `old_referrer_id` bigint(20) DEFAULT NULL COMMENT '旧推荐人ID',
+  `new_referrer_id` bigint(20) NOT NULL COMMENT '新推荐人ID',
+  `change_time` timestamp NOT NULL COMMENT '修改时间',
+  `change_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改原因',
+  `changed_by` bigint(20) NOT NULL COMMENT '操作人ID',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_change_time` (`change_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐关系修改记录表';
+```
+
+### 7.3 修改限制策略
+
+为了防止频繁修改推荐关系导致系统不稳定,系统实施以下限制策略:
+
+```php
+/**
+ * 验证用户是否可以修改推荐关系
+ */
+private function validateReferralChangeEligibility(int $userId): void
+{
+    // 检查修改次数限制
+    $changeCount = $this->referralChangeRepository->countRecentChanges($userId, 180); // 180天内
+    if ($changeCount >= 1) {
+        throw new ReferralException("180天内只能修改一次推荐关系");
+    }
+
+    // 检查注册时间限制
+    $user = $this->userRepository->find($userId);
+    if ($user->created_at->diffInDays(now()) < 30) {
+        throw new ReferralException("注册30天内不能修改推荐关系");
+    }
+
+    // 检查团队规模限制
+    $promotionCount = $this->calculatePromotionCount($userId);
+    if ($promotionCount > 100) {
+        throw new ReferralException("团队规模超过100人不能修改推荐关系");
+    }
+}
+```
+
+## 8. 防循环推荐机制
+
+### 8.1 循环推荐问题
+
+循环推荐是指用户A推荐用户B,用户B又推荐用户A的情况,或者形成更长的循环链。这种情况会导致:
+
+1. 无限循环的推荐关系
+2. 收益分成的循环计算
+3. 团队统计数据的错误
+
+### 8.2 检测和防止机制
+
+系统通过以下机制检测和防止循环推荐:
+
+```php
+/**
+ * 检查是否形成循环推荐
+ */
+private function checkCircularReferral(int $userId, int $referrerId): bool
+{
+    // 如果用户ID和推荐人ID相同,直接形成循环
+    if ($userId == $referrerId) {
+        return true;
+    }
+
+    // 获取用户的所有下级
+    $allMembers = $this->getAllPromotionMembers($userId);
+
+    // 检查新推荐人是否在用户的下级中
+    foreach ($allMembers['members'] as $member) {
+        if ($member['user_id'] == $referrerId) {
+            return true;
+        }
+    }
+
+    return false;
+}
+```
+
+此外,系统还实施以下规则:
+
+1. **修改限制**:限制用户修改推荐关系的频率和条件
+2. **层级限制**:间推关系最多支持20层,防止过深的推荐链
+3. **状态验证**:只有状态正常的用户才能成为推荐人
+
+## 9. 团队统计
+
+### 9.1 直推人数统计
+
+系统实时统计用户的直推人数:
+
+```php
+/**
+ * 统计用户的直推人数
+ */
+public function countDirectReferrals(int $userId): int
+{
+    return $this->referralRepository->countByConditions([
+        'referrer_id' => $userId,
+        'level' => 1
+    ]);
+}
+```
+
+### 9.2 团队总人数统计
+
+系统实时统计用户的团队总人数(包括直推和间推):
+
+```php
+/**
+ * 统计用户的团队总人数
+ */
+public function countPromotionMembers(int $userId): int
+{
+    return $this->referralRepository->countByConditions([
+        'referrer_id' => $userId
+    ]);
+}
+```
+
+### 9.3 团队活跃度统计
+
+系统支持统计团队的活跃度,用于评估团队质量:
+
+```php
+/**
+ * 统计用户团队的活跃度
+ */
+public function calculatePromotionActivity(int $userId, int $days = 7): array
+{
+    // 获取团队成员ID列表
+    $memberIds = $this->referralRepository->getPromotionMemberIds($userId);
+
+    // 统计活跃用户数
+    $activeCount = $this->userActivityRepository->countActiveUsers($memberIds, $days);
+
+    // 计算活跃率
+    $totalCount = count($memberIds);
+    $activeRate = $totalCount > 0 ? $activeCount / $totalCount : 0;
+
+    return [
+        'total_members' => $totalCount,
+        'active_members' => $activeCount,
+        'active_rate' => $activeRate,
+        'days' => $days
+    ];
+}
+```
+
+## 10. 推荐关系可视化
+
+### 10.1 团队树形图
+
+系统支持生成团队的树形结构图,直观展示推荐关系:
+
+```php
+/**
+ * 生成用户的团队树形图
+ */
+public function generatePromotionTree(int $userId, int $maxDepth = 3): array
+{
+    // 获取用户信息
+    $user = $this->userRepository->find($userId);
+    if (!$user) {
+        return [];
+    }
+
+    // 构建根节点
+    $root = [
+        'id' => $user->id,
+        'name' => $user->username,
+        'avatar' => $user->avatar,
+        'children' => []
+    ];
+
+    // 递归构建团队树
+    $this->buildPromotionTreeNode($root, 1, $maxDepth);
+
+    return $root;
+}
+
+/**
+ * 递归构建团队树节点
+ */
+private function buildPromotionTreeNode(array &$node, int $currentDepth, int $maxDepth): void
+{
+    // 达到最大深度,停止递归
+    if ($currentDepth >= $maxDepth) {
+        return;
+    }
+
+    // 获取直推成员
+    $directMembers = $this->referralRepository->findByConditions([
+        'referrer_id' => $node['id'],
+        'level' => 1
+    ]);
+
+    // 构建子节点
+    foreach ($directMembers as $member) {
+        $user = $this->userRepository->find($member->user_id);
+        if ($user) {
+            $childNode = [
+                'id' => $user->id,
+                'name' => $user->username,
+                'avatar' => $user->avatar,
+                'children' => []
+            ];
+
+            // 递归构建子节点
+            $this->buildPromotionTreeNode($childNode, $currentDepth + 1, $maxDepth);
+
+            // 添加到父节点
+            $node['children'][] = $childNode;
+        }
+    }
+}
+```
+
+### 10.2 团队关系图
+
+系统支持生成团队的关系图,展示成员之间的连接:
+
+```php
+/**
+ * 生成用户的团队关系图
+ */
+public function generatePromotionGraph(int $userId, int $maxMembers = 50): array
+{
+    // 获取团队成员
+    $members = $this->getPromotionMembers($userId, 0, 1, $maxMembers)['members'];
+
+    // 构建节点
+    $nodes = [];
+    $nodes[] = [
+        'id' => $userId,
+        'name' => $this->userRepository->find($userId)->username,
+        'type' => 'root'
+    ];
+
+    foreach ($members as $member) {
+        $nodes[] = [
+            'id' => $member['user_id'],
+            'name' => $member['username'],
+            'type' => $member['level'] == 1 ? 'direct' : 'indirect'
+        ];
+    }
+
+    // 构建边
+    $edges = [];
+    foreach ($members as $member) {
+        $edges[] = [
+            'source' => $member['level'] == 1 ? $userId : $this->getDirectReferrerId($member['user_id']),
+            'target' => $member['user_id'],
+            'type' => $member['level'] == 1 ? 'direct' : 'indirect'
+        ];
+    }
+
+    return [
+        'nodes' => $nodes,
+        'edges' => $edges
+    ];
+}
+```
+
+## 11. 与其他系统的集成
+
+### 11.1 与用户系统的集成
+
+推荐关系系统与用户系统紧密集成:
+
+1. **用户注册**:用户注册时处理推荐码,建立推荐关系
+2. **用户状态**:用户状态变更(如封禁、注销)会影响推荐关系
+3. **用户信息**:推荐关系展示需要用户的基本信息(昵称、头像等)
+
+### 11.2 与达人等级系统的集成
+
+推荐关系系统为达人等级系统提供基础数据:
+
+1. **团队规模**:直推人数和团队总人数用于计算达人等级
+2. **团队活跃度**:团队活跃度可作为达人等级的附加条件
+3. **等级变更**:推荐关系变化可能触发达人等级变更
+
+### 11.3 与收益分成系统的集成
+
+推荐关系系统为收益分成系统提供关系数据:
+
+1. **分成对象**:确定哪些上级可以获得分成
+2. **分成比例**:根据推荐关系类型(直推/间推)确定基础分成比例
+3. **分成计算**:提供推荐链路,用于计算多级分成
+
+## 12. 性能优化
+
+### 12.1 查询优化
+
+针对推荐关系的高频查询,系统采用以下优化措施:
+
+1. **索引优化**:为`user_id`、`referrer_id`和`level`字段建立合适的索引
+2. **复合索引**:为常用的查询条件组合建立复合索引
+3. **查询缓存**:缓存频繁查询的结果,如用户的直推人数和团队总人数
+4. **分页查询**:大数据量查询采用分页机制,避免一次加载过多数据
+
+### 12.2 缓存策略
+
+系统采用多级缓存策略提高性能:
+
+1. **本地缓存**:缓存单次请求中重复使用的数据
+2. **分布式缓存**:使用Redis缓存跨请求的数据
+3. **缓存更新**:推荐关系变更时主动更新缓存
+4. **缓存失效**:设置合理的缓存过期时间,平衡实时性和性能
+
+### 12.3 批量操作
+
+对于大规模数据操作,系统采用批量处理方式:
+
+1. **批量插入**:一次插入多条推荐关系记录
+2. **批量更新**:一次更新多个用户的团队统计数据
+3. **异步处理**:耗时操作放入队列异步处理
+4. **定时任务**:定期执行统计和维护任务,避免实时计算
+
+## 13. 安全性考虑
+
+### 13.1 防刷机制
+
+防止恶意用户刷取推荐关系:
+
+1. **IP限制**:限制同一IP短时间内的注册次数
+2. **设备限制**:限制同一设备短时间内的注册次数
+3. **验证码**:注册时使用验证码防止自动注册
+4. **人工审核**:对异常推荐关系进行人工审核
+
+### 13.2 数据验证
+
+严格验证输入数据,确保数据安全:
+
+1. **用户验证**:验证用户和推荐人的存在性和有效性
+2. **关系验证**:验证推荐关系的合法性,防止循环推荐
+3. **推荐码验证**:验证推荐码的有效性和状态
+4. **参数验证**:验证API参数的合法性和范围
+
+### 13.3 权限控制
+
+实施严格的权限控制,保护用户数据:
+
+1. **用户权限**:用户只能查看自己的团队数据
+2. **管理权限**:只有管理员可以查看和修改所有推荐关系
+3. **操作日志**:记录关键操作的日志,便于审计和追踪
+4. **数据脱敏**:展示团队数据时对敏感信息进行脱敏
+
+## 14. 总结
+
+推荐关系系统是团队模块的核心组成部分,通过建立用户间的直推和间推关系,形成多层级的团队结构,为收益分成和达人等级提供基础数据支持。系统采用只存储直接推荐关系 + 缓存策略的设计,在保持较高查询效率的同时,大幅减少了存储空间,并提高了推荐关系修改的灵活性。
+
+系统实现了推荐码生成、推荐关系修改、防循环推荐、团队统计和可视化等功能,通过缓存机制优化了查询性能,同时保持了数据的一致性。通过与用户系统、达人等级系统和收益分成系统的紧密集成,推荐关系系统为团队模块的其他功能提供了坚实的基础。
+
+这种设计方案特别适合需要支持推荐关系修改的场景,用户可以在特定条件下更换上级,系统会自动处理相关的缓存更新和团队统计数据更新,确保数据的一致性和完整性。

+ 548 - 0
app/Module/Promotionurs/Docs/数据库设计.md

@@ -0,0 +1,548 @@
+# 团队模块数据库设计
+
+## 1. 数据库表概览
+
+团队模块包含以下核心数据表:
+
+1. **promotion_user_referrals** - 用户推荐关系表
+2. **promotion_user_talents** - 达人等级表
+3. **promotion_profits** - 团队收益记录表
+4. **promotion_referral_codes** - 推荐码表
+5. **promotion_talent_configs** - 达人等级配置表
+6. **promotion_profit_rules** - 收益分成规则表
+7. **promotion_referral_changes** - 推荐关系修改记录表
+8. **promotion_user_relation_cache** - 用户关系缓存表
+9. **promotion_invite_rewards** - 邀请奖励记录表
+10. **promotion_referral_code_usages** - 邀请码使用记录表
+
+## 2. 表结构详细设计
+
+### 2.1 用户推荐关系表 (promotion_user_referrals)
+
+存储用户与其直接推荐人(直接上级)的关系,间接关系通过缓存或实时计算获取。该表是实现"直间推播种收获贡献百分比农作物收益"功能的基础。
+
+```sql
+CREATE TABLE `promotion_user_referrals` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `referrer_id` bigint(20) NOT NULL COMMENT '直接推荐人ID',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_user_id` (`user_id`),
+  KEY `idx_referrer_id` (`referrer_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户直接推荐关系表';
+```
+
+> 注意:表中只存储直接推荐关系(直推),间接推荐关系(间推)通过递归查询或缓存获取。这种设计简化了数据结构,同时通过缓存机制保证了查询效率。
+
+### 2.2 达人等级表 (promotion_user_talents)
+
+存储用户的达人等级信息,包括直推人数和团队总人数。
+
+```sql
+CREATE TABLE `promotion_user_talents` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `talent_level` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '达人等级:0无,1初级,2中级,3高级,4资深,5顶级',
+  `direct_count` int(11) NOT NULL DEFAULT '0' COMMENT '直推人数',
+  `promotion_count` int(11) NOT NULL DEFAULT '0' COMMENT '团队总人数',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_user_id` (`user_id`),
+  KEY `idx_talent_level` (`talent_level`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='达人等级表';
+```
+
+### 2.3 团队收益记录表 (promotion_profits)
+
+记录团队成员产生的分成收益,是实现"直间推播种收获贡献百分比农作物收益"功能的核心表。
+
+```sql
+CREATE TABLE `promotion_profits` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '获得收益的用户ID',
+  `promotion_member_id` bigint(20) NOT NULL COMMENT '团队成员ID',
+  `source_id` bigint(20) NOT NULL COMMENT '收益来源ID',
+  `source_type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '收益来源类型',
+  `item_id` bigint(20) NOT NULL COMMENT '物品ID',
+  `profit_amount` int(11) NOT NULL COMMENT '分成收益数量',
+  `profit_rate` decimal(5,4) NOT NULL COMMENT '分成比例',
+  `relation_type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '关系类型:1直推,2间推',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_promotion_member_id` (`promotion_member_id`),
+  KEY `idx_source` (`source_type`,`source_id`),
+  KEY `idx_relation_type` (`relation_type`),
+  KEY `idx_created_at` (`created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='团队收益记录表';
+```
+
+> 注意:`relation_type`字段用于区分直推和间推收益,便于统计和分析。`source_type`为'farm_harvest'时表示农场收获收益,是团队收益的主要来源。
+
+### 2.4 推荐码表 (promotion_referral_codes)
+
+存储用户的推荐码信息。
+
+```sql
+CREATE TABLE `promotion_referral_codes` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '推荐码',
+  `usage_count` int(11) NOT NULL DEFAULT '0' COMMENT '使用次数',
+  `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态:1有效,0无效',
+  `expire_time` timestamp NULL DEFAULT NULL COMMENT '过期时间,NULL表示永不过期',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_code` (`code`),
+  UNIQUE KEY `idx_user_id` (`user_id`),
+  KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐码表';
+```
+
+### 2.5 达人等级配置表 (promotion_talent_configs)
+
+存储不同达人等级的配置信息。
+
+```sql
+CREATE TABLE `promotion_talent_configs` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `level` tinyint(3) unsigned NOT NULL COMMENT '等级',
+  `name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '等级名称',
+  `direct_count_required` int(11) NOT NULL COMMENT '所需直推人数',
+  `promotion_count_required` int(11) NOT NULL COMMENT '所需团队总人数',
+  `profit_rate` decimal(5,4) NOT NULL COMMENT '间推分成比例',
+  `benefits` json DEFAULT NULL COMMENT '等级权益',
+  `icon` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '等级图标',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_level` (`level`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='达人等级配置表';
+```
+
+### 2.6 收益分成规则表 (promotion_profit_rules)
+
+存储不同来源的收益分成规则。
+
+```sql
+CREATE TABLE `promotion_profit_rules` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `source_type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '来源类型',
+  `direct_profit_rate` decimal(5,4) NOT NULL COMMENT '直推分成比例',
+  `max_indirect_level` int(11) NOT NULL DEFAULT '20' COMMENT '最大间推层级',
+  `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态:1有效,0无效',
+  `rules` json DEFAULT NULL COMMENT '特殊规则',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_source_type` (`source_type`),
+  KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='收益分成规则表';
+```
+
+### 2.7 推荐关系修改记录表 (promotion_referral_changes)
+
+记录用户推荐关系的修改历史。
+
+```sql
+CREATE TABLE `promotion_referral_changes` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `old_referrer_id` bigint(20) DEFAULT NULL COMMENT '旧推荐人ID',
+  `new_referrer_id` bigint(20) NOT NULL COMMENT '新推荐人ID',
+  `change_time` timestamp NOT NULL COMMENT '修改时间',
+  `change_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '修改原因',
+  `changed_by` bigint(20) NOT NULL COMMENT '操作人ID',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_change_time` (`change_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐关系修改记录表';
+```
+
+### 2.8 用户关系缓存表 (promotion_user_relation_cache)
+
+用户关系缓存表是一个统一的缓存表,用于存储用户之间的所有推荐关系(包括上下级关系)。通过合理设计字段和索引,一张表可以同时满足查询用户上级和下级的需求,减少数据冗余,简化维护。
+
+```sql
+CREATE TABLE `promotion_user_relation_cache` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
+  `related_user_id` bigint(20) NOT NULL COMMENT '关联用户ID(上级)',
+  `level` tinyint(3) unsigned NOT NULL COMMENT '关系层级:1直接,2间接',
+  `path` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关系路径,格式:1,2,3',
+  `depth` tinyint(3) unsigned NOT NULL COMMENT '层级深度,从1开始',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_user_relation` (`user_id`,`related_user_id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_related_user_id` (`related_user_id`),
+  KEY `idx_level` (`level`),
+  KEY `idx_depth` (`depth`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户关系缓存表';
+```
+
+**字段说明**:
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | bigint | 主键ID |
+| user_id | bigint | 用户ID(下级) |
+| related_user_id | bigint | 关联用户ID(上级) |
+| level | tinyint | 关系层级:1直接,2间接 |
+| path | varchar | 关系路径,格式:1,2,3 |
+| depth | tinyint | 层级深度,从1开始 |
+| created_at | timestamp | 创建时间 |
+| updated_at | timestamp | 更新时间 |
+
+### 2.9 邀请奖励记录表 (promotion_invite_rewards)
+
+邀请奖励记录表用于记录用户邀请他人注册时获得的奖励,支持多种奖励类型和来源追踪。
+
+```sql
+CREATE TABLE `promotion_invite_rewards` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `user_id` bigint(20) NOT NULL COMMENT '获得奖励的用户ID',
+  `invited_user_id` bigint(20) NOT NULL COMMENT '被邀请的用户ID',
+  `reward_type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '奖励类型:item=物品,coin=货币,exp=经验',
+  `reward_id` bigint(20) NOT NULL COMMENT '奖励ID(物品ID或货币类型ID)',
+  `reward_amount` int(11) NOT NULL COMMENT '奖励数量',
+  `reward_source` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'register' COMMENT '奖励来源:register=注册,upgrade=升级,task=任务',
+  `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态:0=未发放,1=已发放,2=已过期',
+  `remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_invited_user_id` (`invited_user_id`),
+  KEY `idx_reward_type` (`reward_type`),
+  KEY `idx_reward_source` (`reward_source`),
+  KEY `idx_status` (`status`),
+  KEY `idx_created_at` (`created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邀请奖励记录表';
+```
+
+**字段说明**:
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | bigint | 主键ID |
+| user_id | bigint | 获得奖励的用户ID |
+| invited_user_id | bigint | 被邀请的用户ID |
+| reward_type | varchar | 奖励类型:item=物品,coin=货币,exp=经验 |
+| reward_id | bigint | 奖励ID(物品ID或货币类型ID) |
+| reward_amount | int | 奖励数量 |
+| reward_source | varchar | 奖励来源:register=注册,upgrade=升级,task=任务 |
+| status | tinyint | 状态:0=未发放,1=已发放,2=已过期 |
+| remark | varchar | 备注 |
+| created_at | timestamp | 创建时间 |
+| updated_at | timestamp | 更新时间 |
+
+**示例数据**:
+```
+id | user_id | invited_user_id | reward_type | reward_id | reward_amount | reward_source | status | created_at
+---|---------|----------------|------------|-----------|--------------|--------------|--------|------------
+1  | 1       | 2              | item        | 1001      | 5            | register     | 1      | 2023-05-01 10:00:00
+2  | 1       | 3              | coin        | 1         | 100          | register     | 1      | 2023-05-02 11:00:00
+3  | 2       | 4              | exp         | 0         | 50           | register     | 1      | 2023-05-03 12:00:00
+4  | 1       | 2              | item        | 1002      | 1            | upgrade      | 1      | 2023-05-10 10:00:00
+```
+
+在上面的示例中:
+- 用户1邀请用户2注册,获得了5个ID为1001的物品奖励
+- 用户1邀请用户3注册,获得了100个ID为1的货币奖励
+- 用户2邀请用户4注册,获得了50点经验值奖励
+- 当用户2升级时,用户1获得了1个ID为1002的物品奖励
+
+### 2.10 邀请码使用记录表 (promotion_referral_code_usages)
+
+邀请码使用记录表用于详细记录每次邀请码的使用情况,包括谁使用了哪个邀请码、何时使用、使用结果等信息。
+
+```sql
+CREATE TABLE `promotion_referral_code_usages` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `code` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '使用的邀请码',
+  `code_owner_id` bigint(20) NOT NULL COMMENT '邀请码所有者用户ID',
+  `user_id` bigint(20) NOT NULL COMMENT '使用邀请码的用户ID',
+  `ip_address` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '使用时的IP地址',
+  `user_agent` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '使用时的用户代理',
+  `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '状态:0=失败,1=成功,2=已撤销',
+  `result` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '使用结果描述',
+  `remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
+  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_code` (`code`),
+  KEY `idx_code_owner_id` (`code_owner_id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_status` (`status`),
+  KEY `idx_created_at` (`created_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='邀请码使用记录表';
+```
+
+**字段说明**:
+
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | bigint | 主键ID |
+| code | varchar | 使用的邀请码 |
+| code_owner_id | bigint | 邀请码所有者用户ID |
+| user_id | bigint | 使用邀请码的用户ID |
+| ip_address | varchar | 使用时的IP地址 |
+| user_agent | varchar | 使用时的用户代理 |
+| status | tinyint | 状态:0=失败,1=成功,2=已撤销 |
+| result | varchar | 使用结果描述 |
+| remark | varchar | 备注 |
+| created_at | timestamp | 创建时间 |
+| updated_at | timestamp | 更新时间 |
+
+**示例数据**:
+```
+id | code     | code_owner_id | user_id | ip_address    | status | result                | created_at
+---|----------|--------------|---------|---------------|--------|----------------------|------------
+1  | ABC123   | 1            | 2       | 192.168.1.1   | 1      | 注册成功               | 2023-05-01 10:00:00
+2  | DEF456   | 3            | 4       | 192.168.1.2   | 1      | 注册成功               | 2023-05-02 11:00:00
+3  | ABC123   | 1            | 5       | 192.168.1.3   | 0      | 邀请码已过期            | 2023-05-03 12:00:00
+4  | GHI789   | 6            | 7       | 192.168.1.4   | 2      | 管理员撤销             | 2023-05-04 13:00:00
+```
+
+在上面的示例中:
+- 用户2成功使用了用户1的邀请码ABC123注册
+- 用户4成功使用了用户3的邀请码DEF456注册
+- 用户5尝试使用用户1的邀请码ABC123,但失败了,原因是邀请码已过期
+- 用户7使用了用户6的邀请码GHI789,但后来被管理员撤销了
+
+1. 查询用户4的所有上级:
+```sql
+SELECT * FROM promotion_user_relation_cache WHERE user_id = 4;
+```
+
+2. 查询用户1的所有下级:
+```sql
+SELECT * FROM promotion_user_relation_cache WHERE related_user_id = 1;
+```
+
+3. 查询用户2的直接下级:
+```sql
+SELECT u.* FROM users u
+JOIN promotion_user_relation_cache r ON u.id = r.user_id
+WHERE r.related_user_id = 2 AND r.level = 1;
+```
+
+4. 查询用户1的第2代下级:
+```sql
+SELECT u.* FROM users u
+JOIN promotion_user_relation_cache r ON u.id = r.user_id
+WHERE r.related_user_id = 1 AND r.depth = 2;
+```
+
+> 注意:该表是缓存表,其数据可以通过`promotion_user_referrals`表重新计算生成。使用单一表存储所有关系,减少了数据冗余,简化了维护,同时通过合理的索引设计,保证了查询效率。
+
+## 3. 数据关系
+
+### 3.1 主要关系图
+
+```
+                                    ┌───────────────────┐
+                                    │promotion_talent_configs│
+                                    └─────────┬─────────┘
+                                              │
+                                              ▼
+┌───────────────────┐            ┌───────────────────┐
+│promotion_user_referrals│◄───────────┤ promotion_user_talents │
+└─────────┬─────────┘            └─────────┬─────────┘
+          │                                 │
+          │                                 │
+          ├─────────────┐                   │
+          │             │                   │
+          ▼             ▼                   │
+┌───────────────────┐  ┌───────────────────┐│
+│promotion_referral_codes│  │promotion_user_relation_cache│
+└───────────────────┘  └───────────────────┘│
+                                            │
+                                            │
+                                            ▼
+                                  ┌───────────────────┐
+                                  │   promotion_profits    │
+                                  └─────────┬─────────┘
+                                            │
+                                            │
+                                            ▼
+                                  ┌───────────────────┐
+                                  │ promotion_profit_rules │
+                                  └───────────────────┘
+```
+
+### 3.2 关系说明
+
+1. **用户与推荐关系**:一个用户只有一个直接推荐人,但可以有多个被推荐人(1:N)
+2. **用户与达人等级**:一个用户有一个达人等级记录(1:1)
+3. **用户与推荐码**:一个用户有一个推荐码(1:1)
+4. **达人等级与配置**:用户达人等级对应配置表中的等级(N:1)
+5. **用户与收益记录**:一个用户可以有多条收益记录(1:N)
+6. **收益记录与规则**:收益记录根据来源类型对应不同的分成规则(N:1)
+7. **用户与推荐关系修改**:一个用户可以有多条推荐关系修改记录(1:N)
+8. **用户与关系缓存**:一个用户可以有多条关系缓存记录,包括与其上级和下级的关系(1:N)
+9. **推荐关系与关系缓存**:推荐关系表中的直接关系是关系缓存表的数据来源,关系缓存表通过计算扩展为完整的上下级关系网络
+10. **用户与邀请奖励**:一个用户可以有多条邀请奖励记录(1:N)
+11. **被邀请用户与邀请奖励**:一个被邀请用户可以关联多条邀请奖励记录(1:N),例如注册奖励和升级奖励
+12. **推荐码与使用记录**:一个推荐码可以有多条使用记录(1:N)
+13. **用户与邀请码使用**:一个用户可以使用多个邀请码(1:N),但每个邀请码只能被一个用户成功使用
+14. **邀请码所有者与使用记录**:一个邀请码所有者可以有多条邀请码被使用的记录(1:N)
+
+## 4. 索引设计
+
+### 4.1 主键索引
+
+所有表都使用自增的`id`字段作为主键。
+
+### 4.2 唯一索引
+
+- `promotion_user_referrals`: `user_id`(确保每个用户只有一个直接推荐人)
+- `promotion_user_talents`: `user_id`(确保每个用户只有一条达人记录)
+- `promotion_referral_codes`: `code`(确保推荐码唯一)
+- `promotion_referral_codes`: `user_id`(确保每个用户只有一个推荐码)
+- `promotion_talent_configs`: `level`(确保等级唯一)
+- `promotion_profit_rules`: `source_type`(确保来源类型唯一)
+- `promotion_user_relation_cache`: `user_id,related_user_id`(确保用户与上级的关系唯一)
+
+### 4.3 普通索引
+
+- `promotion_user_referrals`: `referrer_id`
+- `promotion_user_talents`: `talent_level`
+- `promotion_profits`: `user_id`, `promotion_member_id`, `source_type,source_id`, `created_at`
+- `promotion_referral_codes`: `status`
+- `promotion_profit_rules`: `status`
+- `promotion_referral_changes`: `user_id`, `change_time`
+- `promotion_user_relation_cache`: `user_id`, `related_user_id`, `level`, `depth`(支持各种查询场景)
+- `promotion_invite_rewards`: `user_id`, `invited_user_id`, `reward_type`, `reward_source`, `status`, `created_at`(支持多种查询和统计场景)
+- `promotion_referral_code_usages`: `code`, `code_owner_id`, `user_id`, `status`, `created_at`(支持邀请码使用记录的多种查询场景)
+
+## 5. JSON字段结构
+
+### 5.1 达人等级权益JSON (benefits)
+
+```json
+{
+  "farm_output_bonus": 0.05,
+  "daily_gift": {
+    "item_id": 1001,
+    "amount": 1
+  },
+  "special_privileges": [
+    "vip_customer_service",
+    "exclusive_avatar_frame"
+  ]
+}
+```
+
+### 5.2 收益分成特殊规则JSON (rules)
+
+```json
+{
+  "min_amount": 10,
+  "max_amount": 1000,
+  "excluded_items": [1001, 1002],
+  "special_rates": [
+    {"talent_level": 5, "bonus_rate": 0.01}
+  ]
+}
+```
+
+## 6. 数据初始化
+
+### 6.1 达人等级配置数据
+
+初始化5级达人的配置数据:
+
+```sql
+INSERT INTO `promotion_talent_configs` (`level`, `name`, `direct_count_required`, `promotion_count_required`, `profit_rate`, `benefits`) VALUES
+(1, '初级达人', 5, 10, 0.0100, '{"farm_output_bonus": 0.01}'),
+(2, '中级达人', 10, 30, 0.0150, '{"farm_output_bonus": 0.02}'),
+(3, '高级达人', 20, 50, 0.0200, '{"farm_output_bonus": 0.03, "daily_gift": {"item_id": 1001, "amount": 1}}'),
+(4, '资深达人', 30, 100, 0.0250, '{"farm_output_bonus": 0.04, "daily_gift": {"item_id": 1001, "amount": 2}}'),
+(5, '顶级达人', 50, 200, 0.0300, '{"farm_output_bonus": 0.05, "daily_gift": {"item_id": 1001, "amount": 3}, "special_privileges": ["vip_customer_service", "exclusive_avatar_frame"]}');
+```
+
+### 6.2 收益分成规则数据
+
+初始化基本的收益分成规则:
+
+```sql
+INSERT INTO `promotion_profit_rules` (`source_type`, `direct_profit_rate`, `max_indirect_level`, `status`, `rules`) VALUES
+('farm_harvest', 0.0500, 20, 1, '{"min_amount": 10, "max_amount": 1000}'),
+('task_complete', 0.0300, 10, 1, '{"min_amount": 5, "max_amount": 500}'),
+('item_sell', 0.0200, 5, 1, '{"min_amount": 1, "max_amount": 100, "excluded_items": [1001, 1002]}');
+```
+
+### 6.3 新用户初始化
+
+新用户注册时,需要:
+1. 创建用户达人记录,设置达人等级为0
+2. 生成用户专属推荐码
+3. 如有推荐人,建立推荐关系
+
+## 7. 数据维护
+
+### 7.1 定期任务
+
+1. **达人等级更新**:定期检查和更新用户的达人等级
+2. **推荐码清理**:定期清理过期或无效的推荐码
+3. **收益统计**:定期统计和汇总团队收益数据
+
+### 7.2 数据清理
+
+1. **收益记录归档**:定期归档过旧的收益记录
+2. **无效关系清理**:清理已注销用户的推荐关系
+
+### 7.3 数据备份
+
+定期备份关键数据表,特别是推荐关系表和达人等级表。
+
+## 8. 性能优化
+
+### 8.1 分表策略
+
+当用户数量增长到一定规模时,可以考虑按用户ID范围对`promotion_user_referrals`和`promotion_profits`表进行分表。
+
+### 8.2 缓存策略
+
+1. 缓存达人等级配置和分成规则等静态数据
+2. 缓存用户的推荐关系和达人信息等频繁访问的数据
+3. 使用Redis缓存用户的所有上级(包括直接和间接)
+4. 使用Redis缓存用户的所有下级(包括直接和间接)
+5. 使用数据库缓存表(`promotion_user_relation_cache`)持久化存储用户的上下级关系
+6. 设置合理的缓存过期时间,如1天
+7. 推荐关系变更时主动更新相关缓存和缓存表
+8. 定期校验和修复缓存表数据,确保与实际推荐关系一致
+
+### 8.3 批量操作
+
+使用批量插入和更新操作处理大量数据,如批量更新达人等级。
+
+## 9. 安全考虑
+
+### 9.1 数据加密
+
+对敏感数据进行加密存储,如推荐码的生成算法。
+
+### 9.2 访问控制
+
+严格控制数据访问权限,确保用户只能访问自己的团队数据。
+
+### 9.3 操作日志
+
+记录关键操作的日志,如达人等级变更、推荐关系建立等。
+
+## 10. 总结
+
+团队模块的数据库设计采用关系型数据库,通过合理的表结构设计和索引优化,支持用户推荐关系、达人等级和团队收益分成等核心功能。设计注重数据完整性、查询效率和可扩展性,能够满足大规模用户场景下的性能需求。
+
+本设计采用只存储直接推荐关系 + 缓存策略的方案,在保持较高查询效率的同时,大幅减少了存储空间,并提高了推荐关系修改的灵活性。通过缓存机制优化了查询性能,同时保持了数据的一致性。
+
+通过JSON字段存储复杂配置,提供了灵活的扩展能力,同时通过合理的数据维护策略确保系统长期稳定运行。推荐关系修改记录表的设计,支持了用户推荐关系的可变更性,同时保留了完整的修改历史,便于审计和追踪。

+ 342 - 0
app/Module/Promotionurs/Docs/枚举定义.md

@@ -0,0 +1,342 @@
+# 团队模块枚举定义
+
+## 1. 概述
+
+本文档定义了团队模块中使用的所有枚举类型,包括推荐关系类型、达人等级、收益来源类型等。这些枚举类型在模块的各个部分都有使用,是理解模块功能的重要基础。
+
+## 2. 推荐关系相关枚举
+
+### 2.1 推荐层级 (REFERRAL_LEVEL)
+
+推荐层级枚举定义了用户之间的推荐关系类型。
+
+```php
+namespace App\Module\Promotion\Enums;
+
+enum REFERRAL_LEVEL: int {
+    case DIRECT = 1;     // 直推关系
+    case INDIRECT = 2;   // 间推关系
+}
+```
+
+#### 推荐层级说明
+
+| 层级类型 | 值 | 说明 | 分成权益 |
+|---------|---|------|---------|
+| 直推关系 | 1 | 用户A直接推荐用户B | 固定比例分成(通常为5%) |
+| 间推关系 | 2 | 用户A通过中间用户间接推荐用户C | 根据达人等级获得分成 |
+
+## 3. 达人等级相关枚举
+
+### 3.1 达人等级 (TALENT_LEVEL)
+
+达人等级枚举定义了用户的达人等级。
+
+```php
+namespace App\Module\Promotion\Enums;
+
+enum TALENT_LEVEL: int {
+    case NONE = 0;       // 非达人
+    case JUNIOR = 1;     // 初级达人
+    case INTERMEDIATE = 2; // 中级达人
+    case SENIOR = 3;     // 高级达人
+    case EXPERT = 4;     // 资深达人
+    case MASTER = 5;     // 顶级达人
+}
+```
+
+#### 达人等级说明
+
+| 达人等级 | 值 | 名称 | 升级条件 | 间推分成比例 |
+|---------|---|------|---------|------------|
+| 非达人 | 0 | 非达人 | 默认 | 0% |
+| 初级达人 | 1 | 初级达人 | 直推5人且团队总人数≥10 | 1% |
+| 中级达人 | 2 | 中级达人 | 直推10人且团队总人数≥30 | 1.5% |
+| 高级达人 | 3 | 高级达人 | 直推20人且团队总人数≥50 | 2% |
+| 资深达人 | 4 | 资深达人 | 直推30人且团队总人数≥100 | 2.5% |
+| 顶级达人 | 5 | 顶级达人 | 直推50人且团队总人数≥200 | 3% |
+
+## 4. 收益相关枚举
+
+### 4.1 收益来源类型 (PROFIT_SOURCE_TYPE)
+
+收益来源类型枚举定义了团队收益的来源。
+
+```php
+namespace App\Module\Promotion\Enums;
+
+enum PROFIT_SOURCE_TYPE: string {
+    case FARM_HARVEST = 'farm_harvest';   // 农场收获
+    case TASK_COMPLETE = 'task_complete'; // 任务完成
+    case ITEM_SELL = 'item_sell';         // 物品出售
+}
+```
+
+#### 收益来源类型说明
+
+| 来源类型 | 值 | 说明 | 默认直推分成 |
+|---------|---|------|------------|
+| 农场收获 | farm_harvest | 来自Farm模块的作物收获 | 5% |
+| 任务完成 | task_complete | 来自Task模块的任务奖励 | 3% |
+| 物品出售 | item_sell | 来自GameItems模块的物品出售 | 2% |
+
+### 4.2 收益记录状态 (PROFIT_RECORD_STATUS)
+
+收益记录状态枚举定义了团队收益记录的状态。
+
+```php
+namespace App\Module\Promotion\Enums;
+
+enum PROFIT_RECORD_STATUS: int {
+    case PENDING = 0;    // 待处理
+    case SUCCESS = 1;    // 成功
+    case FAILED = 2;     // 失败
+}
+```
+
+#### 收益记录状态说明
+
+| 状态 | 值 | 说明 |
+|------|---|------|
+| 待处理 | 0 | 收益记录已创建但尚未处理 |
+| 成功 | 1 | 收益已成功分配到用户账户 |
+| 失败 | 2 | 收益分配失败,需要重试或人工处理 |
+
+## 5. 团队任务相关枚举
+
+### 5.1 团队任务类型 (TEAM_TASK_TYPE)
+
+团队任务类型枚举定义了团队可以执行的任务类型。
+
+```php
+namespace App\Module\Promotion\Enums;
+
+enum TEAM_TASK_TYPE: string {
+    case PLANTING = 'planting';       // 种植任务
+    case HARVESTING = 'harvesting';   // 收获任务
+    case REFERRAL = 'referral';       // 推荐任务
+    case ACTIVITY = 'activity';       // 活跃度任务
+}
+```
+
+#### 团队任务类型说明
+
+| 任务类型 | 值 | 说明 | 完成条件 |
+|---------|---|------|---------|
+| 种植任务 | planting | 团队成员种植特定作物 | 累计种植数量达标 |
+| 收获任务 | harvesting | 团队成员收获特定作物 | 累计收获数量达标 |
+| 推荐任务 | referral | 团队成员发展新成员 | 新增团队成员数量达标 |
+| 活跃度任务 | activity | 团队成员保持活跃 | 活跃成员比例达标 |
+
+### 5.2 团队任务状态 (TEAM_TASK_STATUS)
+
+团队任务状态枚举定义了团队任务的状态。
+
+```php
+namespace App\Module\Promotion\Enums;
+
+enum TEAM_TASK_STATUS: int {
+    case ACTIVE = 1;     // 进行中
+    case COMPLETED = 2;  // 已完成
+    case EXPIRED = 3;    // 已过期
+    case CANCELLED = 4;  // 已取消
+}
+```
+
+#### 团队任务状态说明
+
+| 状态 | 值 | 说明 |
+|------|---|------|
+| 进行中 | 1 | 任务正在进行中 |
+| 已完成 | 2 | 任务已完成并发放奖励 |
+| 已过期 | 3 | 任务未完成但已过期 |
+| 已取消 | 4 | 任务被创建者或管理员取消 |
+
+## 6. 推荐码相关枚举
+
+### 6.1 推荐码状态 (REFERRAL_CODE_STATUS)
+
+推荐码状态枚举定义了推荐码的状态。
+
+```php
+namespace App\Module\Promotion\Enums;
+
+enum REFERRAL_CODE_STATUS: int {
+    case INACTIVE = 0;   // 未激活
+    case ACTIVE = 1;     // 有效
+    case DISABLED = 2;   // 已禁用
+}
+```
+
+#### 推荐码状态说明
+
+| 状态 | 值 | 说明 |
+|------|---|------|
+| 未激活 | 0 | 推荐码已生成但尚未激活 |
+| 有效 | 1 | 推荐码有效,可以使用 |
+| 已禁用 | 2 | 推荐码已被禁用,无法使用 |
+
+## 7. 枚举使用示例
+
+### 7.1 推荐关系类型判断
+
+```php
+// 判断是否为直推关系
+public function isDirectReferral(int $level): bool
+{
+    return $level == REFERRAL_LEVEL::DIRECT;
+}
+
+// 获取推荐关系描述
+public function getReferralLevelDescription(int $level): string
+{
+    switch ($level) {
+        case REFERRAL_LEVEL::DIRECT:
+            return '直接推荐';
+        case REFERRAL_LEVEL::INDIRECT:
+            return '间接推荐';
+        default:
+            return '未知关系';
+    }
+}
+```
+
+### 7.2 达人等级判断
+
+```php
+// 判断是否为达人
+public function isTalent(int $level): bool
+{
+    return $level > TALENT_LEVEL::NONE;
+}
+
+// 获取达人等级名称
+public function getTalentLevelName(int $level): string
+{
+    switch ($level) {
+        case TALENT_LEVEL::NONE:
+            return '非达人';
+        case TALENT_LEVEL::JUNIOR:
+            return '初级达人';
+        case TALENT_LEVEL::INTERMEDIATE:
+            return '中级达人';
+        case TALENT_LEVEL::SENIOR:
+            return '高级达人';
+        case TALENT_LEVEL::EXPERT:
+            return '资深达人';
+        case TALENT_LEVEL::MASTER:
+            return '顶级达人';
+        default:
+            return '未知等级';
+    }
+}
+```
+
+### 7.3 收益来源类型判断
+
+```php
+// 判断是否为农场收益
+public function isFarmProfit(string $sourceType): bool
+{
+    return $sourceType == PROFIT_SOURCE_TYPE::FARM_HARVEST;
+}
+
+// 获取收益来源描述
+public function getProfitSourceDescription(string $sourceType): string
+{
+    switch ($sourceType) {
+        case PROFIT_SOURCE_TYPE::FARM_HARVEST:
+            return '农场收获';
+        case PROFIT_SOURCE_TYPE::TASK_COMPLETE:
+            return '任务完成';
+        case PROFIT_SOURCE_TYPE::ITEM_SELL:
+            return '物品出售';
+        default:
+            return '未知来源';
+    }
+}
+```
+
+### 7.4 团队任务状态判断
+
+```php
+// 判断任务是否可以参与
+public function isTaskJoinable(int $status): bool
+{
+    return $status == TEAM_TASK_STATUS::ACTIVE;
+}
+
+// 获取任务状态描述
+public function getTaskStatusDescription(int $status): string
+{
+    switch ($status) {
+        case TEAM_TASK_STATUS::ACTIVE:
+            return '进行中';
+        case TEAM_TASK_STATUS::COMPLETED:
+            return '已完成';
+        case TEAM_TASK_STATUS::EXPIRED:
+            return '已过期';
+        case TEAM_TASK_STATUS::CANCELLED:
+            return '已取消';
+        default:
+            return '未知状态';
+    }
+}
+```
+
+## 8. 枚举扩展指南
+
+如需添加新的枚举值,请按照以下步骤操作:
+
+1. 在对应的枚举类中添加新的枚举值
+2. 更新相关的业务逻辑,处理新的枚举值
+3. 更新前端展示逻辑,显示新的枚举值
+4. 更新文档,说明新的枚举值的含义和用法
+
+例如,添加新的达人等级:
+
+```php
+// 在TALENT_LEVEL枚举中添加新的达人等级
+enum TALENT_LEVEL: int {
+    case NONE = 0;       // 非达人
+    case JUNIOR = 1;     // 初级达人
+    case INTERMEDIATE = 2; // 中级达人
+    case SENIOR = 3;     // 高级达人
+    case EXPERT = 4;     // 资深达人
+    case MASTER = 5;     // 顶级达人
+    case LEGENDARY = 6;  // 传奇达人(新增)
+}
+```
+
+然后更新相关的业务逻辑:
+
+```php
+// 在TalentService中添加新达人等级的分成比例
+public function getTalentProfitRate(int $level): float
+{
+    switch ($level) {
+        case TALENT_LEVEL::NONE:
+            return 0.0;
+        case TALENT_LEVEL::JUNIOR:
+            return 0.01;
+        case TALENT_LEVEL::INTERMEDIATE:
+            return 0.015;
+        case TALENT_LEVEL::SENIOR:
+            return 0.02;
+        case TALENT_LEVEL::EXPERT:
+            return 0.025;
+        case TALENT_LEVEL::MASTER:
+            return 0.03;
+        case TALENT_LEVEL::LEGENDARY:  // 新增
+            return 0.035;
+        default:
+            return 0.0;
+    }
+}
+```
+
+## 9. 总结
+
+本文档定义了团队模块中使用的所有枚举类型,包括推荐关系类型、达人等级、收益来源类型等。这些枚举类型是模块功能的重要基础,理解这些枚举类型有助于理解模块的整体功能和业务逻辑。
+
+在开发过程中,应该严格按照枚举定义使用这些类型,避免使用魔法数字或字符串,以提高代码的可读性和可维护性。如需添加新的枚举值,请按照枚举扩展指南进行操作,确保相关的业务逻辑和文档都得到更新。

+ 828 - 0
app/Module/Promotionurs/Docs/模块接口.md

@@ -0,0 +1,828 @@
+# 团队模块接口
+
+## 1. 概述
+
+团队模块对外提供一系列服务接口,用于管理用户推荐关系、达人等级和团队收益分成。这些接口主要供其他模块调用,实现模块间的交互和数据共享。本文档详细说明了团队模块提供的服务接口、参数和返回值。
+
+## 2. 接口规范
+
+### 2.1 接口命名规范
+
+- 接口方法名采用驼峰命名法,如`createReferralRelation`
+- 接口方法名应清晰表达功能,动词开头,如`getUserReferrers`
+- 查询类接口使用`get`前缀,如`getUserTalentInfo`
+- 创建类接口使用`create`前缀,如`createReferralCode`
+- 更新类接口使用`update`前缀,如`updateUserTalent`
+- 删除类接口使用`delete`前缀,如`deleteReferralRelation`
+- 检查类接口使用`check`前缀,如`checkReferralCode`
+
+### 2.2 参数规范
+
+- 必选参数放在前面,可选参数放在后面
+- 参数类型明确,使用强类型
+- 复杂参数使用数组或对象传递
+- 敏感参数不直接暴露,如密码等
+
+### 2.3 返回值规范
+
+- 返回值类型明确,使用强类型
+- 成功操作返回布尔值或操作结果
+- 查询操作返回数据对象或数组
+- 异常情况抛出异常,不返回错误码
+
+### 2.4 异常处理
+
+- 业务异常使用自定义异常类,如`ReferralException`
+- 异常信息清晰明确,便于定位问题
+- 异常包含错误码和错误信息
+- 调用方需捕获并处理异常
+
+## 3. 推荐关系服务接口 (ReferralService)
+
+### 3.1 建立推荐关系
+
+```php
+/**
+ * 建立推荐关系
+ * 
+ * @param int $userId 用户ID
+ * @param int $referrerId 推荐人ID
+ * @return bool 是否成功
+ * @throws UserNotFoundException 用户不存在异常
+ * @throws ReferralException 推荐关系异常
+ */
+public function createReferralRelation(int $userId, int $referrerId): bool;
+```
+
+**功能说明**:
+- 建立用户与推荐人之间的推荐关系
+- 同时建立直推和间推关系
+- 更新推荐人的直推人数和团队总人数
+- 触发检查达人等级
+
+**调用示例**:
+```php
+try {
+    $result = $referralService->createReferralRelation(1001, 1000);
+    if ($result) {
+        // 推荐关系建立成功
+    }
+} catch (UserNotFoundException $e) {
+    // 用户不存在
+} catch (ReferralException $e) {
+    // 推荐关系异常
+}
+```
+
+### 3.2 获取用户的推荐人
+
+```php
+/**
+ * 获取用户的推荐人
+ * 
+ * @param int $userId 用户ID
+ * @param int $level 推荐层级(1=直推,2=间推,0=全部)
+ * @return array 推荐人列表
+ */
+public function getUserReferrers(int $userId, int $level = 0): array;
+```
+
+**功能说明**:
+- 获取用户的所有推荐人(上级)
+- 可以指定只获取直推或间推关系
+- 返回推荐人的基本信息和推荐关系
+
+**调用示例**:
+```php
+// 获取所有推荐人
+$allReferrers = $referralService->getUserReferrers(1001);
+
+// 只获取直推推荐人
+$directReferrers = $referralService->getUserReferrers(1001, 1);
+```
+
+### 3.3 获取用户的团队成员
+
+```php
+/**
+ * 获取用户的团队成员
+ * 
+ * @param int $userId 用户ID
+ * @param int $level 推荐层级(1=直推,2=间推,0=全部)
+ * @param int $page 页码
+ * @param int $pageSize 每页数量
+ * @return array 团队成员列表和分页信息
+ */
+public function getPromotionMembers(int $userId, int $level = 0, int $page = 1, int $pageSize = 20): array;
+```
+
+**功能说明**:
+- 获取用户的所有团队成员(下级)
+- 可以指定只获取直推或间推成员
+- 支持分页查询
+- 返回团队成员的基本信息和推荐关系
+
+**调用示例**:
+```php
+// 获取所有团队成员,第1页,每页20条
+$allMembers = $referralService->getPromotionMembers(1000);
+
+// 只获取直推成员,第1页,每页50条
+$directMembers = $referralService->getPromotionMembers(1000, 1, 1, 50);
+```
+
+### 3.4 检查推荐关系
+
+```php
+/**
+ * 检查用户是否是另一用户的推荐人
+ * 
+ * @param int $userId 用户ID
+ * @param int $memberId 成员ID
+ * @param int $level 推荐层级(1=直推,2=间推,0=任意层级)
+ * @return bool 是否存在推荐关系
+ */
+public function checkReferralRelation(int $userId, int $memberId, int $level = 0): bool;
+```
+
+**功能说明**:
+- 检查用户是否是另一用户的推荐人
+- 可以指定检查直推或间推关系
+- 返回布尔值表示是否存在推荐关系
+
+**调用示例**:
+```php
+// 检查用户1000是否是用户1001的推荐人(任意层级)
+$isReferrer = $referralService->checkReferralRelation(1000, 1001);
+
+// 检查用户1000是否是用户1001的直推推荐人
+$isDirectReferrer = $referralService->checkReferralRelation(1000, 1001, 1);
+```
+
+### 3.5 生成推荐码
+
+```php
+/**
+ * 生成用户推荐码
+ * 
+ * @param int $userId 用户ID
+ * @return string 推荐码
+ * @throws UserNotFoundException 用户不存在异常
+ */
+public function generateReferralCode(int $userId): string;
+```
+
+**功能说明**:
+- 为用户生成唯一的推荐码
+- 如果用户已有推荐码,则返回现有推荐码
+- 推荐码可用于邀请新用户注册
+
+**调用示例**:
+```php
+try {
+    $code = $referralService->generateReferralCode(1000);
+    // 生成的推荐码
+} catch (UserNotFoundException $e) {
+    // 用户不存在
+}
+```
+
+### 3.6 验证推荐码
+
+```php
+/**
+ * 验证推荐码并获取推荐人ID
+ * 
+ * @param string $code 推荐码
+ * @return int|null 推荐人ID,无效返回null
+ */
+public function verifyReferralCode(string $code): ?int;
+```
+
+**功能说明**:
+- 验证推荐码的有效性
+- 返回推荐码对应的用户ID
+- 无效推荐码返回null
+
+**调用示例**:
+```php
+$referrerId = $referralService->verifyReferralCode('ABC123');
+if ($referrerId !== null) {
+    // 有效的推荐码,获取到推荐人ID
+} else {
+    // 无效的推荐码
+}
+```
+
+## 4. 达人等级服务接口 (TalentService)
+
+### 4.1 获取用户达人信息
+
+```php
+/**
+ * 获取用户达人信息
+ * 
+ * @param int $userId 用户ID
+ * @return array 达人信息
+ */
+public function getUserTalentInfo(int $userId): array;
+```
+
+**功能说明**:
+- 获取用户的达人等级信息
+- 包括达人等级、直推人数、团队总人数等
+- 同时返回达人等级对应的权益信息
+
+**调用示例**:
+```php
+$talentInfo = $talentService->getUserTalentInfo(1000);
+// 达人信息数组
+```
+
+### 4.2 检查并更新达人等级
+
+```php
+/**
+ * 检查并更新用户达人等级
+ * 
+ * @param int $userId 用户ID
+ * @return bool 是否有等级变化
+ */
+public function checkAndUpdateTalentLevel(int $userId): bool;
+```
+
+**功能说明**:
+- 检查用户的团队规模,计算新的达人等级
+- 如果等级有变化,更新用户的达人等级
+- 等级提升时触发达人升级事件
+
+**调用示例**:
+```php
+$levelChanged = $talentService->checkAndUpdateTalentLevel(1000);
+if ($levelChanged) {
+    // 达人等级有变化
+}
+```
+
+### 4.3 获取达人等级配置
+
+```php
+/**
+ * 获取达人等级配置
+ * 
+ * @param int $level 达人等级,0表示获取所有等级
+ * @return array 达人等级配置
+ */
+public function getTalentLevelConfig(int $level = 0): array;
+```
+
+**功能说明**:
+- 获取指定达人等级的配置信息
+- 包括等级名称、升级条件、权益等
+- 可以获取所有等级的配置信息
+
+**调用示例**:
+```php
+// 获取所有达人等级配置
+$allConfigs = $talentService->getTalentLevelConfig();
+
+// 获取3级达人的配置
+$level3Config = $talentService->getTalentLevelConfig(3);
+```
+
+### 4.4 获取达人等级分成比例
+
+```php
+/**
+ * 获取达人等级的间推分成比例
+ * 
+ * @param int $level 达人等级
+ * @return float 分成比例
+ */
+public function getTalentProfitRate(int $level): float;
+```
+
+**功能说明**:
+- 获取指定达人等级的间推分成比例
+- 用于计算团队收益分成
+- 非达人(0级)返回0
+
+**调用示例**:
+```php
+// 获取3级达人的分成比例
+$rate = $talentService->getTalentProfitRate(3);
+// 返回0.02 (2%)
+```
+
+## 5. 团队收益服务接口 (PromotionProfitService)
+
+### 5.1 计算团队收益分成
+
+```php
+/**
+ * 计算团队收益分成
+ * 
+ * @param int $userId 产生收益的用户ID
+ * @param string $sourceType 收益来源类型
+ * @param int $sourceId 收益来源ID
+ * @param int $itemId 物品ID
+ * @param int $amount 收益数量
+ * @return bool 是否成功
+ */
+public function calculatePromotionProfit(int $userId, string $sourceType, int $sourceId, int $itemId, int $amount): bool;
+```
+
+**功能说明**:
+- 计算用户产生收益时的团队分成
+- 根据推荐关系和达人等级计算分成比例
+- 记录分成收益并添加到推荐人账户
+- 支持多种收益来源类型
+
+**调用示例**:
+```php
+// 用户1001收获农场作物,产生100个物品ID为2001的收益
+$result = $promotionProfitService->calculatePromotionProfit(1001, 'farm_harvest', 5001, 2001, 100);
+if ($result) {
+    // 分成计算成功
+}
+```
+
+### 5.2 获取用户团队收益统计
+
+```php
+/**
+ * 获取用户团队收益统计
+ * 
+ * @param int $userId 用户ID
+ * @param string $sourceType 收益来源类型,空字符串表示所有类型
+ * @param string $timeRange 时间范围(today,yesterday,week,month,all)
+ * @return array 收益统计
+ */
+public function getUserPromotionProfitStats(int $userId, string $sourceType = '', string $timeRange = 'all'): array;
+```
+
+**功能说明**:
+- 获取用户的团队收益统计数据
+- 可以按收益来源类型和时间范围筛选
+- 返回总收益、直推收益、间推收益等统计数据
+
+**调用示例**:
+```php
+// 获取用户1000的所有团队收益统计
+$allStats = $promotionProfitService->getUserPromotionProfitStats(1000);
+
+// 获取用户1000的本月农场收益统计
+$farmMonthStats = $promotionProfitService->getUserPromotionProfitStats(1000, 'farm_harvest', 'month');
+```
+
+### 5.3 获取用户团队收益记录
+
+```php
+/**
+ * 获取用户团队收益记录
+ * 
+ * @param int $userId 用户ID
+ * @param string $sourceType 收益来源类型,空字符串表示所有类型
+ * @param int $page 页码
+ * @param int $pageSize 每页数量
+ * @return array 收益记录和分页信息
+ */
+public function getUserPromotionProfitRecords(int $userId, string $sourceType = '', int $page = 1, int $pageSize = 20): array;
+```
+
+**功能说明**:
+- 获取用户的团队收益详细记录
+- 可以按收益来源类型筛选
+- 支持分页查询
+- 返回收益记录的详细信息
+
+**调用示例**:
+```php
+// 获取用户1000的所有团队收益记录,第1页,每页20条
+$allRecords = $promotionProfitService->getUserPromotionProfitRecords(1000);
+
+// 获取用户1000的农场收益记录,第1页,每页50条
+$farmRecords = $promotionProfitService->getUserPromotionProfitRecords(1000, 'farm_harvest', 1, 50);
+```
+
+### 5.4 获取收益分成规则
+
+```php
+/**
+ * 获取收益分成规则
+ * 
+ * @param string $sourceType 收益来源类型
+ * @return array 分成规则
+ */
+public function getProfitRule(string $sourceType): array;
+```
+
+**功能说明**:
+- 获取指定收益来源类型的分成规则
+- 包括直推分成比例、最大间推层级等
+- 用于计算团队收益分成
+
+**调用示例**:
+```php
+// 获取农场收益的分成规则
+$farmRule = $promotionProfitService->getProfitRule('farm_harvest');
+```
+
+## 6. 事件接口
+
+团队模块会触发以下事件,其他模块可以监听这些事件:
+
+### 6.1 达人等级提升事件 (TalentLevelUpEvent)
+
+```php
+/**
+ * 达人等级提升事件
+ */
+class TalentLevelUpEvent
+{
+    /**
+     * @var int 用户ID
+     */
+    public $userId;
+    
+    /**
+     * @var int 旧等级
+     */
+    public $oldLevel;
+    
+    /**
+     * @var int 新等级
+     */
+    public $newLevel;
+    
+    /**
+     * 构造函数
+     */
+    public function __construct(int $userId, int $oldLevel, int $newLevel)
+    {
+        $this->userId = $userId;
+        $this->oldLevel = $oldLevel;
+        $this->newLevel = $newLevel;
+    }
+}
+```
+
+**事件说明**:
+- 当用户达人等级提升时触发
+- 包含用户ID、旧等级和新等级
+- 可用于任务完成、发放奖励等
+
+**监听示例**:
+```php
+// 在EventServiceProvider中注册监听器
+protected $listen = [
+    'App\Module\Promotion\Events\TalentLevelUpEvent' => [
+        'App\Module\Task\Listeners\TalentLevelUpListener',
+    ],
+];
+
+// 监听器实现
+public function handle(TalentLevelUpEvent $event)
+{
+    // 处理达人等级提升事件
+    $userId = $event->userId;
+    $newLevel = $event->newLevel;
+    
+    // 完成相关任务
+    $this->taskService->completeTask($userId, 'talent_level_up', ['level' => $newLevel]);
+}
+```
+
+### 6.2 推荐关系创建事件 (ReferralCreatedEvent)
+
+```php
+/**
+ * 推荐关系创建事件
+ */
+class ReferralCreatedEvent
+{
+    /**
+     * @var int 用户ID
+     */
+    public $userId;
+    
+    /**
+     * @var int 推荐人ID
+     */
+    public $referrerId;
+    
+    /**
+     * @var int 推荐层级
+     */
+    public $level;
+    
+    /**
+     * 构造函数
+     */
+    public function __construct(int $userId, int $referrerId, int $level)
+    {
+        $this->userId = $userId;
+        $this->referrerId = $referrerId;
+        $this->level = $level;
+    }
+}
+```
+
+**事件说明**:
+- 当建立新的推荐关系时触发
+- 包含用户ID、推荐人ID和推荐层级
+- 可用于任务完成、发放奖励等
+
+**监听示例**:
+```php
+// 在EventServiceProvider中注册监听器
+protected $listen = [
+    'App\Module\Promotion\Events\ReferralCreatedEvent' => [
+        'App\Module\Task\Listeners\ReferralCreatedListener',
+    ],
+];
+
+// 监听器实现
+public function handle(ReferralCreatedEvent $event)
+{
+    // 处理推荐关系创建事件
+    $userId = $event->userId;
+    $referrerId = $event->referrerId;
+    
+    // 完成相关任务
+    if ($event->level == 1) {
+        $this->taskService->completeTask($referrerId, 'direct_referral');
+    }
+}
+```
+
+## 7. 监听的事件
+
+团队模块会监听以下来自其他模块的事件:
+
+### 7.1 用户注册事件 (UserRegisteredEvent)
+
+```php
+// 用户模块中定义的事件
+namespace App\Module\User\Events;
+
+class UserRegisteredEvent
+{
+    /**
+     * @var int 用户ID
+     */
+    public $userId;
+    
+    /**
+     * @var string 推荐码
+     */
+    public $referralCode;
+    
+    /**
+     * 构造函数
+     */
+    public function __construct(int $userId, string $referralCode = '')
+    {
+        $this->userId = $userId;
+        $this->referralCode = $referralCode;
+    }
+}
+```
+
+**监听处理**:
+```php
+namespace App\Module\Promotion\Listeners;
+
+use App\Module\User\Events\UserRegisteredEvent;
+use App\Module\Promotion\Services\ReferralService;
+
+class UserRegisteredListener
+{
+    /**
+     * @var ReferralService
+     */
+    protected $referralService;
+    
+    /**
+     * 构造函数
+     */
+    public function __construct(ReferralService $referralService)
+    {
+        $this->referralService = $referralService;
+    }
+    
+    /**
+     * 处理事件
+     */
+    public function handle(UserRegisteredEvent $event)
+    {
+        // 处理用户注册事件
+        $userId = $event->userId;
+        $referralCode = $event->referralCode;
+        
+        // 验证推荐码
+        if (!empty($referralCode)) {
+            $referrerId = $this->referralService->verifyReferralCode($referralCode);
+            if ($referrerId !== null) {
+                // 建立推荐关系
+                $this->referralService->createReferralRelation($userId, $referrerId);
+            }
+        }
+        
+        // 生成用户推荐码
+        $this->referralService->generateReferralCode($userId);
+    }
+}
+```
+
+### 7.2 作物收获事件 (CropHarvestedEvent)
+
+```php
+// 农场模块中定义的事件
+namespace App\Module\Farm\Events;
+
+class CropHarvestedEvent
+{
+    /**
+     * @var int 用户ID
+     */
+    public $userId;
+    
+    /**
+     * @var int 作物ID
+     */
+    public $cropId;
+    
+    /**
+     * @var int 产出物品ID
+     */
+    public $itemId;
+    
+    /**
+     * @var int 产出数量
+     */
+    public $amount;
+    
+    /**
+     * 构造函数
+     */
+    public function __construct(int $userId, int $cropId, int $itemId, int $amount)
+    {
+        $this->userId = $userId;
+        $this->cropId = $cropId;
+        $this->itemId = $itemId;
+        $this->amount = $amount;
+    }
+}
+```
+
+**监听处理**:
+```php
+namespace App\Module\Promotion\Listeners;
+
+use App\Module\Farm\Events\CropHarvestedEvent;
+use App\Module\Promotion\Services\PromotionProfitService;
+
+class CropHarvestedListener
+{
+    /**
+     * @var PromotionProfitService
+     */
+    protected $promotionProfitService;
+    
+    /**
+     * 构造函数
+     */
+    public function __construct(PromotionProfitService $promotionProfitService)
+    {
+        $this->promotionProfitService = $promotionProfitService;
+    }
+    
+    /**
+     * 处理事件
+     */
+    public function handle(CropHarvestedEvent $event)
+    {
+        // 处理作物收获事件
+        $userId = $event->userId;
+        $cropId = $event->cropId;
+        $itemId = $event->itemId;
+        $amount = $event->amount;
+        
+        // 计算团队收益分成
+        $this->promotionProfitService->calculatePromotionProfit(
+            $userId,
+            'farm_harvest',
+            $cropId,
+            $itemId,
+            $amount
+        );
+    }
+}
+```
+
+## 8. 接口使用示例
+
+### 8.1 用户注册时建立推荐关系
+
+```php
+// 用户注册处理
+public function register(Request $request)
+{
+    // 创建用户
+    $userId = $this->userService->createUser($request->all());
+    
+    // 处理推荐码
+    $referralCode = $request->input('referral_code');
+    if (!empty($referralCode)) {
+        try {
+            // 验证推荐码
+            $referrerId = $this->referralService->verifyReferralCode($referralCode);
+            if ($referrerId !== null) {
+                // 建立推荐关系
+                $this->referralService->createReferralRelation($userId, $referrerId);
+            }
+        } catch (\Exception $e) {
+            // 处理异常
+            Log::error('建立推荐关系失败', [
+                'user_id' => $userId,
+                'referral_code' => $referralCode,
+                'error' => $e->getMessage()
+            ]);
+        }
+    }
+    
+    // 生成用户推荐码
+    $this->referralService->generateReferralCode($userId);
+    
+    // 返回结果
+    return response()->json([
+        'success' => true,
+        'user_id' => $userId
+    ]);
+}
+```
+
+### 8.2 作物收获时计算团队收益
+
+```php
+// 作物收获处理
+public function harvestCrop(int $cropId)
+{
+    return DB::transaction(function () use ($cropId) {
+        // 收获作物
+        $result = $this->cropService->harvestCrop($cropId);
+        
+        // 获取收获信息
+        $userId = $result['user_id'];
+        $itemId = $result['item_id'];
+        $amount = $result['amount'];
+        
+        // 计算团队收益分成
+        $this->promotionProfitService->calculatePromotionProfit(
+            $userId,
+            'farm_harvest',
+            $cropId,
+            $itemId,
+            $amount
+        );
+        
+        // 触发作物收获事件
+        event(new CropHarvestedEvent($userId, $cropId, $itemId, $amount));
+        
+        return $result;
+    });
+}
+```
+
+### 8.3 获取用户团队信息
+
+```php
+// 获取用户团队信息
+public function getUserPromotionInfo(int $userId)
+{
+    // 获取用户达人信息
+    $talentInfo = $this->talentService->getUserTalentInfo($userId);
+    
+    // 获取直推成员
+    $directMembers = $this->referralService->getPromotionMembers($userId, 1, 1, 10);
+    
+    // 获取团队收益统计
+    $profitStats = $this->promotionProfitService->getUserPromotionProfitStats($userId);
+    
+    // 获取最近收益记录
+    $recentProfits = $this->promotionProfitService->getUserPromotionProfitRecords($userId, '', 1, 5);
+    
+    // 返回结果
+    return [
+        'talent_info' => $talentInfo,
+        'direct_members' => $directMembers,
+        'profit_stats' => $profitStats,
+        'recent_profits' => $recentProfits
+    ];
+}
+```
+
+## 9. 总结
+
+团队模块提供了丰富的服务接口,用于管理用户推荐关系、达人等级和团队收益分成。这些接口设计清晰、功能完善,能够满足团队系统的各种需求。通过事件机制,团队模块与其他模块保持松耦合的交互方式,提高了系统的可维护性和可扩展性。在使用这些接口时,需要注意异常处理和事务管理,确保数据的一致性和完整性。

+ 129 - 0
app/Module/Promotionurs/Docs/直间推收益机制.md

@@ -0,0 +1,129 @@
+# 直间推收益机制
+
+## 1. 概述
+
+直间推收益机制是团队模块的核心功能之一,通过建立用户间的推荐关系,在团队成员产生收益时,按照一定比例分配给上级推荐人,形成多层级的收益分成体系,鼓励用户发展团队,形成良性的社交生态。
+
+## 2. 推荐关系定义
+
+### 2.1 直推关系
+
+用户A直接推荐用户B注册,则用户A是用户B的直推上级(直接推荐人)。直推关系是一级关系,直接存储在数据库中。
+
+### 2.2 间推关系
+
+用户A推荐用户B,用户B推荐用户C,则用户A是用户C的间推上级。间推关系是多级关系,可以通过直推关系递归计算得到,或通过缓存优化查询性能。
+
+### 2.3 团队成员
+
+用户的所有直推和间推下级构成该用户的团队。团队规模是衡量用户影响力的重要指标,也是达人等级评定的主要依据。
+
+## 3. 收益分成比例
+
+### 3.1 直推分成比例
+
+直推上级获得下级收益的**5%**。这一比例固定不变,不受达人等级影响。
+
+例如:用户B收获农作物获得100单位收益,其直推上级用户A将获得5单位分成收益。
+
+### 3.2 间推分成比例
+
+间推分成比例与达人等级相关,具体如下:
+
+| 达人等级 | 名称 | 间推分成比例 |
+|---------|------|------------|
+| 0 | 非达人 | 0% |
+| 1 | 初级达人 | 1% |
+| 2 | 中级达人 | 1.5% |
+| 3 | 高级达人 | 2% |
+| 4 | 资深达人 | 2.5% |
+| 5 | 顶级达人 | 3% |
+
+例如:用户C收获农作物获得100单位收益,其间推上级用户A若为顶级达人,将获得3单位分成收益。
+
+### 3.3 分成范围限制
+
+间推分成仅对**20代以内**的团队成员有效。超过20代的间推关系不产生收益分成。
+
+这一限制是为了控制收益分配的深度,避免过度分散收益,同时也是出于系统性能考虑。
+
+## 4. 收益来源类型
+
+### 4.1 农场收获收益
+
+当团队成员在农场收获作物时,系统会根据收获数量计算分成收益。
+
+收益计算公式:
+- 直推分成 = 收获数量 × 5%
+- 间推分成 = 收获数量 × 达人等级对应分成比例
+
+### 4.2 其他可能的收益来源
+
+系统设计支持扩展其他收益来源类型,如:
+- 任务完成奖励
+- 活动参与奖励
+- 商店销售收益
+
+每种收益来源可以配置不同的分成规则。
+
+## 5. 收益分成流程
+
+### 5.1 收益产生
+
+1. 用户完成收益产生动作(如收获农作物)
+2. 系统记录原始收益
+3. 触发收益分成事件
+
+### 5.2 分成计算
+
+1. 获取用户的所有上级(直推和间推)
+2. 根据推荐关系类型和达人等级计算分成比例
+3. 计算每个上级的分成金额
+
+### 5.3 分成发放
+
+1. 记录分成收益明细
+2. 将分成收益添加到上级账户
+3. 通知上级收到团队收益
+
+## 6. 示例场景
+
+假设推荐链为:A → B → C → D(A推荐B,B推荐C,C推荐D)
+
+当D收获农作物获得1000单位收益时:
+
+1. C作为D的直推上级,获得:1000 × 5% = 50单位
+2. B作为D的间推上级(假设B是中级达人),获得:1000 × 1.5% = 15单位
+3. A作为D的间推上级(假设A是顶级达人),获得:1000 × 3% = 30单位
+
+总分成:50 + 15 + 30 = 95单位(占原始收益的9.5%)
+
+## 7. 注意事项
+
+1. **收益上限**:系统可能设置单次分成的上限,防止极端情况下的过度分成
+2. **最小分成单位**:分成计算结果会四舍五入到最小单位,避免出现小数
+3. **分成记录**:所有分成操作都会详细记录,便于用户查询和系统审计
+4. **达人等级变化**:当用户达人等级变化时,将立即影响其间推分成比例
+5. **推荐关系变更**:推荐关系一旦建立,通常不允许变更,以维护系统稳定性
+
+## 8. 与其他模块的交互
+
+### 8.1 与Farm模块交互
+
+- 监听Farm模块的作物收获事件
+- 获取收获数量和作物类型
+- 计算并分配团队收益
+
+### 8.2 与User模块交互
+
+- 获取用户基本信息
+- 验证用户状态和权限
+
+### 8.3 与GameItems模块交互
+
+- 将分成收益添加到用户物品库
+- 记录收益来源和分成比例
+
+## 9. 总结
+
+直间推收益机制是团队系统的核心功能,通过合理的分成比例设计,既鼓励用户发展直接下级,也激励用户提升达人等级以获得更高的间推分成。这种多层级的收益分成体系,有效促进了用户社交网络的扩展和活跃度的提升,是游戏社交生态的重要支撑。

+ 793 - 0
app/Module/Promotionurs/Docs/缓存策略.md

@@ -0,0 +1,793 @@
+# 团队模块缓存策略
+
+## 1. 概述
+
+团队模块采用只存储直接推荐关系 + 缓存策略的设计方案,通过缓存机制优化查询性能,同时保持数据的一致性。本文档详细说明团队模块的缓存策略,包括缓存内容、缓存键设计、缓存更新策略和缓存失效机制。
+
+## 2. 缓存内容
+
+### 2.1 推荐关系缓存
+
+推荐关系是团队模块最核心的数据,也是查询频率最高的数据。系统缓存以下推荐关系数据:
+
+1. **用户的所有上级**:包括直接上级(直推)和间接上级(间推)
+2. **用户的所有下级**:包括直接下级(直推)和间接下级(间推)
+3. **用户的直推人数**:用户直接推荐的人数
+4. **用户的团队总人数**:用户团队中的总人数(包括直推和间推)
+
+### 2.2 达人等级缓存
+
+达人等级相关的数据变更频率较低,但查询频率较高:
+
+1. **用户的达人等级**:用户当前的达人等级
+2. **达人等级配置**:各个达人等级的配置信息
+3. **达人分成比例**:各个达人等级对应的分成比例
+
+### 2.3 收益分成规则缓存
+
+收益分成规则是相对静态的数据,适合长期缓存:
+
+1. **分成规则**:不同来源类型的分成规则
+2. **直推分成比例**:直推关系的分成比例
+3. **间推分成比例**:间推关系的分成比例(与达人等级相关)
+
+## 3. 缓存键设计
+
+### 3.1 推荐关系缓存键
+
+```
+// 用户的所有上级(包括直接和间接)
+promotion:user:{userId}:all_referrers
+
+// 用户的所有下级(包括直接和间接)
+promotion:user:{userId}:all_members
+
+// 用户的直推人数
+promotion:user:{userId}:direct_count
+
+// 用户的团队总人数
+promotion:user:{userId}:promotion_count
+```
+
+### 3.2 达人等级缓存键
+
+```
+// 用户的达人等级
+promotion:user:{userId}:talent_level
+
+// 达人等级配置
+promotion:talent_config:{level}
+
+// 所有达人等级配置
+promotion:talent_configs
+```
+
+### 3.3 收益分成规则缓存键
+
+```
+// 分成规则
+promotion:profit_rule:{sourceType}
+
+// 所有分成规则
+promotion:profit_rules
+```
+
+## 4. 缓存值结构
+
+### 4.1 推荐关系缓存值
+
+```json
+// 用户的所有上级
+[
+  {
+    "user_id": 1,
+    "level": 1,
+    "created_at": "2023-05-01 10:00:00"
+  },
+  {
+    "user_id": 2,
+    "level": 2,
+    "created_at": "2023-05-01 10:00:00"
+  }
+]
+
+// 用户的所有下级
+[
+  {
+    "user_id": 3,
+    "level": 1,
+    "created_at": "2023-05-01 11:00:00"
+  },
+  {
+    "user_id": 4,
+    "level": 2,
+    "created_at": "2023-05-01 12:00:00"
+  }
+]
+
+// 用户的直推人数
+5
+
+// 用户的团队总人数
+20
+```
+
+### 4.2 达人等级缓存值
+
+```json
+// 用户的达人等级
+{
+  "talent_level": 3,
+  "direct_count": 20,
+  "promotion_count": 50
+}
+
+// 达人等级配置
+{
+  "level": 3,
+  "name": "高级达人",
+  "direct_count_required": 20,
+  "promotion_count_required": 50,
+  "profit_rate": 0.02,
+  "benefits": {
+    "farm_output_bonus": 0.03,
+    "daily_gift": {
+      "item_id": 1001,
+      "amount": 1
+    }
+  }
+}
+```
+
+### 4.3 收益分成规则缓存值
+
+```json
+// 分成规则
+{
+  "source_type": "farm_harvest",
+  "direct_profit_rate": 0.05,
+  "max_indirect_level": 20,
+  "status": 1,
+  "rules": {
+    "min_amount": 10,
+    "max_amount": 1000
+  }
+}
+```
+
+## 5. 缓存更新策略
+
+### 5.1 懒加载更新
+
+对于查询频率较高但不需要实时性的数据,采用懒加载更新策略:
+
+```php
+/**
+ * 获取用户的所有上级(懒加载更新)
+ */
+public function getAllReferrers(int $userId): array
+{
+    // 缓存键
+    $cacheKey = "promotion:user:{$userId}:all_referrers";
+    
+    // 尝试从缓存获取
+    $cachedData = Redis::get($cacheKey);
+    if ($cachedData !== null) {
+        return json_decode($cachedData, true);
+    }
+    
+    // 缓存未命中,计算数据
+    $allReferrers = [];
+    $this->calculateAllReferrers($userId, $allReferrers);
+    
+    // 缓存数据
+    Redis::setex($cacheKey, 86400, json_encode($allReferrers)); // 缓存1天
+    
+    return $allReferrers;
+}
+```
+
+### 5.2 主动更新
+
+对于变更频率较低但需要实时性的数据,采用主动更新策略:
+
+```php
+/**
+ * 更新用户的推荐关系(主动更新)
+ */
+public function updateReferralRelation(int $userId, int $newReferrerId): bool
+{
+    // 更新数据库
+    $result = $this->referralRepository->updateReferrer($userId, $newReferrerId);
+    
+    if ($result) {
+        // 清除相关缓存
+        $this->clearReferralCaches($userId, $this->getOldReferrerId($userId), $newReferrerId);
+    }
+    
+    return $result;
+}
+
+/**
+ * 清除推荐关系相关的缓存
+ */
+private function clearReferralCaches(int $userId, ?int $oldReferrerId, int $newReferrerId): void
+{
+    // 清除用户自身的缓存
+    Redis::del("promotion:user:{$userId}:all_referrers");
+    
+    // 清除旧推荐人相关的缓存
+    if ($oldReferrerId) {
+        Redis::del("promotion:user:{$oldReferrerId}:all_members");
+        Redis::del("promotion:user:{$oldReferrerId}:direct_count");
+        Redis::del("promotion:user:{$oldReferrerId}:promotion_count");
+        
+        // 清除旧推荐人的所有上级的缓存
+        $oldUpperReferrers = $this->getAllReferrers($oldReferrerId);
+        foreach ($oldUpperReferrers as $referrer) {
+            Redis::del("promotion:user:{$referrer['user_id']}:all_members");
+            Redis::del("promotion:user:{$referrer['user_id']}:promotion_count");
+        }
+    }
+    
+    // 清除新推荐人相关的缓存
+    Redis::del("promotion:user:{$newReferrerId}:all_members");
+    Redis::del("promotion:user:{$newReferrerId}:direct_count");
+    Redis::del("promotion:user:{$newReferrerId}:promotion_count");
+    
+    // 清除新推荐人的所有上级的缓存
+    $newUpperReferrers = $this->getAllReferrers($newReferrerId);
+    foreach ($newUpperReferrers as $referrer) {
+        Redis::del("promotion:user:{$referrer['user_id']}:all_members");
+        Redis::del("promotion:user:{$referrer['user_id']}:promotion_count");
+    }
+    
+    // 清除用户的所有下级的缓存
+    $allMembers = $this->getAllPromotionMembers($userId);
+    foreach ($allMembers['members'] as $member) {
+        Redis::del("promotion:user:{$member['user_id']}:all_referrers");
+    }
+}
+```
+
+### 5.3 定期刷新
+
+对于变更频率较低且不需要实时性的数据,采用定期刷新策略:
+
+```php
+/**
+ * 定期刷新达人等级配置缓存
+ */
+public function refreshTalentConfigCache(): void
+{
+    // 获取所有达人等级配置
+    $configs = $this->talentConfigRepository->getAll();
+    
+    // 缓存所有配置
+    Redis::setex("promotion:talent_configs", 86400 * 7, json_encode($configs)); // 缓存7天
+    
+    // 缓存单个配置
+    foreach ($configs as $config) {
+        Redis::setex("promotion:talent_config:{$config->level}", 86400 * 7, json_encode($config));
+    }
+}
+```
+
+## 6. 缓存失效机制
+
+### 6.1 过期时间设置
+
+不同类型的数据设置不同的过期时间:
+
+| 数据类型 | 过期时间 | 说明 |
+|---------|---------|------|
+| 推荐关系 | 1天 | 变更频率中等,查询频率高 |
+| 达人等级 | 1天 | 变更频率低,查询频率高 |
+| 达人配置 | 7天 | 变更频率极低,查询频率高 |
+| 分成规则 | 7天 | 变更频率极低,查询频率高 |
+
+### 6.2 主动失效
+
+在以下情况下主动使缓存失效:
+
+1. **推荐关系变更**:用户的推荐关系发生变更时
+2. **达人等级变更**:用户的达人等级发生变更时
+3. **配置更新**:达人等级配置或分成规则更新时
+4. **用户状态变更**:用户状态发生变更(如封禁、注销)时
+
+```php
+/**
+ * 更新用户的达人等级
+ */
+public function updateTalentLevel(int $userId, int $newLevel): bool
+{
+    // 更新数据库
+    $result = $this->talentRepository->updateLevel($userId, $newLevel);
+    
+    if ($result) {
+        // 清除相关缓存
+        Redis::del("promotion:user:{$userId}:talent_level");
+    }
+    
+    return $result;
+}
+```
+
+### 6.3 批量失效
+
+在系统维护或数据迁移时,可能需要批量使缓存失效:
+
+```php
+/**
+ * 批量清除推荐关系缓存
+ */
+public function batchClearReferralCaches(array $userIds): void
+{
+    foreach ($userIds as $userId) {
+        Redis::del("promotion:user:{$userId}:all_referrers");
+        Redis::del("promotion:user:{$userId}:all_members");
+        Redis::del("promotion:user:{$userId}:direct_count");
+        Redis::del("promotion:user:{$userId}:promotion_count");
+    }
+}
+```
+
+## 7. 缓存预热
+
+### 7.1 系统启动预热
+
+系统启动时,预热一些核心数据的缓存:
+
+```php
+/**
+ * 系统启动预热
+ */
+public function warmupCacheOnStartup(): void
+{
+    // 预热达人等级配置
+    $this->refreshTalentConfigCache();
+    
+    // 预热分成规则
+    $this->refreshProfitRuleCache();
+}
+```
+
+### 7.2 用户活跃度预热
+
+根据用户活跃度,预热高活跃用户的缓存:
+
+```php
+/**
+ * 预热活跃用户的缓存
+ */
+public function warmupActiveUserCache(int $days = 7): void
+{
+    // 获取活跃用户
+    $activeUsers = $this->userActivityRepository->getActiveUsers($days);
+    
+    // 预热用户的推荐关系缓存
+    foreach ($activeUsers as $user) {
+        $this->getAllReferrers($user->id);
+        $this->getAllPromotionMembers($user->id);
+    }
+}
+```
+
+### 7.3 大型团队预热
+
+预热大型团队领导者的缓存:
+
+```php
+/**
+ * 预热大型团队领导者的缓存
+ */
+public function warmupLargePromotionCache(int $minPromotionSize = 100): void
+{
+    // 获取大型团队领导者
+    $leaders = $this->talentRepository->getUsersByMinPromotionSize($minPromotionSize);
+    
+    // 预热领导者的团队缓存
+    foreach ($leaders as $leader) {
+        $this->getAllPromotionMembers($leader->user_id);
+    }
+}
+```
+
+## 8. 缓存监控
+
+### 8.1 缓存命中率监控
+
+监控缓存的命中率,评估缓存效果:
+
+```php
+/**
+ * 记录缓存命中
+ */
+private function recordCacheHit(string $cacheType): void
+{
+    Redis::incr("promotion:cache:hit:{$cacheType}");
+    Redis::incr("promotion:cache:total:{$cacheType}");
+}
+
+/**
+ * 记录缓存未命中
+ */
+private function recordCacheMiss(string $cacheType): void
+{
+    Redis::incr("promotion:cache:miss:{$cacheType}");
+    Redis::incr("promotion:cache:total:{$cacheType}");
+}
+
+/**
+ * 获取缓存命中率
+ */
+public function getCacheHitRate(string $cacheType): float
+{
+    $hit = (int)Redis::get("promotion:cache:hit:{$cacheType}") ?: 0;
+    $total = (int)Redis::get("promotion:cache:total:{$cacheType}") ?: 1;
+    
+    return $hit / $total;
+}
+```
+
+### 8.2 缓存大小监控
+
+监控缓存的大小,避免缓存过大:
+
+```php
+/**
+ * 获取缓存大小统计
+ */
+public function getCacheSize(): array
+{
+    $sizes = [];
+    
+    // 获取推荐关系缓存大小
+    $sizes['referrers'] = $this->estimateCacheSize("promotion:user:*:all_referrers");
+    $sizes['members'] = $this->estimateCacheSize("promotion:user:*:all_members");
+    
+    // 获取达人等级缓存大小
+    $sizes['talent'] = $this->estimateCacheSize("promotion:user:*:talent_level");
+    
+    // 获取配置缓存大小
+    $sizes['config'] = $this->estimateCacheSize("promotion:talent_config:*");
+    
+    return $sizes;
+}
+
+/**
+ * 估算缓存大小
+ */
+private function estimateCacheSize(string $pattern): int
+{
+    $keys = Redis::keys($pattern);
+    $size = 0;
+    
+    foreach ($keys as $key) {
+        $size += Redis::strlen($key);
+    }
+    
+    return $size;
+}
+```
+
+### 8.3 缓存异常监控
+
+监控缓存异常,及时发现问题:
+
+```php
+/**
+ * 记录缓存异常
+ */
+private function recordCacheException(string $cacheType, string $message): void
+{
+    Redis::incr("promotion:cache:exception:{$cacheType}");
+    Log::error("Cache exception: {$cacheType} - {$message}");
+}
+```
+
+## 9. 缓存优化
+
+### 9.1 缓存压缩
+
+对大型缓存数据进行压缩,减少内存占用:
+
+```php
+/**
+ * 压缩缓存数据
+ */
+private function compressCache(array $data): string
+{
+    return gzcompress(json_encode($data), 9);
+}
+
+/**
+ * 解压缓存数据
+ */
+private function decompressCache(string $data): array
+{
+    return json_decode(gzuncompress($data), true);
+}
+```
+
+### 9.2 分级缓存
+
+实现分级缓存策略,提高缓存效率:
+
+```php
+/**
+ * 获取用户的所有上级(分级缓存)
+ */
+public function getAllReferrers(int $userId): array
+{
+    // 尝试从本地缓存获取
+    $localCache = $this->getLocalCache("referrers_{$userId}");
+    if ($localCache !== null) {
+        return $localCache;
+    }
+    
+    // 尝试从Redis缓存获取
+    $cacheKey = "promotion:user:{$userId}:all_referrers";
+    $cachedData = Redis::get($cacheKey);
+    if ($cachedData !== null) {
+        $data = json_decode($cachedData, true);
+        $this->setLocalCache("referrers_{$userId}", $data);
+        return $data;
+    }
+    
+    // 计算数据
+    $allReferrers = [];
+    $this->calculateAllReferrers($userId, $allReferrers);
+    
+    // 缓存数据
+    Redis::setex($cacheKey, 86400, json_encode($allReferrers));
+    $this->setLocalCache("referrers_{$userId}", $allReferrers);
+    
+    return $allReferrers;
+}
+```
+
+### 9.3 缓存预分片
+
+对大型团队的缓存进行预分片,避免单个缓存过大:
+
+```php
+/**
+ * 获取用户的所有团队成员(预分片)
+ */
+public function getAllPromotionMembers(int $userId): array
+{
+    // 获取团队规模
+    $promotionCount = $this->getPromotionCount($userId);
+    
+    // 如果团队规模大于阈值,使用分片缓存
+    if ($promotionCount > 1000) {
+        return $this->getAllPromotionMembersSharded($userId);
+    }
+    
+    // 否则使用普通缓存
+    return $this->getAllPromotionMembersNormal($userId);
+}
+
+/**
+ * 获取用户的所有团队成员(分片版)
+ */
+private function getAllPromotionMembersSharded(int $userId): array
+{
+    $allMembers = [];
+    $shardCount = 10;
+    
+    // 获取所有分片
+    for ($i = 0; $i < $shardCount; $i++) {
+        $cacheKey = "promotion:user:{$userId}:all_members:shard:{$i}";
+        $cachedData = Redis::get($cacheKey);
+        
+        if ($cachedData !== null) {
+            $allMembers = array_merge($allMembers, json_decode($cachedData, true));
+        } else {
+            // 计算该分片的数据
+            $shardMembers = $this->calculatePromotionMembersShard($userId, $i, $shardCount);
+            Redis::setex($cacheKey, 86400, json_encode($shardMembers));
+            $allMembers = array_merge($allMembers, $shardMembers);
+        }
+    }
+    
+    return $allMembers;
+}
+```
+
+## 10. 缓存一致性
+
+### 10.1 写入确认
+
+在更新数据库后,确保缓存更新成功:
+
+```php
+/**
+ * 更新用户的推荐关系(写入确认)
+ */
+public function updateReferralRelation(int $userId, int $newReferrerId): bool
+{
+    // 更新数据库
+    $result = $this->referralRepository->updateReferrer($userId, $newReferrerId);
+    
+    if ($result) {
+        // 清除相关缓存
+        $cacheCleared = $this->clearReferralCaches($userId, $this->getOldReferrerId($userId), $newReferrerId);
+        
+        // 如果缓存清除失败,记录日志
+        if (!$cacheCleared) {
+            Log::error("Failed to clear cache for user {$userId} referral update");
+        }
+    }
+    
+    return $result;
+}
+```
+
+### 10.2 事务一致性
+
+在事务中同时更新数据库和缓存:
+
+```php
+/**
+ * 更新用户的达人等级(事务一致性)
+ */
+public function updateTalentLevel(int $userId, int $newLevel): bool
+{
+    return DB::transaction(function () use ($userId, $newLevel) {
+        // 更新数据库
+        $result = $this->talentRepository->updateLevel($userId, $newLevel);
+        
+        if ($result) {
+            // 清除相关缓存
+            Redis::del("promotion:user:{$userId}:talent_level");
+        }
+        
+        return $result;
+    });
+}
+```
+
+### 10.3 最终一致性
+
+对于不需要强一致性的数据,采用最终一致性策略:
+
+```php
+/**
+ * 更新用户的团队统计数据(最终一致性)
+ */
+public function updatePromotionStats(int $userId): void
+{
+    // 将任务加入队列,异步执行
+    dispatch(new UpdatePromotionStatsJob($userId));
+}
+
+/**
+ * 异步更新团队统计数据
+ */
+public function handleUpdatePromotionStats(int $userId): void
+{
+    // 计算直推人数
+    $directCount = $this->countDirectReferrals($userId);
+    
+    // 计算团队总人数
+    $promotionCount = $this->calculatePromotionCount($userId);
+    
+    // 更新数据库
+    $this->talentRepository->updateCounts($userId, $directCount, $promotionCount);
+    
+    // 更新缓存
+    Redis::setex("promotion:user:{$userId}:direct_count", 86400, $directCount);
+    Redis::setex("promotion:user:{$userId}:promotion_count", 86400, $promotionCount);
+}
+```
+
+## 11. 缓存安全
+
+### 11.1 数据脱敏
+
+对缓存中的敏感数据进行脱敏:
+
+```php
+/**
+ * 获取用户的团队成员(数据脱敏)
+ */
+public function getPromotionMembersWithMasking(int $userId): array
+{
+    $members = $this->getAllPromotionMembers($userId);
+    
+    // 对敏感数据进行脱敏
+    foreach ($members as &$member) {
+        if (isset($member['username'])) {
+            $member['username'] = $this->maskUsername($member['username']);
+        }
+    }
+    
+    return $members;
+}
+
+/**
+ * 对用户名进行脱敏
+ */
+private function maskUsername(string $username): string
+{
+    if (strlen($username) <= 2) {
+        return $username;
+    }
+    
+    return substr($username, 0, 1) . str_repeat('*', strlen($username) - 2) . substr($username, -1);
+}
+```
+
+### 11.2 访问控制
+
+实施缓存访问控制,防止未授权访问:
+
+```php
+/**
+ * 验证用户是否有权限访问团队数据
+ */
+private function validatePromotionAccess(int $userId, int $promotionLeaderId): bool
+{
+    // 如果是自己的团队数据,允许访问
+    if ($userId == $promotionLeaderId) {
+        return true;
+    }
+    
+    // 如果是管理员,允许访问
+    if ($this->isAdmin($userId)) {
+        return true;
+    }
+    
+    // 如果是团队成员,允许访问
+    $referrers = $this->getAllReferrers($userId);
+    foreach ($referrers as $referrer) {
+        if ($referrer['user_id'] == $promotionLeaderId) {
+            return true;
+        }
+    }
+    
+    return false;
+}
+```
+
+### 11.3 数据加密
+
+对敏感缓存数据进行加密:
+
+```php
+/**
+ * 加密缓存数据
+ */
+private function encryptCache(array $data): string
+{
+    $serialized = json_encode($data);
+    return encrypt($serialized);
+}
+
+/**
+ * 解密缓存数据
+ */
+private function decryptCache(string $data): array
+{
+    $decrypted = decrypt($data);
+    return json_decode($decrypted, true);
+}
+```
+
+## 12. 总结
+
+团队模块的缓存策略是只存储直接推荐关系 + 缓存方案的核心组成部分,通过合理的缓存设计和优化,在保持较高查询效率的同时,大幅减少了存储空间,并提高了推荐关系修改的灵活性。
+
+缓存策略的关键点包括:
+
+1. **分层缓存**:针对不同类型的数据采用不同的缓存策略
+2. **主动更新**:在数据变更时主动更新或清除相关缓存
+3. **缓存预热**:对核心数据和高频访问数据进行预热
+4. **缓存监控**:监控缓存的命中率、大小和异常情况
+5. **缓存优化**:通过压缩、分级和预分片等技术优化缓存性能
+6. **缓存一致性**:确保数据库和缓存的一致性
+7. **缓存安全**:保护缓存中的敏感数据
+
+通过这些策略,团队模块能够在大规模用户场景下保持良好的性能和可靠性,同时支持推荐关系的灵活修改。

+ 348 - 0
app/Module/Promotionurs/Docs/设计概述.md

@@ -0,0 +1,348 @@
+# 团队模块设计概述
+
+## 1. 模块定位
+
+团队模块是开心农场系统的核心模块之一,负责管理用户之间的推荐关系、团队结构、达人等级和收益分成机制。该模块通过建立用户间的直推和间推关系,形成团队结构,并在团队成员产生收益时进行分成,鼓励用户发展团队,形成良性的社交生态。
+
+## 2. 设计目标
+
+1. **建立完整的用户推荐体系**:支持直推和间推关系,形成多层级的团队结构
+2. **实现灵活的达人等级系统**:根据团队规模和活跃度评定用户的达人等级
+3. **提供公平的收益分成机制**:根据推荐关系和达人等级分配团队收益
+4. **支持多种收益来源**:支持农场收获、任务完成等多种收益来源的分成
+5. **保证系统的可扩展性**:预留达人等级、分成比例和收益来源的扩展空间
+
+## 3. 架构设计
+
+### 3.1 模块结构
+
+团队模块采用分层架构设计,包括以下层次:
+
+```
+                  ┌─────────────┐
+                  │  Controller │  控制器层:处理HTTP请求
+                  └──────┬──────┘
+                         │
+                         ▼
+┌─────────────┐   ┌─────────────┐
+│    Event    │◄──┤   Service   │  服务层:对外提供服务接口
+└─────────────┘   └──────┬──────┘
+                         │
+                         ▼
+                  ┌─────────────┐
+                  │    Logic    │  逻辑层:实现业务逻辑
+                  └──────┬──────┘
+                         │
+                         ▼
+                  ┌─────────────┐
+                  │ Repository  │  仓库层:数据访问和持久化
+                  └──────┬──────┘
+                         │
+                         ▼
+                  ┌─────────────┐
+                  │    Model    │  模型层:数据结构定义
+                  └─────────────┘
+```
+
+### 3.2 核心组件
+
+1. **Service**:对外提供服务接口,是模块的主要入口
+   - ReferralService:推荐关系服务
+   - TalentService:达人等级服务
+   - PromotionProfitService:团队收益服务
+   - ReferralCodeService:推荐码服务
+
+2. **Logic**:实现业务逻辑,处理复杂的业务规则
+   - ReferralLogic:推荐关系逻辑
+   - TalentLogic:达人等级逻辑
+   - ProfitLogic:收益分成逻辑
+
+3. **Repository**:数据访问和持久化,封装数据库操作
+   - UserReferralRepository:用户推荐关系仓库
+   - UserTalentRepository:用户达人等级仓库
+   - PromotionProfitRepository:团队收益记录仓库
+
+4. **Model**:数据结构定义,对应数据库表
+   - UserReferral:用户推荐关系模型
+   - UserTalent:用户达人等级模型
+   - PromotionProfit:团队收益记录模型
+
+5. **Event**:事件定义,用于模块间通信
+   - TalentLevelUpEvent:达人等级提升事件
+   - ReferralCreatedEvent:推荐关系创建事件
+   - PromotionProfitGeneratedEvent:团队收益生成事件
+
+### 3.3 依赖关系
+
+团队模块与其他模块的依赖关系:
+
+```
+┌─────────────┐      ┌─────────────┐      ┌─────────────┐
+│  User模块   │◄────►│  Promotion模块   │◄────►│  Farm模块   │
+└─────────────┘      └──────┬──────┘      └─────────────┘
+                            │
+                            ▼
+                     ┌─────────────┐
+                     │GameItems模块│
+                     └─────────────┘
+```
+
+- **User模块**:提供用户基本信息,接收推荐关系
+- **Farm模块**:提供收获事件,接收团队收益分成
+- **GameItems模块**:提供物品操作接口,处理收益物品的添加
+
+## 4. 核心流程
+
+### 4.1 推荐关系建立流程
+
+```
+┌─────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
+│  用户   │     │ UserService │     │ReferralService│    │ReferralLogic│
+└────┬────┘     └──────┬──────┘     └───────┬─────┘     └───────┬─────┘
+     │                 │                    │                   │
+     │ 注册(推荐码)     │                    │                   │
+     │────────────────►│                    │                   │
+     │                 │                    │                   │
+     │                 │ 验证推荐码          │                   │
+     │                 │───────────────────►│                   │
+     │                 │                    │                   │
+     │                 │                    │ 解析推荐人ID       │
+     │                 │                    │───────────────────►
+     │                 │                    │                   │
+     │                 │                    │                   │
+     │                 │                    │◄───────────────────
+     │                 │                    │                   │
+     │                 │                    │ 建立推荐关系       │
+     │                 │                    │───────────────────►
+     │                 │                    │                   │
+     │                 │                    │                   │
+     │                 │                    │◄───────────────────
+     │                 │                    │                   │
+     │                 │◄───────────────────│                   │
+     │                 │                    │                   │
+     │◄────────────────│                    │                   │
+     │                 │                    │                   │
+```
+
+### 4.2 达人等级升级流程
+
+```
+┌─────────────┐     ┌─────────────┐     ┌─────────────┐
+│ReferralService│    │TalentService │    │ TalentLogic │
+└───────┬─────┘     └───────┬─────┘     └───────┬─────┘
+        │                   │                   │
+        │ 建立推荐关系       │                   │
+        │───────────────────┼───────────────────┼────►
+        │                   │                   │
+        │ 触发检查达人等级   │                   │
+        │───────────────────►                   │
+        │                   │                   │
+        │                   │ 获取团队规模       │
+        │                   │───────────────────►
+        │                   │                   │
+        │                   │                   │
+        │                   │◄───────────────────
+        │                   │                   │
+        │                   │ 计算新达人等级     │
+        │                   │───────────────────►
+        │                   │                   │
+        │                   │                   │
+        │                   │◄───────────────────
+        │                   │                   │
+        │                   │ 更新达人等级       │
+        │                   │───────────────────►
+        │                   │                   │
+        │                   │                   │
+        │                   │◄───────────────────
+        │                   │                   │
+        │                   │ 触发等级提升事件   │
+        │                   │───────────────────►
+        │                   │                   │
+```
+
+### 4.3 团队收益分成流程
+
+```
+┌─────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
+│Farm模块  │     │PromotionProfitService│  │ ProfitLogic │    │GameItems模块│
+└────┬────┘     └───────┬─────┘     └───────┬─────┘     └───────┬─────┘
+     │                  │                   │                   │
+     │ 收获事件          │                   │                   │
+     │─────────────────►│                   │                   │
+     │                  │                   │                   │
+     │                  │ 获取推荐关系       │                   │
+     │                  │───────────────────►                   │
+     │                  │                   │                   │
+     │                  │                   │                   │
+     │                  │◄───────────────────                   │
+     │                  │                   │                   │
+     │                  │ 计算分成比例       │                   │
+     │                  │───────────────────►                   │
+     │                  │                   │                   │
+     │                  │                   │                   │
+     │                  │◄───────────────────                   │
+     │                  │                   │                   │
+     │                  │ 记录分成收益       │                   │
+     │                  │───────────────────►                   │
+     │                  │                   │                   │
+     │                  │                   │                   │
+     │                  │◄───────────────────                   │
+     │                  │                   │                   │
+     │                  │ 添加收益物品       │                   │
+     │                  │───────────────────┼───────────────────►
+     │                  │                   │                   │
+     │                  │                   │                   │
+     │                  │◄───────────────────┼───────────────────
+     │                  │                   │                   │
+```
+
+## 5. 关键设计点
+
+### 5.1 推荐关系存储
+
+团队模块采用扁平化存储推荐关系,即直接存储用户与其所有上级(直推和间推)的关系,而不是仅存储直接推荐关系。这种设计有以下优点:
+
+1. **查询效率高**:查询用户的所有上级或下级只需一次数据库查询
+2. **计算负担小**:不需要在运行时递归计算多级关系
+3. **维护成本低**:新增用户只需添加与其所有上级的关系记录
+
+示例数据结构:
+```
+user_id | referrer_id | level
+--------|------------|------
+   2    |     1      |   1    # 用户1直推了用户2
+   3    |     2      |   1    # 用户2直推了用户3
+   3    |     1      |   2    # 用户1间推了用户3
+```
+
+### 5.2 达人等级计算
+
+达人等级计算采用定期更新和事件触发相结合的方式:
+
+1. **事件触发更新**:当用户团队规模变化时(如新增团队成员),触发达人等级检查
+2. **定期批量更新**:定时任务定期检查和更新所有用户的达人等级
+3. **缓存优化**:缓存用户的达人等级信息,减少频繁查询
+
+### 5.3 收益分成策略
+
+收益分成采用灵活的策略模式设计:
+
+1. **来源适配器**:为不同的收益来源(如农场收获、任务完成)提供适配器
+2. **分成规则引擎**:根据推荐关系和达人等级计算分成比例
+3. **分成上限控制**:设置分成总比例上限,确保系统可持续运营
+
+### 5.4 事件驱动通信
+
+模块间采用事件驱动的松耦合通信方式:
+
+1. **发布-订阅模式**:模块发布事件,其他模块订阅并处理
+2. **异步处理**:收益分成等耗时操作采用异步处理
+3. **失败重试**:关键操作失败后支持重试机制
+
+## 6. 扩展性设计
+
+### 6.1 达人等级扩展
+
+达人等级系统设计为可扩展的:
+
+1. **配置化等级**:达人等级条件和权益通过配置表定义
+2. **动态权益**:达人权益可动态配置,支持不同类型的权益
+3. **等级评定规则**:支持扩展等级评定规则,如增加活跃度指标
+
+### 6.2 收益来源扩展
+
+收益分成系统支持多种收益来源:
+
+1. **来源注册机制**:新的收益来源可通过注册机制接入
+2. **统一分成接口**:提供统一的分成计算和处理接口
+3. **来源特定规则**:支持为不同来源配置特定的分成规则
+
+### 6.3 分成规则扩展
+
+分成规则设计为可扩展的:
+
+1. **规则引擎**:使用规则引擎处理复杂的分成逻辑
+2. **规则配置**:分成比例和条件通过配置表定义
+3. **规则版本**:支持规则版本管理,便于规则调整和升级
+
+## 7. 安全性设计
+
+### 7.1 数据验证
+
+所有输入数据进行严格验证:
+
+1. **用户身份验证**:验证操作用户的身份和权限
+2. **数据完整性验证**:验证输入数据的完整性和有效性
+3. **业务规则验证**:验证操作是否符合业务规则
+
+### 7.2 防刷机制
+
+防止恶意刷取团队收益:
+
+1. **操作频率限制**:限制用户操作频率
+2. **异常行为检测**:检测和阻止异常的团队行为
+3. **收益上限控制**:设置单用户收益上限
+
+### 7.3 数据隔离
+
+确保数据安全和隔离:
+
+1. **用户数据隔离**:用户只能访问自己的团队数据
+2. **权限分级**:不同角色拥有不同的数据访问权限
+3. **敏感数据保护**:加密存储敏感数据
+
+## 8. 性能优化
+
+### 8.1 数据库优化
+
+优化数据库设计和查询:
+
+1. **索引设计**:为常用查询字段建立合适的索引
+2. **分表策略**:大表采用分表策略,提高查询效率
+3. **批量操作**:使用批量插入和更新,减少数据库操作次数
+
+### 8.2 缓存策略
+
+合理使用缓存提高性能:
+
+1. **用户团队缓存**:缓存用户的团队结构和达人信息
+2. **分成规则缓存**:缓存分成规则和比例
+3. **统计数据缓存**:缓存团队规模等统计数据
+
+### 8.3 异步处理
+
+耗时操作采用异步处理:
+
+1. **队列处理**:收益分成等操作通过队列异步处理
+2. **批量处理**:定期批量处理团队统计和达人升级
+3. **任务调度**:使用任务调度系统管理异步任务
+
+## 9. 与其他模块的交互
+
+### 9.1 与User模块的交互
+
+- **用户注册**:接收用户注册事件,处理推荐关系
+- **用户信息**:获取用户基本信息,如昵称、头像等
+- **用户状态**:监听用户状态变更,如封禁、注销等
+
+### 9.2 与Farm模块的交互
+
+- **收获事件**:监听作物收获事件,计算农场收益分成
+- **达人权益**:提供达人等级信息,影响农场产出
+- **团队任务**:支持团队协作完成农场任务
+
+### 9.3 与GameItems模块的交互
+
+- **物品操作**:调用物品模块接口,添加收益物品
+- **物品记录**:记录收益物品的来源和分成信息
+- **物品消耗**:支持使用物品激活团队特权
+
+### 9.4 与Task模块的交互
+
+- **任务完成**:触发团队相关任务的完成
+- **任务奖励**:计算团队任务奖励的分成
+- **团队目标**:支持设置和完成团队目标
+
+## 10. 总结
+
+团队模块作为开心农场系统的核心模块之一,通过建立用户间的推荐关系、团队结构、达人等级和收益分成机制,鼓励用户发展团队,形成良性的社交生态。模块设计注重灵活性、可扩展性和性能,采用分层架构和事件驱动通信,与其他模块保持松耦合关系。通过合理的数据结构设计和优化策略,确保系统在大规模用户场景下的稳定运行。

+ 27 - 0
app/Module/Promotionurs/Enums/PROFIT_RECORD_STATUS.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Module\Promotion\Enums;
+
+/**
+ * 收益记录状态枚举
+ * 定义了团队收益记录的状态
+ */
+enum PROFIT_RECORD_STATUS: int {
+    /**
+     * 待处理
+     * 收益记录已创建但尚未处理
+     */
+    case PENDING = 0;
+
+    /**
+     * 成功
+     * 收益已成功分配到用户账户
+     */
+    case SUCCESS = 1;
+
+    /**
+     * 失败
+     * 收益分配失败,需要重试或人工处理
+     */
+    case FAILED = 2;
+}

+ 27 - 0
app/Module/Promotionurs/Enums/PROFIT_SOURCE_TYPE.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Module\Promotion\Enums;
+
+/**
+ * 收益来源类型枚举
+ * 定义了团队收益的来源
+ */
+enum PROFIT_SOURCE_TYPE: string {
+    /**
+     * 农场收获
+     * 来自Farm模块的作物收获
+     */
+    case FARM_HARVEST = 'farm_harvest';
+
+    /**
+     * 任务完成
+     * 来自Task模块的任务奖励
+     */
+    case TASK_COMPLETE = 'task_complete';
+
+    /**
+     * 物品出售
+     * 来自GameItems模块的物品出售
+     */
+    case ITEM_SELL = 'item_sell';
+}

+ 27 - 0
app/Module/Promotionurs/Enums/REFERRAL_CODE_STATUS.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Module\Promotion\Enums;
+
+/**
+ * 推荐码状态枚举
+ * 定义了推荐码的状态
+ */
+enum REFERRAL_CODE_STATUS: int {
+    /**
+     * 未激活
+     * 推荐码已生成但尚未激活
+     */
+    case INACTIVE = 0;
+
+    /**
+     * 有效
+     * 推荐码有效,可以使用
+     */
+    case ACTIVE = 1;
+
+    /**
+     * 已禁用
+     * 推荐码已被禁用,无法使用
+     */
+    case DISABLED = 2;
+}

+ 21 - 0
app/Module/Promotionurs/Enums/REFERRAL_LEVEL.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Module\Promotion\Enums;
+
+/**
+ * 推荐层级枚举
+ * 定义了用户之间的推荐关系类型
+ */
+enum REFERRAL_LEVEL: int {
+    /**
+     * 直推关系
+     * 用户A直接推荐用户B
+     */
+    case DIRECT = 1;
+
+    /**
+     * 间推关系
+     * 用户A通过中间用户间接推荐用户C
+     */
+    case INDIRECT = 2;
+}

+ 45 - 0
app/Module/Promotionurs/Enums/TALENT_LEVEL.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace App\Module\Promotion\Enums;
+
+/**
+ * 达人等级枚举
+ * 定义了用户的达人等级
+ */
+enum TALENT_LEVEL: int {
+    /**
+     * 非达人
+     * 默认等级
+     */
+    case NONE = 0;
+
+    /**
+     * 初级达人
+     * 直推5人且团队总人数≥10
+     */
+    case JUNIOR = 1;
+
+    /**
+     * 中级达人
+     * 直推10人且团队总人数≥30
+     */
+    case INTERMEDIATE = 2;
+
+    /**
+     * 高级达人
+     * 直推20人且团队总人数≥50
+     */
+    case SENIOR = 3;
+
+    /**
+     * 资深达人
+     * 直推30人且团队总人数≥100
+     */
+    case EXPERT = 4;
+
+    /**
+     * 顶级达人
+     * 直推50人且团队总人数≥200
+     */
+    case MASTER = 5;
+}

+ 33 - 0
app/Module/Promotionurs/Enums/TEAM_TASK_STATUS.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Module\Promotion\Enums;
+
+/**
+ * 团队任务状态枚举
+ * 定义了团队任务的状态
+ */
+enum TEAM_TASK_STATUS: int {
+    /**
+     * 进行中
+     * 任务正在进行中
+     */
+    case ACTIVE = 1;
+
+    /**
+     * 已完成
+     * 任务已完成并发放奖励
+     */
+    case COMPLETED = 2;
+
+    /**
+     * 已过期
+     * 任务未完成但已过期
+     */
+    case EXPIRED = 3;
+
+    /**
+     * 已取消
+     * 任务被创建者或管理员取消
+     */
+    case CANCELLED = 4;
+}

+ 33 - 0
app/Module/Promotionurs/Enums/TEAM_TASK_TYPE.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Module\Promotion\Enums;
+
+/**
+ * 团队任务类型枚举
+ * 定义了团队可以执行的任务类型
+ */
+enum TEAM_TASK_TYPE: string {
+    /**
+     * 种植任务
+     * 团队成员种植特定作物
+     */
+    case PLANTING = 'planting';
+
+    /**
+     * 收获任务
+     * 团队成员收获特定作物
+     */
+    case HARVESTING = 'harvesting';
+
+    /**
+     * 推荐任务
+     * 团队成员发展新成员
+     */
+    case REFERRAL = 'referral';
+
+    /**
+     * 活跃度任务
+     * 团队成员保持活跃
+     */
+    case ACTIVITY = 'activity';
+}

+ 40 - 0
app/Module/Promotionurs/Events/ReferralCreatedEvent.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Module\Promotion\Events;
+
+use App\Module\Promotion\Models\PromotionUserReferral;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+/**
+ * 推荐关系创建事件
+ *
+ * 当用户的推荐关系被创建时触发此事件。
+ * 监听此事件可以执行如下操作:
+ * 1. 更新用户的团队统计数据
+ * 2. 更新用户的达人等级
+ * 3. 发放邀请奖励
+ */
+class ReferralCreatedEvent
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    /**
+     * 推荐关系模型
+     *
+     * @var PromotionUserReferral
+     */
+    public $referral;
+
+    /**
+     * 创建一个新的事件实例
+     *
+     * @param PromotionUserReferral $referral 推荐关系模型
+     * @return void
+     */
+    public function __construct(PromotionUserReferral $referral)
+    {
+        $this->referral = $referral;
+    }
+}

+ 50 - 0
app/Module/Promotionurs/Events/ReferralUpdatedEvent.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Module\Promotion\Events;
+
+use App\Module\Promotion\Models\PromotionReferralChange;
+use App\Module\Promotion\Models\PromotionUserReferral;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+/**
+ * 推荐关系更新事件
+ *
+ * 当用户的推荐关系被更新时触发此事件。
+ * 监听此事件可以执行如下操作:
+ * 1. 更新用户的团队统计数据
+ * 2. 更新用户的达人等级
+ * 3. 更新用户的关系缓存
+ */
+class ReferralUpdatedEvent
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    /**
+     * 推荐关系模型
+     *
+     * @var PromotionUserReferral
+     */
+    public $referral;
+
+    /**
+     * 推荐关系修改记录
+     *
+     * @var PromotionReferralChange
+     */
+    public $change;
+
+    /**
+     * 创建一个新的事件实例
+     *
+     * @param PromotionUserReferral $referral 推荐关系模型
+     * @param PromotionReferralChange $change 推荐关系修改记录
+     * @return void
+     */
+    public function __construct(PromotionUserReferral $referral, PromotionReferralChange $change)
+    {
+        $this->referral = $referral;
+        $this->change = $change;
+    }
+}

+ 58 - 0
app/Module/Promotionurs/Events/TalentLevelChangedEvent.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace App\Module\Promotion\Events;
+
+use App\Module\Promotion\Models\PromotionUserTalent;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+/**
+ * 达人等级变更事件
+ *
+ * 当用户的达人等级发生变化时触发此事件。
+ * 监听此事件可以执行如下操作:
+ * 1. 更新用户的权益
+ * 2. 发送通知
+ * 3. 记录等级变更日志
+ */
+class TalentLevelChangedEvent
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    /**
+     * 达人等级模型
+     *
+     * @var PromotionUserTalent
+     */
+    public $talent;
+
+    /**
+     * 旧的达人等级
+     *
+     * @var int
+     */
+    public $oldLevel;
+
+    /**
+     * 新的达人等级
+     *
+     * @var int
+     */
+    public $newLevel;
+
+    /**
+     * 创建一个新的事件实例
+     *
+     * @param PromotionUserTalent $talent 达人等级模型
+     * @param int $oldLevel 旧的达人等级
+     * @param int $newLevel 新的达人等级
+     * @return void
+     */
+    public function __construct(PromotionUserTalent $talent, int $oldLevel, int $newLevel)
+    {
+        $this->talent = $talent;
+        $this->oldLevel = $oldLevel;
+        $this->newLevel = $newLevel;
+    }
+}

+ 40 - 0
app/Module/Promotionurs/Events/TeamProfitCreatedEvent.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Module\Promotion\Events;
+
+use App\Module\Promotion\Models\PromotionProfit;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+/**
+ * 团队收益创建事件
+ *
+ * 当团队收益记录被创建时触发此事件。
+ * 监听此事件可以执行如下操作:
+ * 1. 发送通知
+ * 2. 记录收益日志
+ * 3. 更新用户的收益统计
+ */
+class PromotionProfitCreatedEvent
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    /**
+     * 团队收益模型
+     *
+     * @var PromotionProfit
+     */
+    public $profit;
+
+    /**
+     * 创建一个新的事件实例
+     *
+     * @param PromotionProfit $profit 团队收益模型
+     * @return void
+     */
+    public function __construct(PromotionProfit $profit)
+    {
+        $this->profit = $profit;
+    }
+}

+ 101 - 0
app/Module/Promotionurs/Listeners/DistributeTeamProfitListener.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace App\Module\Promotion\Listeners;
+
+use App\Module\Promotion\Events\PromotionProfitCreatedEvent;
+use App\Module\Promotion\Logics\ReferralLogic;
+use App\Module\Promotion\Logics\TalentLogic;
+use App\Module\Promotion\Logics\PromotionProfitLogic;
+
+
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 分配团队收益监听器
+ *
+ * 监听团队收益创建事件,分配团队收益。
+ * 根据用户的推荐关系和达人等级,计算并分配团队收益。
+ */
+class DistributePromotionProfitListener
+{
+
+
+    /**
+     * 推荐关系逻辑类
+     *
+     * @var ReferralLogic
+     */
+    protected $referralLogic;
+
+    /**
+     * 达人等级逻辑类
+     *
+     * @var TalentLogic
+     */
+    protected $talentLogic;
+
+    /**
+     * 团队收益逻辑类
+     *
+     * @var PromotionProfitLogic
+     */
+    protected $promotionProfitLogic;
+
+    /**
+     * 创建监听器实例
+     *
+     * @param ReferralLogic $referralLogic 推荐关系逻辑类
+     * @param TalentLogic $talentLogic 达人等级逻辑类
+     * @param PromotionProfitLogic $promotionProfitLogic 团队收益逻辑类
+     * @return void
+     */
+    public function __construct(
+        ReferralLogic $referralLogic,
+        TalentLogic $talentLogic,
+        PromotionProfitLogic $promotionProfitLogic
+    ) {
+        $this->referralLogic = $referralLogic;
+        $this->talentLogic = $talentLogic;
+        $this->promotionProfitLogic = $promotionProfitLogic;
+    }
+
+    /**
+     * 处理团队收益创建事件
+     *
+     * @param PromotionProfitCreatedEvent $event 团队收益创建事件
+     * @return void
+     */
+    public function handle(PromotionProfitCreatedEvent $event)
+    {
+        try {
+            $profit = $event->profit;
+            $userId = $profit->from_user_id;
+            $sourceType = $profit->source_type;
+            $sourceId = $profit->source_id;
+            $itemId = $profit->item_id;
+            $amount = $profit->amount;
+
+            // 分配团队收益
+            $this->promotionProfitLogic->distributePromotionProfit(
+                $userId,
+                $sourceType,
+                $sourceId,
+                $itemId,
+                $amount
+            );
+
+            Log::info("团队收益分配成功", [
+                'user_id' => $userId,
+                'source_type' => $sourceType,
+                'source_id' => $sourceId,
+                'item_id' => $itemId,
+                'amount' => $amount
+            ]);
+        } catch (\Exception $e) {
+            Log::error("团队收益分配失败", [
+                'error' => $e->getMessage(),
+                'event' => 'PromotionProfitCreated'
+            ]);
+        }
+    }
+}

+ 131 - 0
app/Module/Promotionurs/Listeners/UpdateTalentLevelListener.php

@@ -0,0 +1,131 @@
+<?php
+
+namespace App\Module\Promotion\Listeners;
+
+use App\Module\Promotion\Events\ReferralCreatedEvent;
+use App\Module\Promotion\Events\ReferralUpdatedEvent;
+use App\Module\Promotion\Logics\ReferralLogic;
+use App\Module\Promotion\Logics\TalentLogic;
+
+
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 更新达人等级监听器
+ *
+ * 监听推荐关系创建和更新事件,更新用户的达人等级。
+ * 根据用户的直推人数和团队总人数,计算用户的达人等级。
+ */
+class UpdateTalentLevelListener
+{
+
+
+    /**
+     * 推荐关系逻辑类
+     *
+     * @var ReferralLogic
+     */
+    protected $referralLogic;
+
+    /**
+     * 达人等级逻辑类
+     *
+     * @var TalentLogic
+     */
+    protected $talentLogic;
+
+    /**
+     * 创建监听器实例
+     *
+     * @param ReferralLogic $referralLogic 推荐关系逻辑类
+     * @param TalentLogic $talentLogic 达人等级逻辑类
+     * @return void
+     */
+    public function __construct(ReferralLogic $referralLogic, TalentLogic $talentLogic)
+    {
+        $this->referralLogic = $referralLogic;
+        $this->talentLogic = $talentLogic;
+    }
+
+    /**
+     * 处理推荐关系创建事件
+     *
+     * @param ReferralCreatedEvent $event 推荐关系创建事件
+     * @return void
+     */
+    public function handleReferralCreated(ReferralCreatedEvent $event)
+    {
+        try {
+            $referral = $event->referral;
+            $referrerId = $referral->referrer_id;
+
+            // 更新推荐人的达人等级
+            $this->talentLogic->checkAndUpdateTalentLevel($referrerId);
+
+            Log::info("达人等级更新成功", [
+                'referrer_id' => $referrerId,
+                'event' => 'ReferralCreated'
+            ]);
+        } catch (\Exception $e) {
+            Log::error("达人等级更新失败", [
+                'error' => $e->getMessage(),
+                'event' => 'ReferralCreated'
+            ]);
+        }
+    }
+
+    /**
+     * 处理推荐关系更新事件
+     *
+     * @param ReferralUpdatedEvent $event 推荐关系更新事件
+     * @return void
+     */
+    public function handleReferralUpdated(ReferralUpdatedEvent $event)
+    {
+        try {
+            $change = $event->change;
+            $oldReferrerId = $change->old_referrer_id;
+            $newReferrerId = $change->new_referrer_id;
+
+            // 更新旧推荐人的达人等级
+            if ($oldReferrerId) {
+                $this->talentLogic->checkAndUpdateTalentLevel($oldReferrerId);
+            }
+
+            // 更新新推荐人的达人等级
+            if ($newReferrerId) {
+                $this->talentLogic->checkAndUpdateTalentLevel($newReferrerId);
+            }
+
+            Log::info("达人等级更新成功", [
+                'old_referrer_id' => $oldReferrerId,
+                'new_referrer_id' => $newReferrerId,
+                'event' => 'ReferralUpdated'
+            ]);
+        } catch (\Exception $e) {
+            Log::error("达人等级更新失败", [
+                'error' => $e->getMessage(),
+                'event' => 'ReferralUpdated'
+            ]);
+        }
+    }
+
+    /**
+     * 注册监听器的订阅者
+     *
+     * @param \Illuminate\Events\Dispatcher $events
+     * @return void
+     */
+    public function subscribe($events)
+    {
+        $events->listen(
+            ReferralCreatedEvent::class,
+            [UpdateTalentLevelListener::class, 'handleReferralCreated']
+        );
+
+        $events->listen(
+            ReferralUpdatedEvent::class,
+            [UpdateTalentLevelListener::class, 'handleReferralUpdated']
+        );
+    }
+}

+ 155 - 0
app/Module/Promotionurs/Listeners/UpdateTeamCountsListener.php

@@ -0,0 +1,155 @@
+<?php
+
+namespace App\Module\Promotion\Listeners;
+
+use App\Module\Promotion\Events\ReferralCreatedEvent;
+use App\Module\Promotion\Events\ReferralUpdatedEvent;
+use App\Module\Promotion\Logics\ReferralLogic;
+use App\Module\Promotion\Logics\RelationCacheLogic;
+
+
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 更新团队统计数据监听器
+ *
+ * 监听推荐关系创建和更新事件,更新团队统计数据。
+ * 包括更新直推人数和团队总人数。
+ */
+class UpdatePromotionCountsListener
+{
+
+
+    /**
+     * 推荐关系逻辑类
+     *
+     * @var ReferralLogic
+     */
+    protected $referralLogic;
+
+    /**
+     * 关系缓存逻辑类
+     *
+     * @var RelationCacheLogic
+     */
+    protected $relationCacheLogic;
+
+    /**
+     * 创建监听器实例
+     *
+     * @param ReferralLogic $referralLogic 推荐关系逻辑类
+     * @param RelationCacheLogic $relationCacheLogic 关系缓存逻辑类
+     * @return void
+     */
+    public function __construct(ReferralLogic $referralLogic, RelationCacheLogic $relationCacheLogic)
+    {
+        $this->referralLogic = $referralLogic;
+        $this->relationCacheLogic = $relationCacheLogic;
+    }
+
+    /**
+     * 处理推荐关系创建事件
+     *
+     * @param ReferralCreatedEvent $event 推荐关系创建事件
+     * @return void
+     */
+    public function handleReferralCreated(ReferralCreatedEvent $event)
+    {
+        try {
+            $referral = $event->referral;
+            $userId = $referral->user_id;
+            $referrerId = $referral->referrer_id;
+
+            // 更新直推人数
+            $this->referralLogic->incrementDirectCount($referrerId);
+
+            // 更新关系缓存
+            $this->relationCacheLogic->buildUserRelationCache($userId);
+
+            // 更新团队总人数
+            $this->referralLogic->updatePromotionCounts($referrerId);
+
+            Log::info("团队统计数据更新成功", [
+                'user_id' => $userId,
+                'referrer_id' => $referrerId,
+                'event' => 'ReferralCreated'
+            ]);
+        } catch (\Exception $e) {
+            Log::error("团队统计数据更新失败", [
+                'error' => $e->getMessage(),
+                'event' => 'ReferralCreated'
+            ]);
+        }
+    }
+
+    /**
+     * 处理推荐关系更新事件
+     *
+     * @param ReferralUpdatedEvent $event 推荐关系更新事件
+     * @return void
+     */
+    public function handleReferralUpdated(ReferralUpdatedEvent $event)
+    {
+        try {
+            $referral = $event->referral;
+            $change = $event->change;
+            $userId = $referral->user_id;
+            $oldReferrerId = $change->old_referrer_id;
+            $newReferrerId = $change->new_referrer_id;
+
+            // 减少旧推荐人的直推人数
+            if ($oldReferrerId) {
+                $this->referralLogic->decrementDirectCount($oldReferrerId);
+            }
+
+            // 增加新推荐人的直推人数
+            if ($newReferrerId) {
+                $this->referralLogic->incrementDirectCount($newReferrerId);
+            }
+
+            // 更新关系缓存
+            $this->relationCacheLogic->rebuildUserRelationCache($userId);
+
+            // 更新旧推荐人的团队总人数
+            if ($oldReferrerId) {
+                $this->referralLogic->updatePromotionCounts($oldReferrerId);
+            }
+
+            // 更新新推荐人的团队总人数
+            if ($newReferrerId) {
+                $this->referralLogic->updatePromotionCounts($newReferrerId);
+            }
+
+            Log::info("团队统计数据更新成功", [
+                'user_id' => $userId,
+                'old_referrer_id' => $oldReferrerId,
+                'new_referrer_id' => $newReferrerId,
+                'event' => 'ReferralUpdated'
+            ]);
+        } catch (\Exception $e) {
+            Log::error("团队统计数据更新失败", [
+                'error' => $e->getMessage(),
+                'event' => 'ReferralUpdated'
+            ]);
+        }
+    }
+
+    /**
+     * 注册监听器的订阅者
+     *
+     * @param \Illuminate\Events\Dispatcher $events
+     * @return void
+     */
+    public function subscribe($events)
+    {
+        $events->listen(
+            ReferralCreatedEvent::class,
+            [UpdatePromotionCountsListener::class, 'handleReferralCreated']
+        );
+
+        $events->listen(
+            ReferralUpdatedEvent::class,
+            [UpdatePromotionCountsListener::class, 'handleReferralUpdated']
+        );
+    }
+}

+ 309 - 0
app/Module/Promotionurs/Logics/ReferralCodeLogic.php

@@ -0,0 +1,309 @@
+<?php
+
+namespace App\Module\Promotion\Logics;
+
+use App\Module\Promotion\Enums\REFERRAL_CODE_STATUS;
+use App\Module\Promotion\Models\PromotionReferralCode;
+use App\Module\Promotion\Models\PromotionReferralCodeUsage;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Str;
+
+/**
+ * 推荐码逻辑类
+ *
+ * 处理推荐码的核心业务逻辑,包括生成推荐码、验证推荐码、
+ * 使用推荐码等功能。该类仅供内部使用,不对外提供服务。
+ */
+class ReferralCodeLogic
+{
+    /**
+     * @var ReferralLogic
+     */
+    protected $referralLogic;
+
+    /**
+     * 构造函数
+     *
+     * @param ReferralLogic $referralLogic
+     */
+    public function __construct(ReferralLogic $referralLogic)
+    {
+        $this->referralLogic = $referralLogic;
+    }
+
+    /**
+     * 生成推荐码
+     *
+     * @param int $userId 用户ID
+     * @param \DateTime|null $expireTime 过期时间
+     * @return string|null
+     */
+    public function generateReferralCode(int $userId, ?\DateTime $expireTime = null): ?string
+    {
+        try {
+            // 检查用户是否已有有效的推荐码
+            $existingCode = $this->getUserActiveCode($userId);
+
+            if ($existingCode) {
+                return $existingCode->code;
+            }
+
+            // 生成唯一的推荐码
+            $code = $this->generateUniqueCode();
+
+            // 创建推荐码记录
+            $referralCode = new PromotionReferralCode();
+            $referralCode->user_id = $userId;
+            $referralCode->code = $code;
+            $referralCode->usage_count = 0;
+            $referralCode->status = REFERRAL_CODE_STATUS::ACTIVE;
+            $referralCode->expire_time = $expireTime;
+
+            if ($referralCode->save()) {
+                return $code;
+            }
+
+            return null;
+        } catch (\Exception $e) {
+            Log::error("生成推荐码失败: " . $e->getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 生成唯一的推荐码
+     *
+     * @param int $length 推荐码长度
+     * @return string
+     */
+    private function generateUniqueCode(int $length = 8): string
+    {
+        $attempts = 0;
+        $maxAttempts = 10;
+
+        do {
+            // 生成随机字符串
+            $code = strtoupper(Str::random($length));
+
+            // 检查是否已存在
+            $exists = PromotionReferralCode::where('code', $code)->exists();
+
+            $attempts++;
+        } while ($exists && $attempts < $maxAttempts);
+
+        // 如果多次尝试后仍然存在冲突,添加时间戳确保唯一性
+        if ($exists) {
+            $code = strtoupper(substr(Str::random($length - 4), 0, $length - 4) . substr(time(), -4));
+        }
+
+        return $code;
+    }
+
+    /**
+     * 获取用户的有效推荐码
+     *
+     * @param int $userId 用户ID
+     * @return PromotionReferralCode|null
+     */
+    public function getUserActiveCode(int $userId): ?PromotionReferralCode
+    {
+        $code = PromotionReferralCode::where('user_id', $userId)
+            ->where('status', REFERRAL_CODE_STATUS::ACTIVE)
+            ->where(function ($query) {
+                $query->whereNull('expire_time')
+                    ->orWhere('expire_time', '>', now());
+            })
+            ->first();
+
+        return $code;
+    }
+
+    /**
+     * 验证推荐码
+     *
+     * @param string $code 推荐码
+     * @return array 包含验证结果和推荐码信息
+     */
+    public function validateReferralCode(string $code): array
+    {
+        try {
+            // 查找推荐码
+            $referralCode = PromotionReferralCode::where('code', $code)->first();
+
+            if (!$referralCode) {
+                return [
+                    'valid' => false,
+                    'message' => '推荐码不存在',
+                    'code' => null
+                ];
+            }
+
+            // 检查状态
+            if ($referralCode->status != REFERRAL_CODE_STATUS::ACTIVE) {
+                return [
+                    'valid' => false,
+                    'message' => '推荐码已禁用',
+                    'code' => $referralCode
+                ];
+            }
+
+            // 检查过期时间
+            if ($referralCode->expire_time && $referralCode->expire_time->isPast()) {
+                return [
+                    'valid' => false,
+                    'message' => '推荐码已过期',
+                    'code' => $referralCode
+                ];
+            }
+
+            return [
+                'valid' => true,
+                'message' => '推荐码有效',
+                'code' => $referralCode
+            ];
+        } catch (\Exception $e) {
+            Log::error("验证推荐码失败: " . $e->getMessage());
+            return [
+                'valid' => false,
+                'message' => '验证推荐码时发生错误',
+                'code' => null
+            ];
+        }
+    }
+
+    /**
+     * 使用推荐码
+     *
+     * @param string $code 推荐码
+     * @param int $userId 使用者ID
+     * @param string $ipAddress IP地址
+     * @param string $userAgent 用户代理
+     * @return array 包含使用结果和信息
+     */
+    public function useReferralCode(string $code, int $userId, string $ipAddress, string $userAgent): array
+    {
+        try {
+            // 验证推荐码
+            $validation = $this->validateReferralCode($code);
+
+            if (!$validation['valid']) {
+                return [
+                    'success' => false,
+                    'message' => $validation['message']
+                ];
+            }
+
+            $referralCode = $validation['code'];
+
+            // 检查用户是否使用自己的推荐码
+            if ($referralCode->user_id == $userId) {
+                return [
+                    'success' => false,
+                    'message' => '不能使用自己的推荐码'
+                ];
+            }
+
+            // 检查用户是否已有推荐人
+            if ($this->referralLogic->hasReferrer($userId)) {
+                return [
+                    'success' => false,
+                    'message' => '用户已有推荐人,不能使用推荐码'
+                ];
+            }
+
+            // 检查是否形成循环推荐
+            if ($this->referralLogic->checkCircularReferral($userId, $referralCode->user_id)) {
+                return [
+                    'success' => false,
+                    'message' => '使用此推荐码会形成循环推荐关系'
+                ];
+            }
+
+            // 检查事务是否已开启
+            \UCore\Db\Helper::check_tr();
+
+            // 记录使用记录
+            $usage = new PromotionReferralCodeUsage();
+            $usage->code = $code;
+            $usage->code_owner_id = $referralCode->user_id;
+            $usage->user_id = $userId;
+            $usage->ip_address = $ipAddress;
+            $usage->user_agent = $userAgent;
+            $usage->status = PromotionReferralCodeUsage::STATUS_SUCCESS;
+            $usage->result = '成功使用推荐码';
+            $usage->save();
+
+            // 更新推荐码使用次数
+            $referralCode->usage_count += 1;
+            $referralCode->save();
+
+            // 建立推荐关系
+            $result = $this->referralLogic->createReferralRelation($userId, $referralCode->user_id);
+
+            if (!$result) {
+                // 如果建立推荐关系失败,更新使用记录状态
+                $usage->status = PromotionReferralCodeUsage::STATUS_FAILED;
+                $usage->result = '建立推荐关系失败';
+                $usage->save();
+
+                return [
+                    'success' => false,
+                    'message' => '建立推荐关系失败'
+                ];
+            }
+
+            return [
+                'success' => true,
+                'message' => '成功使用推荐码并建立推荐关系',
+                'referrer_id' => $referralCode->user_id
+            ];
+        } catch (\Exception $e) {
+            Log::error("使用推荐码失败: " . $e->getMessage());
+            return [
+                'success' => false,
+                'message' => '使用推荐码时发生错误'
+            ];
+        }
+    }
+
+    /**
+     * 禁用推荐码
+     *
+     * @param string $code 推荐码
+     * @return bool
+     */
+    public function disableReferralCode(string $code): bool
+    {
+        try {
+            $referralCode = PromotionReferralCode::where('code', $code)->first();
+
+            if (!$referralCode) {
+                return false;
+            }
+
+            $referralCode->status = REFERRAL_CODE_STATUS::DISABLED;
+            return $referralCode->save();
+        } catch (\Exception $e) {
+            Log::error("禁用推荐码失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 清理过期的推荐码
+     *
+     * @return int 清理的数量
+     */
+    public function cleanExpiredReferralCodes(): int
+    {
+        try {
+            return PromotionReferralCode::where('status', REFERRAL_CODE_STATUS::ACTIVE)
+                ->whereNotNull('expire_time')
+                ->where('expire_time', '<', now())
+                ->update(['status' => REFERRAL_CODE_STATUS::DISABLED]);
+        } catch (\Exception $e) {
+            Log::error("清理过期推荐码失败: " . $e->getMessage());
+            return 0;
+        }
+    }
+}

+ 397 - 0
app/Module/Promotionurs/Logics/ReferralLogic.php

@@ -0,0 +1,397 @@
+<?php
+
+namespace App\Module\Promotion\Logics;
+
+use App\Module\Promotion\Enums\REFERRAL_LEVEL;
+use App\Module\Promotion\Models\PromotionReferralChange;
+use App\Module\Promotion\Models\PromotionUserReferral;
+use App\Module\Promotion\Models\PromotionUserRelationCache;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Redis;
+
+/**
+ * 推荐关系逻辑类
+ *
+ * 处理用户推荐关系的核心业务逻辑,包括建立推荐关系、修改推荐关系、
+ * 查询推荐关系、检查循环推荐等功能。该类仅供内部使用,不对外提供服务。
+ */
+class ReferralLogic
+{
+    /**
+     * 建立推荐关系
+     *
+     * @param int $userId 用户ID
+     * @param int $referrerId 推荐人ID
+     * @return bool
+     */
+    public function createReferralRelation(int $userId, int $referrerId): bool
+    {
+        // 验证是否已存在推荐关系
+        if ($this->hasReferrer($userId)) {
+            Log::warning("用户 {$userId} 已有推荐人,不能重复设置");
+            return false;
+        }
+
+        // 验证是否形成循环推荐
+        if ($this->checkCircularReferral($userId, $referrerId)) {
+            Log::warning("用户 {$userId} 和推荐人 {$referrerId} 形成循环推荐关系");
+            return false;
+        }
+
+        try {
+            // 创建直推关系
+            $referral = new PromotionUserReferral();
+            $referral->user_id = $userId;
+            $referral->referrer_id = $referrerId;
+            $result = $referral->save();
+
+            if ($result) {
+                // 更新关系缓存
+                $this->updateRelationCache($userId, null, $referrerId);
+            }
+
+            return $result;
+        } catch (\Exception $e) {
+            Log::error("创建推荐关系失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 更新推荐关系
+     *
+     * @param int $userId 用户ID
+     * @param int $newReferrerId 新推荐人ID
+     * @param string $reason 修改原因
+     * @param int $operatorId 操作人ID
+     * @return bool
+     */
+    public function updateReferralRelation(int $userId, int $newReferrerId, string $reason, int $operatorId): bool
+    {
+        // 获取旧的推荐人
+        $oldReferrerId = $this->getDirectReferrerId($userId);
+
+        // 如果新旧推荐人相同,无需更新
+        if ($oldReferrerId == $newReferrerId) {
+            return true;
+        }
+
+        // 验证是否形成循环推荐
+        if ($this->checkCircularReferral($userId, $newReferrerId)) {
+            Log::warning("用户 {$userId} 和新推荐人 {$newReferrerId} 形成循环推荐关系");
+            return false;
+        }
+
+        // 检查事务是否已开启
+        \UCore\Db\Helper::check_tr();
+
+        try {
+            // 删除旧的推荐关系
+            if ($oldReferrerId) {
+                PromotionUserReferral::where('user_id', $userId)->delete();
+            }
+
+            // 创建新的推荐关系
+            $referral = new PromotionUserReferral();
+            $referral->user_id = $userId;
+            $referral->referrer_id = $newReferrerId;
+            $result = $referral->save();
+
+            if ($result) {
+                // 记录修改历史
+                $change = new PromotionReferralChange();
+                $change->user_id = $userId;
+                $change->old_referrer_id = $oldReferrerId;
+                $change->new_referrer_id = $newReferrerId;
+                $change->change_time = now();
+                $change->change_reason = $reason;
+                $change->changed_by = $operatorId;
+                $change->save();
+
+                // 更新关系缓存
+                $this->updateRelationCache($userId, $oldReferrerId, $newReferrerId);
+            }
+
+            return $result;
+        } catch (\Exception $e) {
+            Log::error("更新推荐关系失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 获取用户的直接推荐人ID
+     *
+     * @param int $userId 用户ID
+     * @return int|null
+     */
+    public function getDirectReferrerId(int $userId): ?int
+    {
+        $referral = PromotionUserReferral::where('user_id', $userId)->first();
+        return $referral ? $referral->referrer_id : null;
+    }
+
+    /**
+     * 检查用户是否已有推荐人
+     *
+     * @param int $userId 用户ID
+     * @return bool
+     */
+    public function hasReferrer(int $userId): bool
+    {
+        return PromotionUserReferral::where('user_id', $userId)->exists();
+    }
+
+    /**
+     * 获取用户的直接下级
+     *
+     * @param int $userId 用户ID
+     * @return array
+     */
+    public function getDirectMembers(int $userId): array
+    {
+        return PromotionUserReferral::where('referrer_id', $userId)
+            ->with('user')
+            ->get()
+            ->toArray();
+    }
+
+    /**
+     * 获取用户的所有上级(包括直接和间接)
+     *
+     * @param int $userId 用户ID
+     * @param int $maxLevel 最大层级
+     * @return array
+     */
+    public function getAllReferrers(int $userId, int $maxLevel = 20): array
+    {
+        // 尝试从缓存获取
+        $cacheKey = "promotion:user:{$userId}:all_referrers";
+        $cachedData = Redis::get($cacheKey);
+
+        if ($cachedData !== null) {
+            return json_decode($cachedData, true);
+        }
+
+        // 从关系缓存表获取
+        $relations = PromotionUserRelationCache::where('user_id', $userId)
+            ->where('depth', '<=', $maxLevel)
+            ->orderBy('depth')
+            ->get();
+
+        $referrers = [];
+        foreach ($relations as $relation) {
+            $referrers[] = [
+                'user_id' => $relation->related_user_id,
+                'level' => $relation->level,
+                'depth' => $relation->depth,
+                'path' => $relation->path,
+            ];
+        }
+
+        // 缓存结果
+        Redis::setex($cacheKey, 86400, json_encode($referrers)); // 缓存1天
+
+        return $referrers;
+    }
+
+    /**
+     * 获取用户的所有团队成员(包括直接和间接)
+     *
+     * @param int $userId 用户ID
+     * @param int $maxLevel 最大层级
+     * @param int $page 页码
+     * @param int $pageSize 每页数量
+     * @return array
+     */
+    public function getAllPromotionMembers(int $userId, int $maxLevel = 20, int $page = 1, int $pageSize = 20): array
+    {
+        // 从关系缓存表获取
+        $query = PromotionUserRelationCache::where('related_user_id', $userId)
+            ->where('depth', '<=', $maxLevel);
+
+        $total = $query->count();
+
+        $relations = $query->orderBy('depth')
+            ->offset(($page - 1) * $pageSize)
+            ->limit($pageSize)
+            ->with('user')
+            ->get();
+
+        $members = [];
+        foreach ($relations as $relation) {
+            if ($relation->user) {
+                $members[] = [
+                    'user_id' => $relation->user_id,
+                    'username' => $relation->user->username ?? '',
+                    'level' => $relation->level,
+                    'depth' => $relation->depth,
+                    'path' => $relation->path,
+                    'created_at' => $relation->created_at,
+                ];
+            }
+        }
+
+        return [
+            'total' => $total,
+            'page' => $page,
+            'page_size' => $pageSize,
+            'total_pages' => ceil($total / $pageSize),
+            'members' => $members
+        ];
+    }
+
+    /**
+     * 检查是否形成循环推荐
+     *
+     * @param int $userId 用户ID
+     * @param int $referrerId 推荐人ID
+     * @return bool
+     */
+    public function checkCircularReferral(int $userId, int $referrerId): bool
+    {
+        // 如果用户ID和推荐人ID相同,直接形成循环
+        if ($userId == $referrerId) {
+            return true;
+        }
+
+        // 获取推荐人的所有上级
+        $referrerReferrers = $this->getAllReferrers($referrerId);
+
+        // 检查用户是否在推荐人的上级中
+        foreach ($referrerReferrers as $referrer) {
+            if ($referrer['user_id'] == $userId) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * 更新关系缓存
+     *
+     * @param int $userId 用户ID
+     * @param int|null $oldReferrerId 旧推荐人ID
+     * @param int $newReferrerId 新推荐人ID
+     * @return void
+     */
+    private function updateRelationCache(int $userId, ?int $oldReferrerId, int $newReferrerId): void
+    {
+        // 检查事务是否已开启
+        \UCore\Db\Helper::check_tr();
+
+        try {
+                // 1. 删除用户及其所有下级与旧上级及其所有上级的关系缓存
+                if ($oldReferrerId) {
+                    // 获取用户的所有下级ID
+                    $downlineIds = PromotionUserRelationCache::where('related_user_id', $userId)
+                        ->pluck('user_id')
+                        ->toArray();
+                    $downlineIds[] = $userId; // 包括用户自身
+
+                    // 获取旧上级的所有上级ID
+                    $upperReferrerIds = PromotionUserRelationCache::where('user_id', $oldReferrerId)
+                        ->pluck('related_user_id')
+                        ->toArray();
+                    $upperReferrerIds[] = $oldReferrerId; // 包括直接上级
+
+                    // 删除关系缓存
+                    PromotionUserRelationCache::whereIn('user_id', $downlineIds)
+                        ->whereIn('related_user_id', $upperReferrerIds)
+                        ->delete();
+
+                    // 清除Redis缓存
+                    foreach ($downlineIds as $downlineId) {
+                        Redis::del("promotion:user:{$downlineId}:all_referrers");
+                    }
+
+                    foreach ($upperReferrerIds as $upperReferrerId) {
+                        Redis::del("promotion:user:{$upperReferrerId}:all_members");
+                    }
+                }
+
+                // 2. 为用户创建与新上级的直接关系缓存
+                $directRelation = new PromotionUserRelationCache();
+                $directRelation->user_id = $userId;
+                $directRelation->related_user_id = $newReferrerId;
+                $directRelation->level = REFERRAL_LEVEL::DIRECT;
+                $directRelation->path = (string)$newReferrerId;
+                $directRelation->depth = 1;
+                $directRelation->save();
+
+                // 3. 获取新上级的所有上级
+                $upperRelations = PromotionUserRelationCache::where('user_id', $newReferrerId)->get();
+
+                // 4. 为用户创建与新上级的所有上级的间接关系缓存
+                foreach ($upperRelations as $upperRelation) {
+                    $indirectRelation = new PromotionUserRelationCache();
+                    $indirectRelation->user_id = $userId;
+                    $indirectRelation->related_user_id = $upperRelation->related_user_id;
+                    $indirectRelation->level = REFERRAL_LEVEL::INDIRECT;
+                    $indirectRelation->path = $newReferrerId . ',' . $upperRelation->path;
+                    $indirectRelation->depth = $upperRelation->depth + 1;
+                    $indirectRelation->save();
+                }
+
+                // 5. 获取用户的所有下级
+                $downlineRelations = PromotionUserRelationCache::where('related_user_id', $userId)->get();
+
+                // 6. 为用户的所有下级创建与新上级及其所有上级的关系缓存
+                foreach ($downlineRelations as $downlineRelation) {
+                    // 创建下级与新上级的关系
+                    $downlineToReferrer = new PromotionUserRelationCache();
+                    $downlineToReferrer->user_id = $downlineRelation->user_id;
+                    $downlineToReferrer->related_user_id = $newReferrerId;
+                    $downlineToReferrer->level = REFERRAL_LEVEL::INDIRECT;
+                    $downlineToReferrer->path = $downlineRelation->path . ',' . $newReferrerId;
+                    $downlineToReferrer->depth = $downlineRelation->depth + 1;
+                    $downlineToReferrer->save();
+
+                    // 创建下级与新上级的所有上级的关系
+                    foreach ($upperRelations as $upperRelation) {
+                        $downlineToUpper = new PromotionUserRelationCache();
+                        $downlineToUpper->user_id = $downlineRelation->user_id;
+                        $downlineToUpper->related_user_id = $upperRelation->related_user_id;
+                        $downlineToUpper->level = REFERRAL_LEVEL::INDIRECT;
+                        $downlineToUpper->path = $downlineRelation->path . ',' . $newReferrerId . ',' . $upperRelation->path;
+                        $downlineToUpper->depth = $downlineRelation->depth + $upperRelation->depth + 1;
+                        $downlineToUpper->save();
+                    }
+
+                    // 清除下级的Redis缓存
+                    Redis::del("promotion:user:{$downlineRelation->user_id}:all_referrers");
+                }
+
+                // 7. 清除新上级及其所有上级的Redis缓存
+                Redis::del("promotion:user:{$newReferrerId}:all_members");
+                foreach ($upperRelations as $upperRelation) {
+                    Redis::del("promotion:user:{$upperRelation->related_user_id}:all_members");
+                }
+        } catch (\Exception $e) {
+            Log::error("更新关系缓存失败: " . $e->getMessage());
+        }
+    }
+
+    /**
+     * 统计用户的直推人数
+     *
+     * @param int $userId 用户ID
+     * @return int
+     */
+    public function countDirectReferrals(int $userId): int
+    {
+        return PromotionUserReferral::where('referrer_id', $userId)->count();
+    }
+
+    /**
+     * 统计用户的团队总人数
+     *
+     * @param int $userId 用户ID
+     * @return int
+     */
+    public function countPromotionMembers(int $userId): int
+    {
+        return PromotionUserRelationCache::where('related_user_id', $userId)->count();
+    }
+}

+ 247 - 0
app/Module/Promotionurs/Logics/RelationCacheLogic.php

@@ -0,0 +1,247 @@
+<?php
+
+namespace App\Module\Promotion\Logics;
+
+use App\Module\Promotion\Enums\REFERRAL_LEVEL;
+use App\Module\Promotion\Models\PromotionUserReferral;
+use App\Module\Promotion\Models\PromotionUserRelationCache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Redis;
+
+/**
+ * 关系缓存逻辑类
+ *
+ * 处理用户关系缓存的核心业务逻辑,包括生成关系缓存、清理关系缓存、
+ * 重建关系缓存等功能。该类仅供内部使用,不对外提供服务。
+ */
+class RelationCacheLogic
+{
+    /**
+     * 生成用户的关系缓存
+     *
+     * @param int $userId 用户ID
+     * @return bool
+     */
+    public function generateUserRelationCache(int $userId): bool
+    {
+        try {
+            // 清除用户的现有关系缓存
+            $this->clearUserRelationCache($userId);
+
+            // 获取用户的直接推荐人
+            $referral = PromotionUserReferral::where('user_id', $userId)->first();
+
+            if (!$referral) {
+                Log::info("用户 {$userId} 没有推荐人,无需生成关系缓存");
+                return true;
+            }
+
+            $referrerId = $referral->referrer_id;
+
+            // 创建直接关系缓存
+            $directRelation = new PromotionUserRelationCache();
+            $directRelation->user_id = $userId;
+            $directRelation->related_user_id = $referrerId;
+            $directRelation->level = REFERRAL_LEVEL::DIRECT;
+            $directRelation->path = (string)$referrerId;
+            $directRelation->depth = 1;
+            $directRelation->save();
+
+            // 获取推荐人的所有上级
+            $upperRelations = PromotionUserRelationCache::where('user_id', $referrerId)->get();
+
+            // 创建间接关系缓存
+            foreach ($upperRelations as $upperRelation) {
+                $indirectRelation = new PromotionUserRelationCache();
+                $indirectRelation->user_id = $userId;
+                $indirectRelation->related_user_id = $upperRelation->related_user_id;
+                $indirectRelation->level = REFERRAL_LEVEL::INDIRECT;
+                $indirectRelation->path = $referrerId . ',' . $upperRelation->path;
+                $indirectRelation->depth = $upperRelation->depth + 1;
+                $indirectRelation->save();
+            }
+
+            // 清除Redis缓存
+            Redis::del("promotion:user:{$userId}:all_referrers");
+
+            return true;
+        } catch (\Exception $e) {
+            Log::error("生成用户关系缓存失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 清除用户的关系缓存
+     *
+     * @param int $userId 用户ID
+     * @return bool
+     */
+    public function clearUserRelationCache(int $userId): bool
+    {
+        try {
+            // 删除数据库中的关系缓存
+            PromotionUserRelationCache::where('user_id', $userId)->delete();
+
+            // 清除Redis缓存
+            Redis::del("promotion:user:{$userId}:all_referrers");
+
+            return true;
+        } catch (\Exception $e) {
+            Log::error("清除用户关系缓存失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 重建所有用户的关系缓存
+     *
+     * @param int $batchSize 批处理大小
+     * @return array 包含成功和失败的数量
+     */
+    public function rebuildAllRelationCache(int $batchSize = 100): array
+    {
+        try {
+            // 清空关系缓存表
+            PromotionUserRelationCache::truncate();
+
+            // 清除所有Redis缓存
+            $keys = Redis::keys("promotion:user:*:all_referrers");
+            if (!empty($keys)) {
+                Redis::del($keys);
+            }
+
+            // 获取所有用户ID
+            $userIds = PromotionUserReferral::pluck('user_id')->toArray();
+
+            $successCount = 0;
+            $failCount = 0;
+
+            // 分批处理
+            $chunks = array_chunk($userIds, $batchSize);
+
+            foreach ($chunks as $chunk) {
+                foreach ($chunk as $userId) {
+                    if ($this->generateUserRelationCache($userId)) {
+                        $successCount++;
+                    } else {
+                        $failCount++;
+                    }
+                }
+            }
+
+            return [
+                'success' => $successCount,
+                'fail' => $failCount,
+                'total' => count($userIds)
+            ];
+        } catch (\Exception $e) {
+            Log::error("重建所有关系缓存失败: " . $e->getMessage());
+            return [
+                'success' => 0,
+                'fail' => 0,
+                'total' => 0,
+                'error' => $e->getMessage()
+            ];
+        }
+    }
+
+    /**
+     * 检查关系缓存的完整性
+     *
+     * @return array 包含检查结果
+     */
+    public function checkRelationCacheIntegrity(): array
+    {
+        try {
+            $totalUsers = PromotionUserReferral::count();
+            $usersWithCache = PromotionUserRelationCache::distinct('user_id')->count('user_id');
+            $missingUsers = $totalUsers - $usersWithCache;
+
+            $circularRelations = DB::select("
+                SELECT r1.user_id, r1.referrer_id
+                FROM promotion_user_referrals r1
+                JOIN promotion_user_referrals r2 ON r1.referrer_id = r2.user_id
+                WHERE r2.referrer_id = r1.user_id
+            ");
+
+            $orphanedCaches = DB::select("
+                SELECT c.user_id
+                FROM promotion_user_relation_cache c
+                LEFT JOIN promotion_user_referrals r ON c.user_id = r.user_id
+                WHERE r.user_id IS NULL
+            ");
+
+            return [
+                'total_users' => $totalUsers,
+                'users_with_cache' => $usersWithCache,
+                'missing_users' => $missingUsers,
+                'circular_relations' => count($circularRelations),
+                'orphaned_caches' => count($orphanedCaches)
+            ];
+        } catch (\Exception $e) {
+            Log::error("检查关系缓存完整性失败: " . $e->getMessage());
+            return [
+                'error' => $e->getMessage()
+            ];
+        }
+    }
+
+    /**
+     * 修复关系缓存问题
+     *
+     * @return array 包含修复结果
+     */
+    public function fixRelationCacheIssues(): array
+    {
+        try {
+            // 检查完整性
+            $integrity = $this->checkRelationCacheIntegrity();
+
+            $fixed = [
+                'missing_users' => 0,
+                'orphaned_caches' => 0
+            ];
+
+            // 修复缺失的用户缓存
+            if ($integrity['missing_users'] > 0) {
+                $usersWithCache = PromotionUserRelationCache::distinct('user_id')->pluck('user_id')->toArray();
+                $allUsers = PromotionUserReferral::pluck('user_id')->toArray();
+                $missingUsers = array_diff($allUsers, $usersWithCache);
+
+                foreach ($missingUsers as $userId) {
+                    if ($this->generateUserRelationCache($userId)) {
+                        $fixed['missing_users']++;
+                    }
+                }
+            }
+
+            // 清理孤立的缓存
+            if ($integrity['orphaned_caches'] > 0) {
+                $orphanedCaches = DB::select("
+                    SELECT c.user_id
+                    FROM promotion_user_relation_cache c
+                    LEFT JOIN promotion_user_referrals r ON c.user_id = r.user_id
+                    WHERE r.user_id IS NULL
+                ");
+
+                foreach ($orphanedCaches as $cache) {
+                    if ($this->clearUserRelationCache($cache->user_id)) {
+                        $fixed['orphaned_caches']++;
+                    }
+                }
+            }
+
+            return [
+                'integrity' => $integrity,
+                'fixed' => $fixed
+            ];
+        } catch (\Exception $e) {
+            Log::error("修复关系缓存问题失败: " . $e->getMessage());
+            return [
+                'error' => $e->getMessage()
+            ];
+        }
+    }
+}

+ 326 - 0
app/Module/Promotionurs/Logics/TalentLogic.php

@@ -0,0 +1,326 @@
+<?php
+
+namespace App\Module\Promotion\Logics;
+
+use App\Module\Promotion\Enums\TALENT_LEVEL;
+use App\Module\Promotion\Models\PromotionTalentConfig;
+use App\Module\Promotion\Models\PromotionUserTalent;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Redis;
+
+/**
+ * 达人等级逻辑类
+ *
+ * 处理用户达人等级的核心业务逻辑,包括计算达人等级、更新达人等级、
+ * 获取达人等级配置等功能。该类仅供内部使用,不对外提供服务。
+ */
+class TalentLogic
+{
+    /**
+     * @var ReferralLogic
+     */
+    protected $referralLogic;
+
+    /**
+     * 构造函数
+     *
+     * @param ReferralLogic $referralLogic
+     */
+    public function __construct(ReferralLogic $referralLogic)
+    {
+        $this->referralLogic = $referralLogic;
+    }
+
+    /**
+     * 获取用户的达人等级信息
+     *
+     * @param int $userId 用户ID
+     * @return PromotionUserTalent|null
+     */
+    public function getUserTalent(int $userId): ?PromotionUserTalent
+    {
+        // 尝试从缓存获取
+        $cacheKey = "promotion:user:{$userId}:talent";
+        $cachedData = Redis::get($cacheKey);
+
+        if ($cachedData !== null) {
+            $talentData = json_decode($cachedData, true);
+            $talent = new PromotionUserTalent();
+            $talent->fill($talentData);
+            return $talent;
+        }
+
+        // 从数据库获取
+        $talent = PromotionUserTalent::where('user_id', $userId)->first();
+
+        // 如果不存在,创建一个默认的达人等级记录
+        if (!$talent) {
+            $talent = $this->createDefaultTalent($userId);
+        }
+
+        // 缓存结果
+        if ($talent) {
+            Redis::setex($cacheKey, 86400, json_encode($talent->toArray())); // 缓存1天
+        }
+
+        return $talent;
+    }
+
+    /**
+     * 创建默认的达人等级记录
+     *
+     * @param int $userId 用户ID
+     * @return PromotionUserTalent|null
+     */
+    public function createDefaultTalent(int $userId): ?PromotionUserTalent
+    {
+        try {
+            $talent = new PromotionUserTalent();
+            $talent->user_id = $userId;
+            $talent->talent_level = TALENT_LEVEL::NONE;
+            $talent->direct_count = 0;
+            $talent->promotion_count = 0;
+            $talent->save();
+
+            return $talent;
+        } catch (\Exception $e) {
+            Log::error("创建默认达人等级记录失败: " . $e->getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 更新用户的达人等级
+     *
+     * @param int $userId 用户ID
+     * @param int $talentLevel 达人等级
+     * @return bool
+     */
+    public function updateTalentLevel(int $userId, int $talentLevel): bool
+    {
+        try {
+            $talent = $this->getUserTalent($userId);
+
+            if (!$talent) {
+                return false;
+            }
+
+            // 如果等级没有变化,无需更新
+            if ($talent->talent_level == $talentLevel) {
+                return true;
+            }
+
+            $talent->talent_level = $talentLevel;
+            $result = $talent->save();
+
+            if ($result) {
+                // 清除缓存
+                Redis::del("promotion:user:{$userId}:talent");
+            }
+
+            return $result;
+        } catch (\Exception $e) {
+            Log::error("更新达人等级失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 更新用户的团队统计数据
+     *
+     * @param int $userId 用户ID
+     * @return bool
+     */
+    public function updatePromotionCounts(int $userId): bool
+    {
+        try {
+            $talent = $this->getUserTalent($userId);
+
+            if (!$talent) {
+                return false;
+            }
+
+            // 计算直推人数
+            $directCount = $this->referralLogic->countDirectReferrals($userId);
+
+            // 计算团队总人数
+            $promotionCount = $this->referralLogic->countPromotionMembers($userId);
+
+            // 更新数据
+            $talent->direct_count = $directCount;
+            $talent->promotion_count = $promotionCount;
+            $result = $talent->save();
+
+            if ($result) {
+                // 清除缓存
+                Redis::del("promotion:user:{$userId}:talent");
+            }
+
+            return $result;
+        } catch (\Exception $e) {
+            Log::error("更新团队统计数据失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 检查并更新用户的达人等级
+     *
+     * @param int $userId 用户ID
+     * @return bool
+     */
+    public function checkAndUpdateTalentLevel(int $userId): bool
+    {
+        try {
+            // 更新团队统计数据
+            $this->updatePromotionCounts($userId);
+
+            // 获取用户的达人信息
+            $talent = $this->getUserTalent($userId);
+
+            if (!$talent) {
+                return false;
+            }
+
+            // 获取所有达人等级配置
+            $configs = $this->getAllTalentConfigs();
+
+            // 计算应该的达人等级
+            $newLevel = TALENT_LEVEL::NONE;
+
+            foreach ($configs as $config) {
+                if ($talent->direct_count >= $config->direct_count_required &&
+                    $talent->promotion_count >= $config->promotion_count_required) {
+                    $newLevel = $config->level;
+                }
+            }
+
+            // 如果等级有变化,更新达人等级
+            if ($talent->talent_level != $newLevel) {
+                return $this->updateTalentLevel($userId, $newLevel);
+            }
+
+            return true;
+        } catch (\Exception $e) {
+            Log::error("检查并更新达人等级失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 获取达人等级配置
+     *
+     * @param int $level 等级
+     * @return PromotionTalentConfig|null
+     */
+    public function getTalentConfig(int $level): ?PromotionTalentConfig
+    {
+        // 尝试从缓存获取
+        $cacheKey = "promotion:talent_config:{$level}";
+        $cachedData = Redis::get($cacheKey);
+
+        if ($cachedData !== null) {
+            $configData = json_decode($cachedData, true);
+            $config = new PromotionTalentConfig();
+            $config->fill($configData);
+            return $config;
+        }
+
+        // 从数据库获取
+        $config = PromotionTalentConfig::where('level', $level)->first();
+
+        // 缓存结果
+        if ($config) {
+            Redis::setex($cacheKey, 86400 * 7, json_encode($config->toArray())); // 缓存7天
+        }
+
+        return $config;
+    }
+
+    /**
+     * 获取所有达人等级配置
+     *
+     * @return array
+     */
+    public function getAllTalentConfigs(): array
+    {
+        // 尝试从缓存获取
+        $cacheKey = "promotion:talent_configs";
+        $cachedData = Redis::get($cacheKey);
+
+        if ($cachedData !== null) {
+            $configsData = json_decode($cachedData, true);
+            $configs = [];
+
+            foreach ($configsData as $configData) {
+                $config = new PromotionTalentConfig();
+                $config->fill($configData);
+                $configs[] = $config;
+            }
+
+            return $configs;
+        }
+
+        // 从数据库获取
+        $configs = PromotionTalentConfig::orderBy('level')->get()->toArray();
+
+        // 缓存结果
+        Redis::setex($cacheKey, 86400 * 7, json_encode($configs)); // 缓存7天
+
+        return $configs;
+    }
+
+    /**
+     * 获取达人等级分成比例
+     *
+     * @param int $level 等级
+     * @return float
+     */
+    public function getTalentProfitRate(int $level): float
+    {
+        $config = $this->getTalentConfig($level);
+
+        if (!$config) {
+            return 0.0;
+        }
+
+        return $config->profit_rate;
+    }
+
+    /**
+     * 获取达人等级权益
+     *
+     * @param int $level 等级
+     * @return array
+     */
+    public function getTalentBenefits(int $level): array
+    {
+        $config = $this->getTalentConfig($level);
+
+        if (!$config || empty($config->benefits)) {
+            return [];
+        }
+
+        return is_array($config->benefits) ? $config->benefits : json_decode($config->benefits, true);
+    }
+
+    /**
+     * 批量更新用户的达人等级
+     *
+     * @param array $userIds 用户ID数组
+     * @return int 成功更新的数量
+     */
+    public function batchUpdateTalentLevels(array $userIds): int
+    {
+        $successCount = 0;
+
+        foreach ($userIds as $userId) {
+            if ($this->checkAndUpdateTalentLevel($userId)) {
+                $successCount++;
+            }
+        }
+
+        return $successCount;
+    }
+}

+ 256 - 0
app/Module/Promotionurs/Logics/TeamProfitLogic.php

@@ -0,0 +1,256 @@
+<?php
+
+namespace App\Module\Promotion\Logics;
+
+use App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE;
+use App\Module\Promotion\Enums\REFERRAL_LEVEL;
+use App\Module\Promotion\Models\PromotionProfit;
+use App\Module\Promotion\Models\PromotionProfitRule;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 团队收益逻辑类
+ *
+ * 处理团队收益的核心业务逻辑,包括计算收益分成、记录收益、
+ * 获取收益规则等功能。该类仅供内部使用,不对外提供服务。
+ */
+class PromotionProfitLogic
+{
+    /**
+     * @var ReferralLogic
+     */
+    protected $referralLogic;
+
+    /**
+     * @var TalentLogic
+     */
+    protected $talentLogic;
+
+    /**
+     * 构造函数
+     *
+     * @param ReferralLogic $referralLogic
+     * @param TalentLogic $talentLogic
+     */
+    public function __construct(ReferralLogic $referralLogic, TalentLogic $talentLogic)
+    {
+        $this->referralLogic = $referralLogic;
+        $this->talentLogic = $talentLogic;
+    }
+
+    /**
+     * 计算并记录团队收益分成
+     *
+     * @param int $userId 产生收益的用户ID
+     * @param string $sourceType 收益来源类型
+     * @param int $sourceId 收益来源ID
+     * @param int $itemId 物品ID
+     * @param int $amount 收益数量
+     * @return bool
+     */
+    public function calculateAndRecordProfit(int $userId, string $sourceType, int $sourceId, int $itemId, int $amount): bool
+    {
+        try {
+            // 获取收益分成规则
+            $rule = $this->getProfitRule($sourceType);
+
+            if (!$rule || !$rule->isActive()) {
+                Log::info("收益分成规则不存在或未激活: {$sourceType}");
+                return false;
+            }
+
+            // 检查收益是否满足最小数量要求
+            $minAmount = $rule->getRuleAttribute('min_amount', 0);
+            if ($amount < $minAmount) {
+                Log::info("收益数量不满足最小要求: {$amount} < {$minAmount}");
+                return false;
+            }
+
+            // 检查收益是否超过最大数量限制
+            $maxAmount = $rule->getRuleAttribute('max_amount', PHP_INT_MAX);
+            if ($amount > $maxAmount) {
+                $amount = $maxAmount;
+                Log::info("收益数量超过最大限制,已调整为: {$maxAmount}");
+            }
+
+            // 检查物品是否在排除列表中
+            $excludedItems = $rule->getRuleAttribute('excluded_items', []);
+            if (in_array($itemId, $excludedItems)) {
+                Log::info("物品在排除列表中: {$itemId}");
+                return false;
+            }
+
+            // 获取用户的所有上级
+            $referrers = $this->referralLogic->getAllReferrers($userId, $rule->max_indirect_level);
+
+            if (empty($referrers)) {
+                Log::info("用户没有上级: {$userId}");
+                return true;
+            }
+
+            // 检查事务是否已开启
+            \UCore\Db\Helper::check_tr();
+
+            // 记录收益分成
+            $success = true;
+
+            foreach ($referrers as $referrer) {
+                $referrerId = $referrer['user_id'];
+                $level = $referrer['level'];
+                $depth = $referrer['depth'];
+
+                // 计算分成比例
+                $profitRate = $this->calculateProfitRate($level, $depth, $rule);
+
+                // 计算分成数量
+                $profitAmount = (int)($amount * $profitRate);
+
+                if ($profitAmount <= 0) {
+                    continue;
+                }
+
+                // 记录收益
+                $profit = new PromotionProfit();
+                $profit->user_id = $referrerId;
+                $profit->promotion_member_id = $userId;
+                $profit->source_id = $sourceId;
+                $profit->source_type = $sourceType;
+                $profit->item_id = $itemId;
+                $profit->profit_amount = $profitAmount;
+                $profit->profit_rate = $profitRate;
+                $profit->relation_type = $level;
+
+                if (!$profit->save()) {
+                    $success = false;
+                    Log::error("记录收益分成失败: 用户 {$referrerId}, 来源 {$sourceType}, 数量 {$profitAmount}");
+                }
+            }
+
+            return $success;
+        } catch (\Exception $e) {
+            Log::error("计算并记录团队收益分成失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 计算分成比例
+     *
+     * @param int $level 关系层级
+     * @param int $depth 层级深度
+     * @param PromotionProfitRule $rule 分成规则
+     * @return float
+     */
+    private function calculateProfitRate(int $level, int $depth, PromotionProfitRule $rule): float
+    {
+        // 直推关系
+        if ($level == REFERRAL_LEVEL::DIRECT) {
+            return $rule->direct_profit_rate;
+        }
+
+        // 间推关系
+        if ($level == REFERRAL_LEVEL::INDIRECT) {
+            // 获取用户的达人等级
+            $talent = $this->talentLogic->getUserTalent($depth);
+
+            if (!$talent || $talent->talent_level == 0) {
+                return 0.0;
+            }
+
+            // 获取达人等级对应的分成比例
+            $profitRate = $this->talentLogic->getTalentProfitRate($talent->talent_level);
+
+            // 应用特殊规则
+            $specialRates = $rule->getRuleAttribute('special_rates', []);
+            foreach ($specialRates as $specialRate) {
+                if (isset($specialRate['talent_level']) && $specialRate['talent_level'] == $talent->talent_level) {
+                    if (isset($specialRate['bonus_rate'])) {
+                        $profitRate += $specialRate['bonus_rate'];
+                    }
+                }
+            }
+
+            return $profitRate;
+        }
+
+        return 0.0;
+    }
+
+    /**
+     * 获取收益分成规则
+     *
+     * @param string $sourceType 收益来源类型
+     * @return PromotionProfitRule|null
+     */
+    public function getProfitRule(string $sourceType): ?PromotionProfitRule
+    {
+        return PromotionProfitRule::where('source_type', $sourceType)
+            ->where('status', 1)
+            ->first();
+    }
+
+    /**
+     * 获取用户的团队收益记录
+     *
+     * @param int $userId 用户ID
+     * @param string|null $sourceType 收益来源类型
+     * @param int $page 页码
+     * @param int $pageSize 每页数量
+     * @return array
+     */
+    public function getUserProfits(int $userId, ?string $sourceType = null, int $page = 1, int $pageSize = 20): array
+    {
+        $query = PromotionProfit::where('user_id', $userId);
+
+        if ($sourceType) {
+            $query->where('source_type', $sourceType);
+        }
+
+        $total = $query->count();
+
+        $profits = $query->orderBy('created_at', 'desc')
+            ->offset(($page - 1) * $pageSize)
+            ->limit($pageSize)
+            ->with(['promotionMember', 'item'])
+            ->get();
+
+        return [
+            'total' => $total,
+            'page' => $page,
+            'page_size' => $pageSize,
+            'total_pages' => ceil($total / $pageSize),
+            'profits' => $profits
+        ];
+    }
+
+    /**
+     * 统计用户的团队收益
+     *
+     * @param int $userId 用户ID
+     * @param string|null $sourceType 收益来源类型
+     * @param int|null $itemId 物品ID
+     * @return array
+     */
+    public function sumUserProfits(int $userId, ?string $sourceType = null, ?int $itemId = null): array
+    {
+        $query = PromotionProfit::where('user_id', $userId);
+
+        if ($sourceType) {
+            $query->where('source_type', $sourceType);
+        }
+
+        if ($itemId) {
+            $query->where('item_id', $itemId);
+        }
+
+        $directSum = (clone $query)->where('relation_type', REFERRAL_LEVEL::DIRECT)->sum('profit_amount');
+        $indirectSum = (clone $query)->where('relation_type', REFERRAL_LEVEL::INDIRECT)->sum('profit_amount');
+        $totalSum = $directSum + $indirectSum;
+
+        return [
+            'direct_profit' => $directSum,
+            'indirect_profit' => $indirectSum,
+            'total_profit' => $totalSum
+        ];
+    }
+}

+ 124 - 0
app/Module/Promotionurs/Models/TeamInviteReward.php

@@ -0,0 +1,124 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use UCore\ModelCore;
+
+/**
+ * 邀请奖励记录
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  int  $user_id  获得奖励的用户ID
+ * @property  int  $invited_user_id  被邀请的用户ID
+ * @property  string  $reward_type  奖励类型:item=物品,coin=货币,exp=经验
+ * @property  int  $reward_id  奖励ID(物品ID或货币类型ID)
+ * @property  int  $reward_amount  奖励数量
+ * @property  string  $reward_source  奖励来源:register=注册,upgrade=升级,task=任务
+ * @property  int  $status  状态:0=未发放,1=已发放,2=已过期
+ * @property  string  $remark  备注
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * @property  \Carbon\Carbon  $updated_at  更新时间
+ * field end
+ */
+class PromotionInviteReward extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_invite_rewards';
+
+
+
+    /**
+     * 奖励类型常量
+     */
+    const REWARD_TYPE_ITEM = 'item';
+    const REWARD_TYPE_COIN = 'coin';
+    const REWARD_TYPE_EXP = 'exp';
+
+    /**
+     * 奖励来源常量
+     */
+    const REWARD_SOURCE_REGISTER = 'register';
+    const REWARD_SOURCE_UPGRADE = 'upgrade';
+    const REWARD_SOURCE_TASK = 'task';
+
+    /**
+     * 状态常量
+     */
+    const STATUS_PENDING = 0;
+    const STATUS_ISSUED = 1;
+    const STATUS_EXPIRED = 2;
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'user_id',
+        'invited_user_id',
+        'reward_type',
+        'reward_id',
+        'reward_amount',
+        'reward_source',
+        'status',
+        'remark',
+    ];
+    // attrlist end
+
+    /**
+     * 获取用户信息
+     *
+     * @return BelongsTo
+     */
+    public function user()
+    {
+        return $this->belongsTo('App\Models\User', 'user_id');
+    }
+
+    /**
+     * 获取被邀请用户信息
+     *
+     * @return BelongsTo
+     */
+    public function invitedUser()
+    {
+        return $this->belongsTo('App\Models\User', 'invited_user_id');
+    }
+
+    /**
+     * 获取物品信息(如果奖励类型为物品)
+     *
+     * @return BelongsTo|null
+     */
+    public function item()
+    {
+        if ($this->reward_type != self::REWARD_TYPE_ITEM) {
+            return null;
+        }
+
+        return $this->belongsTo('App\Module\GameItems\Models\Item', 'reward_id');
+    }
+
+    /**
+     * 判断奖励是否已发放
+     *
+     * @return bool
+     */
+    public function isIssued(): bool
+    {
+        return $this->status == self::STATUS_ISSUED;
+    }
+
+    /**
+     * 判断奖励是否已过期
+     *
+     * @return bool
+     */
+    public function isExpired(): bool
+    {
+        return $this->status == self::STATUS_EXPIRED;
+    }
+}

+ 98 - 0
app/Module/Promotionurs/Models/TeamProfit.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE;
+use App\Module\Promotion\Enums\REFERRAL_LEVEL;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use UCore\ModelCore;
+
+/**
+ * 团队收益记录
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  int  $user_id  获得收益的用户ID
+ * @property  int  $promotion_member_id  团队成员ID
+ * @property  int  $source_id  收益来源ID
+ * @property  \App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE  $source_type  收益来源类型
+ * @property  int  $item_id  物品ID
+ * @property  int  $profit_amount  分成收益数量
+ * @property  float  $profit_rate  分成比例
+ * @property  \App\Module\Promotion\Enums\REFERRAL_LEVEL  $relation_type  关系类型:1直推,2间推
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * field end
+ */
+class PromotionProfit extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_profits';
+
+
+
+    /**
+     * 指示模型是否应该被打上时间戳
+     *
+     * @var bool
+     */
+    public $timestamps = false;
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'user_id',
+        'promotion_member_id',
+        'source_id',
+        'source_type',
+        'item_id',
+        'profit_amount',
+        'profit_rate',
+        'relation_type',
+    ];
+    // attrlist end
+
+    /**
+     * 应该被转换为原生类型的属性
+     *
+     * @var array
+     */
+    protected $casts = [
+        'profit_rate' => 'float',
+        'source_type' => PROFIT_SOURCE_TYPE::class,
+        'relation_type' => REFERRAL_LEVEL::class,
+    ];
+
+    /**
+     * 获取收益用户信息
+     *
+     * @return BelongsTo
+     */
+    public function user()
+    {
+        return $this->belongsTo('App\Models\User', 'user_id');
+    }
+
+    /**
+     * 获取团队成员信息
+     *
+     * @return BelongsTo
+     */
+    public function promotionMember()
+    {
+        return $this->belongsTo('App\Models\User', 'promotion_member_id');
+    }
+
+    /**
+     * 获取物品信息
+     *
+     * @return BelongsTo
+     */
+    public function item()
+    {
+        return $this->belongsTo('App\Module\GameItems\Models\Item', 'item_id');
+    }
+}

+ 83 - 0
app/Module/Promotionurs/Models/TeamProfitRule.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE;
+use UCore\ModelCore;
+
+/**
+ * 收益分成规则
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  \App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE  $source_type  来源类型
+ * @property  float  $direct_profit_rate  直推分成比例
+ * @property  int  $max_indirect_level  最大间推层级
+ * @property  bool  $status  状态:1有效,0无效
+ * @property  array  $rules  特殊规则
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * @property  \Carbon\Carbon  $updated_at  更新时间
+ * field end
+ */
+class PromotionProfitRule extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_profit_rules';
+
+
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'source_type',
+        'direct_profit_rate',
+        'max_indirect_level',
+        'status',
+        'rules',
+    ];
+    // attrlist end
+
+    /**
+     * 应该被转换为原生类型的属性
+     *
+     * @var array
+     */
+    protected $casts = [
+        'source_type' => PROFIT_SOURCE_TYPE::class,
+        'direct_profit_rate' => 'float',
+        'status' => 'boolean',
+        'rules' => 'json',
+    ];
+
+    /**
+     * 获取规则的特定属性
+     *
+     * @param string $key
+     * @param mixed $default
+     * @return mixed
+     */
+    public function getRuleAttribute(string $key, $default = null)
+    {
+        if (empty($this->rules)) {
+            return $default;
+        }
+
+        $rules = is_array($this->rules) ? $this->rules : json_decode($this->rules, true);
+
+        return $rules[$key] ?? $default;
+    }
+
+    /**
+     * 判断规则是否有效
+     *
+     * @return bool
+     */
+    public function isActive(): bool
+    {
+        return (bool)$this->status;
+    }
+}

+ 101 - 0
app/Module/Promotionurs/Models/TeamReferralChange.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use UCore\ModelCore;
+
+/**
+ * 推荐关系修改记录
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  int  $user_id  用户ID
+ * @property  int  $old_referrer_id  旧推荐人ID
+ * @property  int  $new_referrer_id  新推荐人ID
+ * @property  string  $change_time  修改时间
+ * @property  string  $change_reason  修改原因
+ * @property  int  $changed_by  操作人ID
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * field end
+ */
+class PromotionReferralChange extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_referral_changes';
+
+
+
+    /**
+     * 指示模型是否应该被打上时间戳
+     *
+     * @var bool
+     */
+    public $timestamps = false;
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'user_id',
+        'old_referrer_id',
+        'new_referrer_id',
+        'change_time',
+        'change_reason',
+        'changed_by',
+    ];
+    // attrlist end
+
+    /**
+     * 应该被转换为日期的属性
+     *
+     * @var array
+     */
+    protected $dates = [
+        'change_time',
+        'created_at',
+    ];
+
+    /**
+     * 获取用户信息
+     *
+     * @return BelongsTo
+     */
+    public function user()
+    {
+        return $this->belongsTo('App\Models\User', 'user_id');
+    }
+
+    /**
+     * 获取旧推荐人信息
+     *
+     * @return BelongsTo
+     */
+    public function oldReferrer()
+    {
+        return $this->belongsTo('App\Models\User', 'old_referrer_id');
+    }
+
+    /**
+     * 获取新推荐人信息
+     *
+     * @return BelongsTo
+     */
+    public function newReferrer()
+    {
+        return $this->belongsTo('App\Models\User', 'new_referrer_id');
+    }
+
+    /**
+     * 获取操作人信息
+     *
+     * @return BelongsTo
+     */
+    public function operator()
+    {
+        return $this->belongsTo('App\Models\User', 'changed_by');
+    }
+}

+ 103 - 0
app/Module/Promotionurs/Models/TeamReferralCode.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use App\Module\Promotion\Enums\REFERRAL_CODE_STATUS;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use UCore\ModelCore;
+
+/**
+ * 推荐码
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  int  $user_id  用户ID
+ * @property  string  $code  推荐码
+ * @property  int  $usage_count  使用次数
+ * @property  \App\Module\Promotion\Enums\REFERRAL_CODE_STATUS  $status  状态:1有效,0无效
+ * @property  string  $expire_time  过期时间,NULL表示永不过期
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * @property  \Carbon\Carbon  $updated_at  更新时间
+ * field end
+ */
+class PromotionReferralCode extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_referral_codes';
+
+
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'user_id',
+        'code',
+        'usage_count',
+        'status',
+        'expire_time',
+    ];
+    // attrlist end
+
+    /**
+     * 应该被转换为日期的属性
+     *
+     * @var array
+     */
+    protected $dates = [
+        'expire_time',
+        'created_at',
+        'updated_at',
+    ];
+
+    /**
+     * 应该被转换为原生类型的属性
+     *
+     * @var array
+     */
+    protected $casts = [
+        'status' => REFERRAL_CODE_STATUS::class,
+    ];
+
+    /**
+     * 获取用户信息
+     *
+     * @return BelongsTo
+     */
+    public function user()
+    {
+        return $this->belongsTo('App\Models\User', 'user_id');
+    }
+
+    /**
+     * 获取推荐码使用记录
+     *
+     * @return HasMany
+     */
+    public function usages()
+    {
+        return $this->hasMany(PromotionReferralCodeUsage::class, 'code', 'code');
+    }
+
+    /**
+     * 判断推荐码是否有效
+     *
+     * @return bool
+     */
+    public function isValid(): bool
+    {
+        if ($this->status != REFERRAL_CODE_STATUS::ACTIVE) {
+            return false;
+        }
+
+        if ($this->expire_time && $this->expire_time->isPast()) {
+            return false;
+        }
+
+        return true;
+    }
+}

+ 106 - 0
app/Module/Promotionurs/Models/TeamReferralCodeUsage.php

@@ -0,0 +1,106 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use UCore\ModelCore;
+
+/**
+ * 邀请码使用记录
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  string  $code  使用的邀请码
+ * @property  int  $code_owner_id  邀请码所有者用户ID
+ * @property  int  $user_id  使用邀请码的用户ID
+ * @property  string  $ip_address  使用时的IP地址
+ * @property  string  $user_agent  使用时的用户代理
+ * @property  int  $status  状态:0=失败,1=成功,2=已撤销
+ * @property  string  $result  使用结果描述
+ * @property  string  $remark  备注
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * @property  \Carbon\Carbon  $updated_at  更新时间
+ * field end
+ */
+class PromotionReferralCodeUsage extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_referral_code_usages';
+
+
+
+    /**
+     * 状态常量
+     */
+    const STATUS_FAILED = 0;
+    const STATUS_SUCCESS = 1;
+    const STATUS_REVOKED = 2;
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'code',
+        'code_owner_id',
+        'user_id',
+        'ip_address',
+        'user_agent',
+        'status',
+        'result',
+        'remark',
+    ];
+    // attrlist end
+
+    /**
+     * 获取邀请码信息
+     *
+     * @return BelongsTo
+     */
+    public function referralCode()
+    {
+        return $this->belongsTo(PromotionReferralCode::class, 'code', 'code');
+    }
+
+    /**
+     * 获取邀请码所有者信息
+     *
+     * @return BelongsTo
+     */
+    public function codeOwner()
+    {
+        return $this->belongsTo('App\Models\User', 'code_owner_id');
+    }
+
+    /**
+     * 获取使用者信息
+     *
+     * @return BelongsTo
+     */
+    public function user()
+    {
+        return $this->belongsTo('App\Models\User', 'user_id');
+    }
+
+    /**
+     * 判断使用是否成功
+     *
+     * @return bool
+     */
+    public function isSuccess(): bool
+    {
+        return $this->status == self::STATUS_SUCCESS;
+    }
+
+    /**
+     * 判断使用是否已撤销
+     *
+     * @return bool
+     */
+    public function isRevoked(): bool
+    {
+        return $this->status == self::STATUS_REVOKED;
+    }
+}

+ 83 - 0
app/Module/Promotionurs/Models/TeamTalentConfig.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use App\Module\Promotion\Enums\TALENT_LEVEL;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use UCore\ModelCore;
+
+/**
+ * 达人等级配置
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  \App\Module\Promotion\Enums\TALENT_LEVEL  $level  等级
+ * @property  string  $name  等级名称
+ * @property  int  $direct_count_required  所需直推人数
+ * @property  int  $promotion_count_required  所需团队总人数
+ * @property  float  $profit_rate  间推分成比例
+ * @property  array  $benefits  等级权益
+ * @property  string  $icon  等级图标
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * @property  \Carbon\Carbon  $updated_at  更新时间
+ * field end
+ */
+class PromotionTalentConfig extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_talent_configs';
+
+
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'level',
+        'name',
+        'direct_count_required',
+        'promotion_count_required',
+        'profit_rate',
+        'benefits',
+        'icon',
+    ];
+    // attrlist end
+
+    /**
+     * 应该被转换为原生类型的属性
+     *
+     * @var array
+     */
+    protected $casts = [
+        'level' => TALENT_LEVEL::class,
+        'profit_rate' => 'float',
+        'benefits' => 'json',
+    ];
+
+    /**
+     * 获取拥有此等级的用户
+     *
+     * @return HasMany
+     */
+    public function users()
+    {
+        return $this->hasMany(PromotionUserTalent::class, 'talent_level', 'level');
+    }
+
+    /**
+     * 获取等级图标的完整URL
+     *
+     * @return string|null
+     */
+    public function getIconUrlAttribute()
+    {
+        if (empty($this->icon)) {
+            return null;
+        }
+
+        return asset($this->icon);
+    }
+}

+ 57 - 0
app/Module/Promotionurs/Models/TeamUserReferral.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use UCore\ModelCore;
+
+/**
+ * 用户推荐关系
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  int  $user_id  用户ID
+ * @property  int  $referrer_id  直接推荐人ID
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * @property  \Carbon\Carbon  $updated_at  更新时间
+ * field end
+ */
+class PromotionUserReferral extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_user_referrals';
+
+
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'user_id',
+        'referrer_id',
+    ];
+    // attrlist end
+
+    /**
+     * 获取被推荐用户信息
+     *
+     * @return BelongsTo
+     */
+    public function user()
+    {
+        return $this->belongsTo('App\Models\User', 'user_id');
+    }
+
+    /**
+     * 获取推荐人信息
+     *
+     * @return BelongsTo
+     */
+    public function referrer()
+    {
+        return $this->belongsTo('App\Models\User', 'referrer_id');
+    }
+}

+ 98 - 0
app/Module/Promotionurs/Models/TeamUserRelationCache.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use App\Module\Promotion\Enums\REFERRAL_LEVEL;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use UCore\ModelCore;
+
+/**
+ * 用户关系缓存
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  int  $user_id  用户ID
+ * @property  int  $related_user_id  关联用户ID(上级)
+ * @property  \App\Module\Promotion\Enums\REFERRAL_LEVEL  $level  关系层级:1直接,2间接
+ * @property  string  $path  关系路径,格式:1,2,3
+ * @property  int  $depth  层级深度,从1开始
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * @property  \Carbon\Carbon  $updated_at  更新时间
+ * field end
+ */
+class PromotionUserRelationCache extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_user_relation_cache';
+
+
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'user_id',
+        'related_user_id',
+        'level',
+        'path',
+        'depth',
+    ];
+    // attrlist end
+
+    /**
+     * 应该被转换为原生类型的属性
+     *
+     * @var array
+     */
+    protected $casts = [
+        'level' => REFERRAL_LEVEL::class,
+        'depth' => 'integer',
+    ];
+
+    /**
+     * 获取用户信息
+     *
+     * @return BelongsTo
+     */
+    public function user()
+    {
+        return $this->belongsTo('App\Models\User', 'user_id');
+    }
+
+    /**
+     * 获取关联用户信息
+     *
+     * @return BelongsTo
+     */
+    public function relatedUser()
+    {
+        return $this->belongsTo('App\Models\User', 'related_user_id');
+    }
+
+    /**
+     * 获取关系路径数组
+     *
+     * @return array
+     */
+    public function getPathArrayAttribute(): array
+    {
+        if (empty($this->path)) {
+            return [];
+        }
+
+        return explode(',', $this->path);
+    }
+
+    /**
+     * 判断是否为直接关系
+     *
+     * @return bool
+     */
+    public function isDirectRelation(): bool
+    {
+        return $this->level == REFERRAL_LEVEL::DIRECT;
+    }
+}

+ 70 - 0
app/Module/Promotionurs/Models/TeamUserTalent.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace App\Module\Promotion\Models;
+
+use App\Module\Promotion\Enums\TALENT_LEVEL;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use UCore\ModelCore;
+
+/**
+ * 达人等级
+ *
+ * field start
+ * @property  int  $id  主键ID
+ * @property  int  $user_id  用户ID
+ * @property  \App\Module\Promotion\Enums\TALENT_LEVEL  $talent_level  达人等级:0无,1初级,2中级,3高级,4资深,5顶级
+ * @property  int  $direct_count  直推人数
+ * @property  int  $promotion_count  团队总人数
+ * @property  \Carbon\Carbon  $created_at  创建时间
+ * @property  \Carbon\Carbon  $updated_at  更新时间
+ * field end
+ */
+class PromotionUserTalent extends ModelCore
+{
+    /**
+     * 与模型关联的表名
+     *
+     * @var string
+     */
+    protected $table = 'promotion_user_talents';
+
+
+    // attrlist start
+    protected $fillable = [
+        'id',
+        'user_id',
+        'talent_level',
+        'direct_count',
+        'promotion_count',
+    ];
+    // attrlist end
+
+    /**
+     * 应该被转换为原生类型的属性
+     *
+     * @var array
+     */
+    protected $casts = [
+        'talent_level' => TALENT_LEVEL::class,
+    ];
+
+    /**
+     * 获取用户信息
+     *
+     * @return BelongsTo
+     */
+    public function user()
+    {
+        return $this->belongsTo('App\Models\User', 'user_id');
+    }
+
+    /**
+     * 获取达人等级配置
+     *
+     * @return BelongsTo
+     */
+    public function config()
+    {
+        return $this->belongsTo(PromotionTalentConfig::class, 'talent_level', 'level');
+    }
+}

+ 84 - 0
app/Module/Promotionurs/Providers/TeamServiceProvider.php

@@ -0,0 +1,84 @@
+<?php
+
+namespace App\Module\Promotion\Providers;
+
+use App\Module\Promotion\Commands\CleanExpiredReferralCodesCommand;
+use App\Module\Promotion\Commands\RebuildRelationCacheCommand;
+use App\Module\Promotion\Commands\UpdateTalentLevelsCommand;
+use App\Module\Promotion\Events\ReferralCreatedEvent;
+use App\Module\Promotion\Events\ReferralUpdatedEvent;
+use App\Module\Promotion\Events\PromotionProfitCreatedEvent;
+use App\Module\Promotion\Listeners\DistributePromotionProfitListener;
+use App\Module\Promotion\Listeners\UpdateTalentLevelListener;
+use App\Module\Promotion\Listeners\UpdatePromotionCountsListener;
+use App\Module\Promotion\Logics\ReferralCodeLogic;
+use App\Module\Promotion\Logics\ReferralLogic;
+use App\Module\Promotion\Logics\RelationCacheLogic;
+use App\Module\Promotion\Logics\TalentLogic;
+use App\Module\Promotion\Logics\PromotionProfitLogic;
+use Illuminate\Support\Facades\Event;
+use Illuminate\Support\ServiceProvider;
+
+/**
+ * 团队模块服务提供者
+ *
+ * 注册团队模块的服务、事件、命令等。
+ */
+class PromotionServiceProvider extends ServiceProvider
+{
+    /**
+     * 注册服务
+     *
+     * @return void
+     */
+    public function register()
+    {
+        // 注册逻辑层类
+        $this->app->singleton(ReferralLogic::class, function ($app) {
+            return new ReferralLogic();
+        });
+
+        $this->app->singleton(RelationCacheLogic::class, function ($app) {
+            return new RelationCacheLogic();
+        });
+
+        $this->app->singleton(TalentLogic::class, function ($app) {
+            return new TalentLogic($app->make(ReferralLogic::class));
+        });
+
+        $this->app->singleton(PromotionProfitLogic::class, function ($app) {
+            return new PromotionProfitLogic(
+                $app->make(ReferralLogic::class),
+                $app->make(TalentLogic::class)
+            );
+        });
+
+        $this->app->singleton(ReferralCodeLogic::class, function ($app) {
+            return new ReferralCodeLogic($app->make(ReferralLogic::class));
+        });
+    }
+
+    /**
+     * 启动服务
+     *
+     * @return void
+     */
+    public function boot()
+    {
+        // 注册事件和监听器
+        Event::listen(ReferralCreatedEvent::class, UpdatePromotionCountsListener::class);
+        Event::listen(ReferralCreatedEvent::class, UpdateTalentLevelListener::class);
+        Event::listen(ReferralUpdatedEvent::class, UpdatePromotionCountsListener::class);
+        Event::listen(ReferralUpdatedEvent::class, UpdateTalentLevelListener::class);
+        Event::listen(PromotionProfitCreatedEvent::class, DistributePromotionProfitListener::class);
+
+        // 注册命令
+        if ($this->app->runningInConsole()) {
+            $this->commands([
+                CleanExpiredReferralCodesCommand::class,
+                RebuildRelationCacheCommand::class,
+                UpdateTalentLevelsCommand::class,
+            ]);
+        }
+    }
+}

+ 156 - 0
app/Module/Promotionurs/README.md

@@ -0,0 +1,156 @@
+# 团队模块
+
+## 概述
+
+团队模块是开心农场系统的核心模块之一,负责管理用户之间的推荐关系、团队结构、达人等级和收益分成机制。该模块通过建立用户间的直推和间推关系,形成团队结构,并在团队成员产生收益时进行分成,鼓励用户发展团队,形成良性的社交生态。
+
+## 主要功能
+
+1. **推荐关系管理**:建立和维护用户间的直推和间推关系
+2. **达人等级系统**:根据团队规模和活跃度评定用户的达人等级
+3. **团队收益分成**:计算和分配团队成员产生的收益分成
+4. **团队数据统计**:统计和展示团队规模、收益等数据
+5. **推荐码生成**:生成用户专属推荐码和推荐链接
+
+## 目录结构
+
+```
+app/Module/Promotion/
+├── AdminControllers/        # 后台管理控制器
+├── Commands/                # 命令行工具
+├── Databases/               # 数据库相关文件
+│   └── createsql/          # 数据库创建SQL
+├── Docs/                    # 详细文档目录
+│   ├── README.md            # 文档索引
+│   ├── 设计概述.md           # 模块整体设计
+│   ├── 数据库设计.md         # 数据库表结构设计
+│   ├── 模块接口.md           # 模块对外接口
+│   └── 推荐关系系统.md        # 推荐关系系统设计
+├── Enums/                   # 枚举类型定义
+├── Events/                  # 事件类
+├── Logics/                  # 业务逻辑类
+├── Models/                  # 数据模型
+├── Providers/               # 服务提供者
+├── Repositorys/             # 数据仓库
+└── Services/                # 开放服务类
+```
+
+## 核心概念
+
+### 1. 推荐关系
+
+- **直推关系**:用户A直接推荐用户B注册,则用户A是用户B的直推上级
+- **间推关系**:用户A推荐用户B,用户B推荐用户C,则用户A是用户C的间推上级
+- **团队成员**:用户的所有直推和间推下级构成该用户的团队
+
+### 2. 达人等级
+
+团队系统设有达人等级,根据团队规模和活跃度评定:
+
+| 达人等级 | 名称 | 升级条件 | 奖励权益 |
+|---------|------|---------|---------|
+| 1 | 初级达人 | 直推5人且团队总人数≥10 | 团队收益1%分成 |
+| 2 | 中级达人 | 直推10人且团队总人数≥30 | 团队收益1.5%分成 |
+| 3 | 高级达人 | 直推20人且团队总人数≥50 | 团队收益2%分成 |
+| 4 | 资深达人 | 直推30人且团队总人数≥100 | 团队收益2.5%分成 |
+| 5 | 顶级达人 | 直推50人且团队总人数≥200 | 团队收益3%分成 |
+
+### 3. 收益分成
+
+- **直推分成**:直推上级获得下级收益的5%
+- **间推分成**:间推上级根据达人等级获得下级收益的1%-3%
+- **分成范围**:间推分成仅对20代以内的团队成员有效
+- **收益来源**:主要来自团队成员播种收获的农作物收益
+
+## 核心数据表
+
+### 1. 用户推荐关系表 (promotion_user_referrals)
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | bigint | 主键ID |
+| user_id | bigint | 用户ID |
+| referrer_id | bigint | 推荐人ID |
+| level | tinyint | 推荐层级(1=直推,2=间推) |
+| created_at | timestamp | 创建时间 |
+| updated_at | timestamp | 更新时间 |
+
+### 2. 达人等级表 (promotion_user_talents)
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | bigint | 主键ID |
+| user_id | bigint | 用户ID |
+| talent_level | tinyint | 达人等级(0-5) |
+| direct_count | int | 直推人数 |
+| promotion_count | int | 团队总人数 |
+| created_at | timestamp | 创建时间 |
+| updated_at | timestamp | 更新时间 |
+
+### 3. 团队收益记录表 (promotion_profits)
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| id | bigint | 主键ID |
+| user_id | bigint | 获得收益的用户ID |
+| promotion_member_id | bigint | 团队成员ID |
+| source_id | bigint | 收益来源ID |
+| source_type | varchar | 收益来源类型 |
+| profit_amount | int | 分成收益数量 |
+| profit_rate | decimal | 分成比例 |
+| created_at | timestamp | 创建时间 |
+
+## 核心服务
+
+### 1. 推荐关系服务 (ReferralService)
+- 建立推荐关系
+- 获取用户的推荐人
+- 获取用户的团队成员
+- 验证推荐关系
+
+### 2. 达人等级服务 (TalentService)
+- 计算和更新达人等级
+- 获取用户的达人信息
+- 获取达人等级权益
+
+### 3. 团队收益服务 (PromotionProfitService)
+- 计算团队收益分成
+- 记录收益分成
+- 统计团队收益
+
+### 4. 推荐码服务 (ReferralCodeService)
+- 生成推荐码
+- 验证推荐码
+- 生成推荐链接
+
+## 与其他模块的交互
+
+### 1. 与User模块的交互
+- 用户注册时接收推荐人ID
+- 验证推荐人ID的有效性
+- 建立推荐关系
+
+### 2. 与Farm模块的交互
+- 监听作物收获事件,计算农场收益分成
+- 提供达人等级信息,影响农场产出
+
+### 3. 与GameItems模块的交互
+- 将团队分成收益添加到用户物品库
+- 记录收益来源和分成比例
+
+### 4. 与Task模块的交互
+- 达人升级可触发任务完成
+- 发展团队成员可完成特定任务
+
+## 开发注意事项
+
+1. **数据一致性**:所有涉及多个操作的功能都应使用数据库事务
+2. **性能优化**:推荐关系查询和团队成员统计应考虑性能优化
+3. **扩展性考虑**:预留达人等级和分成比例的扩展空间
+4. **安全验证**:所有操作前验证用户权限和数据有效性
+5. **日志记录**:记录关键操作,便于问题排查和数据分析
+
+## 开发文档
+
+详细的开发文档请参考以下资源:
+- [设计概述](Docs/设计概述.md) - 模块的整体设计思路和架构
+- [数据库设计](Docs/数据库设计.md) - 详细的数据库表结构和关系设计
+- [模块接口](Docs/模块接口.md) - 模块对外提供的服务接口
+- [推荐关系系统](Docs/推荐关系系统.md) - 推荐关系系统的设计与实现

+ 189 - 0
app/Module/Promotionurs/Repositorys/BaseRepository.php

@@ -0,0 +1,189 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\DB;
+
+/**
+ * 基础仓库类
+ *
+ * 提供基本的数据访问和操作功能,所有具体的仓库类都继承自此类。
+ * 该类主要用于后台管理控制器,提供数据的CRUD操作。
+ */
+class BaseRepository
+{
+    /**
+     * 模型实例
+     *
+     * @var Model
+     */
+    protected $model;
+
+    /**
+     * 构造函数
+     *
+     * @param Model $model
+     */
+    public function __construct(Model $model)
+    {
+        $this->model = $model;
+    }
+
+    /**
+     * 获取所有记录
+     *
+     * @param array $columns 要获取的字段
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function all(array $columns = ['*'])
+    {
+        return $this->model->all($columns);
+    }
+
+    /**
+     * 分页获取记录
+     *
+     * @param int $perPage 每页数量
+     * @param array $columns 要获取的字段
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     */
+    public function paginate(int $perPage = 15, array $columns = ['*'])
+    {
+        return $this->model->paginate($perPage, $columns);
+    }
+
+    /**
+     * 根据ID获取记录
+     *
+     * @param int $id 记录ID
+     * @param array $columns 要获取的字段
+     * @return Model|null
+     */
+    public function find(int $id, array $columns = ['*'])
+    {
+        return $this->model->find($id, $columns);
+    }
+
+    /**
+     * 根据条件获取第一条记录
+     *
+     * @param array $conditions 查询条件
+     * @param array $columns 要获取的字段
+     * @return Model|null
+     */
+    public function findBy(array $conditions, array $columns = ['*'])
+    {
+        $query = $this->model->query();
+
+        foreach ($conditions as $field => $value) {
+            $query->where($field, $value);
+        }
+
+        return $query->first($columns);
+    }
+
+    /**
+     * 根据条件获取多条记录
+     *
+     * @param array $conditions 查询条件
+     * @param array $columns 要获取的字段
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function findAllBy(array $conditions, array $columns = ['*'])
+    {
+        $query = $this->model->query();
+
+        foreach ($conditions as $field => $value) {
+            $query->where($field, $value);
+        }
+
+        return $query->get($columns);
+    }
+
+    /**
+     * 创建记录
+     *
+     * @param array $data 记录数据
+     * @return Model
+     */
+    public function create(array $data)
+    {
+        return $this->model->create($data);
+    }
+
+    /**
+     * 更新记录
+     *
+     * @param int $id 记录ID
+     * @param array $data 记录数据
+     * @return bool
+     */
+    public function update(int $id, array $data)
+    {
+        $record = $this->find($id);
+
+        if (!$record) {
+            return false;
+        }
+
+        return $record->update($data);
+    }
+
+    /**
+     * 删除记录
+     *
+     * @param int $id 记录ID
+     * @return bool
+     */
+    public function delete(int $id)
+    {
+        $record = $this->find($id);
+
+        if (!$record) {
+            return false;
+        }
+
+        return $record->delete();
+    }
+
+    /**
+     * 批量删除记录
+     *
+     * @param array $ids 记录ID数组
+     * @return int 删除的记录数
+     */
+    public function deleteMultiple(array $ids)
+    {
+        return $this->model->whereIn('id', $ids)->delete();
+    }
+
+    /**
+     * 获取记录总数
+     *
+     * @param array $conditions 查询条件
+     * @return int
+     */
+    public function count(array $conditions = [])
+    {
+        $query = $this->model->query();
+
+        foreach ($conditions as $field => $value) {
+            $query->where($field, $value);
+        }
+
+        return $query->count();
+    }
+
+    /**
+     * 执行原生SQL查询
+     *
+     * @param string $query SQL查询语句
+     * @param array $bindings 绑定参数
+     * @return array
+     */
+    public function rawQuery(string $query, array $bindings = [])
+    {
+        return DB::select($query, $bindings);
+    }
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamInviteRewardRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionInviteReward;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 邀请奖励记录数据仓库类
+ *
+ * 提供邀请奖励记录数据的访问和操作功能。
+ * 该类是邀请奖励记录模块与后台管理系统的桥梁,用于处理邀请奖励记录数据的CRUD操作。
+ * 邀请奖励记录了用户邀请新用户获得的奖励,包括奖励类型、奖励数量、奖励状态等信息。
+ */
+class PromotionInviteRewardRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionInviteReward::class;
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamProfitRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionProfit;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 团队收益记录数据仓库类
+ *
+ * 提供团队收益记录数据的访问和操作功能。
+ * 该类是团队收益记录模块与后台管理系统的桥梁,用于处理团队收益记录数据的CRUD操作。
+ * 团队收益记录了用户获得的团队收益,包括收益来源、收益数量、分成比例等信息。
+ */
+class PromotionProfitRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionProfit::class;
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamProfitRuleRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionProfitRule;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 收益分成规则数据仓库类
+ *
+ * 提供收益分成规则数据的访问和操作功能。
+ * 该类是收益分成规则模块与后台管理系统的桥梁,用于处理收益分成规则数据的CRUD操作。
+ * 收益分成规则定义了不同来源类型的收益分成比例和规则。
+ */
+class PromotionProfitRuleRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionProfitRule::class;
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamReferralChangeRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionReferralChange;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 推荐关系修改记录数据仓库类
+ *
+ * 提供推荐关系修改记录数据的访问和操作功能。
+ * 该类是推荐关系修改记录模块与后台管理系统的桥梁,用于处理推荐关系修改记录数据的CRUD操作。
+ * 推荐关系修改记录了用户推荐关系的变更历史,包括旧推荐人、新推荐人、修改原因等信息。
+ */
+class PromotionReferralChangeRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionReferralChange::class;
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamReferralCodeRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionReferralCode;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 推荐码数据仓库类
+ *
+ * 提供推荐码数据的访问和操作功能。
+ * 该类是推荐码模块与后台管理系统的桥梁,用于处理推荐码数据的CRUD操作。
+ * 推荐码用于用户邀请新用户加入,建立推荐关系。
+ */
+class PromotionReferralCodeRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionReferralCode::class;
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamReferralCodeUsageRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionReferralCodeUsage;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 邀请码使用记录数据仓库类
+ *
+ * 提供邀请码使用记录数据的访问和操作功能。
+ * 该类是邀请码使用记录模块与后台管理系统的桥梁,用于处理邀请码使用记录数据的CRUD操作。
+ * 邀请码使用记录了用户使用邀请码的详细信息,包括使用时间、使用结果、IP地址等。
+ */
+class PromotionReferralCodeUsageRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionReferralCodeUsage::class;
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamTalentConfigRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionTalentConfig;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 达人等级配置数据仓库类
+ *
+ * 提供达人等级配置数据的访问和操作功能。
+ * 该类是达人等级配置模块与后台管理系统的桥梁,用于处理达人等级配置数据的CRUD操作。
+ * 达人等级配置定义了不同达人等级的要求和权益。
+ */
+class PromotionTalentConfigRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionTalentConfig::class;
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamUserReferralRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionUserReferral;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 用户推荐关系数据仓库类
+ *
+ * 提供用户推荐关系数据的访问和操作功能。
+ * 该类是用户推荐关系模块与后台管理系统的桥梁,用于处理用户推荐关系数据的CRUD操作。
+ * 用户推荐关系记录了用户之间的直接推荐关系,是构建团队结构的基础。
+ */
+class PromotionUserReferralRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionUserReferral::class;
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamUserRelationCacheRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionUserRelationCache;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 用户关系缓存数据仓库类
+ *
+ * 提供用户关系缓存数据的访问和操作功能。
+ * 该类是用户关系缓存模块与后台管理系统的桥梁,用于处理用户关系缓存数据的CRUD操作。
+ * 用户关系缓存记录了用户之间的间接推荐关系,用于快速查询用户的团队结构。
+ */
+class PromotionUserRelationCacheRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionUserRelationCache::class;
+}

+ 18 - 0
app/Module/Promotionurs/Repositorys/TeamUserTalentRepository.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Module\Promotion\Repositorys;
+
+use App\Module\Promotion\Models\PromotionUserTalent;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+/**
+ * 用户达人等级数据仓库类
+ *
+ * 提供用户达人等级数据的访问和操作功能。
+ * 该类是用户达人等级模块与后台管理系统的桥梁,用于处理用户达人等级数据的CRUD操作。
+ * 用户达人等级记录了用户的达人等级、直推人数和团队总人数等信息。
+ */
+class PromotionUserTalentRepository extends EloquentRepository
+{
+    protected $eloquentClass = PromotionUserTalent::class;
+}

+ 258 - 0
app/Module/Promotionurs/Services/ReferralCodeService.php

@@ -0,0 +1,258 @@
+<?php
+
+namespace App\Module\Promotion\Services;
+
+use App\Module\Promotion\Logics\ReferralCodeLogic;
+use App\Module\Promotion\Logics\ReferralLogic;
+use App\Module\Promotion\Models\PromotionReferralCode;
+use App\Module\Promotion\Models\PromotionReferralCodeUsage;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 推荐码服务类
+ *
+ * 对外提供推荐码相关的服务,包括生成推荐码、验证推荐码、
+ * 使用推荐码等功能。该类对外提供服务,内部调用逻辑层实现。
+ */
+class ReferralCodeService
+{
+
+    /**
+     * 获取或生成用户的推荐码
+     *
+     * @param int $userId 用户ID
+     * @param \DateTime|null $expireTime 过期时间
+     * @return array|null
+     */
+    public static function getUserReferralCode(int $userId, ?\DateTime $expireTime = null): ?array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $referralCodeLogic = new ReferralCodeLogic($referralLogic);
+
+            // 先尝试获取现有的推荐码
+            $code = $referralCodeLogic->getUserActiveCode($userId);
+
+            // 如果没有有效的推荐码,则生成一个新的
+            if (!$code) {
+                $codeStr = $referralCodeLogic->generateReferralCode($userId, $expireTime);
+
+                if (!$codeStr) {
+                    return null;
+                }
+
+                $code = $referralCodeLogic->getUserActiveCode($userId);
+            }
+
+            if (!$code) {
+                return null;
+            }
+
+            return [
+                'code' => $code->code,
+                'usage_count' => $code->usage_count,
+                'status' => $code->status,
+                'expire_time' => $code->expire_time,
+                'created_at' => $code->created_at,
+                'is_valid' => $code->isValid()
+            ];
+        } catch (\Exception $e) {
+            Log::error("获取或生成用户推荐码失败: " . $e->getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 验证推荐码
+     *
+     * @param string $code 推荐码
+     * @return array
+     */
+    public static function validateReferralCode(string $code): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $referralCodeLogic = new ReferralCodeLogic($referralLogic);
+
+            return $referralCodeLogic->validateReferralCode($code);
+        } catch (\Exception $e) {
+            Log::error("验证推荐码失败: " . $e->getMessage());
+            return [
+                'valid' => false,
+                'message' => '验证推荐码时发生错误',
+                'code' => null
+            ];
+        }
+    }
+
+    /**
+     * 使用推荐码
+     *
+     * @param string $code 推荐码
+     * @param int $userId 使用者ID
+     * @param string $ipAddress IP地址
+     * @param string $userAgent 用户代理
+     * @return array
+     */
+    public static function useReferralCode(string $code, int $userId, string $ipAddress, string $userAgent): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $referralCodeLogic = new ReferralCodeLogic($referralLogic);
+
+            return $referralCodeLogic->useReferralCode($code, $userId, $ipAddress, $userAgent);
+        } catch (\Exception $e) {
+            Log::error("使用推荐码失败: " . $e->getMessage());
+            return [
+                'success' => false,
+                'message' => '使用推荐码时发生错误'
+            ];
+        }
+    }
+
+    /**
+     * 禁用推荐码
+     *
+     * @param string $code 推荐码
+     * @return bool
+     */
+    public static function disableReferralCode(string $code): bool
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $referralCodeLogic = new ReferralCodeLogic($referralLogic);
+
+            return $referralCodeLogic->disableReferralCode($code);
+        } catch (\Exception $e) {
+            Log::error("禁用推荐码失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 获取推荐码使用记录
+     *
+     * @param string $code 推荐码
+     * @param int $page 页码
+     * @param int $pageSize 每页数量
+     * @return array
+     */
+    public static function getReferralCodeUsages(string $code, int $page = 1, int $pageSize = 20): array
+    {
+        try {
+            $query = PromotionReferralCodeUsage::where('code', $code);
+
+            $total = $query->count();
+
+            $usages = $query->orderBy('created_at', 'desc')
+                ->offset(($page - 1) * $pageSize)
+                ->limit($pageSize)
+                ->with(['user', 'codeOwner'])
+                ->get();
+
+            $result = [];
+            foreach ($usages as $usage) {
+                $result[] = [
+                    'id' => $usage->id,
+                    'code' => $usage->code,
+                    'code_owner_id' => $usage->code_owner_id,
+                    'code_owner_name' => $usage->codeOwner ? ($usage->codeOwner->username ?? '') : '',
+                    'user_id' => $usage->user_id,
+                    'user_name' => $usage->user ? ($usage->user->username ?? '') : '',
+                    'ip_address' => $usage->ip_address,
+                    'user_agent' => $usage->user_agent,
+                    'status' => $usage->status,
+                    'result' => $usage->result,
+                    'remark' => $usage->remark,
+                    'created_at' => $usage->created_at
+                ];
+            }
+
+            return [
+                'total' => $total,
+                'page' => $page,
+                'page_size' => $pageSize,
+                'total_pages' => ceil($total / $pageSize),
+                'usages' => $result
+            ];
+        } catch (\Exception $e) {
+            Log::error("获取推荐码使用记录失败: " . $e->getMessage());
+            return [
+                'total' => 0,
+                'page' => $page,
+                'page_size' => $pageSize,
+                'total_pages' => 0,
+                'usages' => []
+            ];
+        }
+    }
+
+    /**
+     * 获取用户的所有推荐码
+     *
+     * @param int $userId 用户ID
+     * @param int $page 页码
+     * @param int $pageSize 每页数量
+     * @return array
+     */
+    public static function getUserAllReferralCodes(int $userId, int $page = 1, int $pageSize = 20): array
+    {
+        try {
+            $query = PromotionReferralCode::where('user_id', $userId);
+
+            $total = $query->count();
+
+            $codes = $query->orderBy('created_at', 'desc')
+                ->offset(($page - 1) * $pageSize)
+                ->limit($pageSize)
+                ->get();
+
+            $result = [];
+            foreach ($codes as $code) {
+                $result[] = [
+                    'code' => $code->code,
+                    'usage_count' => $code->usage_count,
+                    'status' => $code->status,
+                    'expire_time' => $code->expire_time,
+                    'created_at' => $code->created_at,
+                    'is_valid' => $code->isValid()
+                ];
+            }
+
+            return [
+                'total' => $total,
+                'page' => $page,
+                'page_size' => $pageSize,
+                'total_pages' => ceil($total / $pageSize),
+                'codes' => $result
+            ];
+        } catch (\Exception $e) {
+            Log::error("获取用户所有推荐码失败: " . $e->getMessage());
+            return [
+                'total' => 0,
+                'page' => $page,
+                'page_size' => $pageSize,
+                'total_pages' => 0,
+                'codes' => []
+            ];
+        }
+    }
+
+    /**
+     * 清理过期的推荐码
+     *
+     * @return int 清理的数量
+     */
+    public static function cleanExpiredReferralCodes(): int
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $referralCodeLogic = new ReferralCodeLogic($referralLogic);
+
+            return $referralCodeLogic->cleanExpiredReferralCodes();
+        } catch (\Exception $e) {
+            Log::error("清理过期推荐码失败: " . $e->getMessage());
+            return 0;
+        }
+    }
+}

+ 263 - 0
app/Module/Promotionurs/Services/ReferralService.php

@@ -0,0 +1,263 @@
+<?php
+
+namespace App\Module\Promotion\Services;
+
+use App\Module\Promotion\Logics\ReferralLogic;
+use App\Module\Promotion\Logics\RelationCacheLogic;
+use App\Module\Promotion\Logics\TalentLogic;
+use App\Module\Promotion\Models\PromotionUserReferral;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 推荐关系服务类
+ *
+ * 对外提供推荐关系相关的服务,包括获取推荐关系、设置推荐关系、
+ * 查询团队成员等功能。该类对外提供服务,内部调用逻辑层实现。
+ */
+class ReferralService
+{
+
+    /**
+     * 获取用户的直接推荐人
+     *
+     * @param int $userId 用户ID
+     * @return array|null 推荐人信息
+     */
+    public static function getUserReferrer(int $userId): ?array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $referrerId = $referralLogic->getDirectReferrerId($userId);
+
+            if (!$referrerId) {
+                return null;
+            }
+
+            // 获取推荐人信息
+            $user = app('db')->table('users')->where('id', $referrerId)->first();
+
+            if (!$user) {
+                return null;
+            }
+
+            return [
+                'user_id' => $user->id,
+                'username' => $user->username ?? '',
+                'nickname' => $user->nickname ?? '',
+                'avatar' => $user->avatar ?? '',
+                'created_at' => $user->created_at
+            ];
+        } catch (\Exception $e) {
+            Log::error("获取用户推荐人失败: " . $e->getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 获取用户的直接推荐列表
+     *
+     * @param int $userId 用户ID
+     * @param int $page 页码
+     * @param int $pageSize 每页数量
+     * @return array
+     */
+    public static function getUserDirectReferrals(int $userId, int $page = 1, int $pageSize = 20): array
+    {
+        try {
+            $query = PromotionUserReferral::where('referrer_id', $userId)
+                ->with('user');
+
+            $total = $query->count();
+
+            $referrals = $query->orderBy('created_at', 'desc')
+                ->offset(($page - 1) * $pageSize)
+                ->limit($pageSize)
+                ->get();
+
+            $result = [];
+            foreach ($referrals as $referral) {
+                if ($referral->user) {
+                    $result[] = [
+                        'user_id' => $referral->user->id,
+                        'username' => $referral->user->username ?? '',
+                        'nickname' => $referral->user->nickname ?? '',
+                        'avatar' => $referral->user->avatar ?? '',
+                        'created_at' => $referral->created_at
+                    ];
+                }
+            }
+
+            return [
+                'total' => $total,
+                'page' => $page,
+                'page_size' => $pageSize,
+                'total_pages' => ceil($total / $pageSize),
+                'referrals' => $result
+            ];
+        } catch (\Exception $e) {
+            Log::error("获取用户直接推荐列表失败: " . $e->getMessage());
+            return [
+                'total' => 0,
+                'page' => $page,
+                'page_size' => $pageSize,
+                'total_pages' => 0,
+                'referrals' => []
+            ];
+        }
+    }
+
+    /**
+     * 获取用户的团队成员列表
+     *
+     * @param int $userId 用户ID
+     * @param int $page 页码
+     * @param int $pageSize 每页数量
+     * @param int $maxLevel 最大层级
+     * @return array
+     */
+    public static function getUserPromotionMembers(int $userId, int $page = 1, int $pageSize = 20, int $maxLevel = 20): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            return $referralLogic->getAllPromotionMembers($userId, $maxLevel, $page, $pageSize);
+        } catch (\Exception $e) {
+            Log::error("获取用户团队成员列表失败: " . $e->getMessage());
+            return [
+                'total' => 0,
+                'page' => $page,
+                'page_size' => $pageSize,
+                'total_pages' => 0,
+                'members' => []
+            ];
+        }
+    }
+
+    /**
+     * 设置用户的推荐人
+     *
+     * @param int $userId 用户ID
+     * @param int $referrerId 推荐人ID
+     * @param string $reason 设置原因
+     * @param int $operatorId 操作人ID
+     * @return array 包含设置结果和消息
+     */
+    public static function setUserReferrer(int $userId, int $referrerId, string $reason, int $operatorId): array
+    {
+        try {
+            // 验证用户和推荐人是否存在
+            $user = app('db')->table('users')->where('id', $userId)->first();
+            $referrer = app('db')->table('users')->where('id', $referrerId)->first();
+
+            if (!$user) {
+                return [
+                    'success' => false,
+                    'message' => '用户不存在'
+                ];
+            }
+
+            if (!$referrer) {
+                return [
+                    'success' => false,
+                    'message' => '推荐人不存在'
+                ];
+            }
+
+            // 检查是否自己推荐自己
+            if ($userId == $referrerId) {
+                return [
+                    'success' => false,
+                    'message' => '不能设置自己为推荐人'
+                ];
+            }
+
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+
+            // 检查是否形成循环推荐
+            if ($referralLogic->checkCircularReferral($userId, $referrerId)) {
+                return [
+                    'success' => false,
+                    'message' => '设置此推荐人会形成循环推荐关系'
+                ];
+            }
+
+            // 设置推荐关系
+            $result = $referralLogic->updateReferralRelation($userId, $referrerId, $reason, $operatorId);
+
+            if (!$result) {
+                return [
+                    'success' => false,
+                    'message' => '设置推荐人失败'
+                ];
+            }
+
+            // 更新团队统计和达人等级
+            $talentLogic->updatePromotionCounts($referrerId);
+            $talentLogic->checkAndUpdateTalentLevel($referrerId);
+
+            return [
+                'success' => true,
+                'message' => '设置推荐人成功'
+            ];
+        } catch (\Exception $e) {
+            Log::error("设置用户推荐人失败: " . $e->getMessage());
+            return [
+                'success' => false,
+                'message' => '设置推荐人时发生错误: ' . $e->getMessage()
+            ];
+        }
+    }
+
+    /**
+     * 获取用户的团队统计数据
+     *
+     * @param int $userId 用户ID
+     * @return array
+     */
+    public static function getUserPromotionStats(int $userId): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            $talent = $talentLogic->getUserTalent($userId);
+
+            if (!$talent) {
+                return [
+                    'direct_count' => 0,
+                    'promotion_count' => 0,
+                    'talent_level' => 0
+                ];
+            }
+
+            return [
+                'direct_count' => $talent->direct_count,
+                'promotion_count' => $talent->promotion_count,
+                'talent_level' => $talent->talent_level
+            ];
+        } catch (\Exception $e) {
+            Log::error("获取用户团队统计数据失败: " . $e->getMessage());
+            return [
+                'direct_count' => 0,
+                'promotion_count' => 0,
+                'talent_level' => 0
+            ];
+        }
+    }
+
+    /**
+     * 重建用户的关系缓存
+     *
+     * @param int $userId 用户ID
+     * @return bool
+     */
+    public static function rebuildUserRelationCache(int $userId): bool
+    {
+        try {
+            $relationCacheLogic = new RelationCacheLogic();
+            return $relationCacheLogic->generateUserRelationCache($userId);
+        } catch (\Exception $e) {
+            Log::error("重建用户关系缓存失败: " . $e->getMessage());
+            return false;
+        }
+    }
+}

+ 264 - 0
app/Module/Promotionurs/Services/TalentService.php

@@ -0,0 +1,264 @@
+<?php
+
+namespace App\Module\Promotion\Services;
+
+use App\Module\Promotion\Logics\ReferralLogic;
+use App\Module\Promotion\Logics\TalentLogic;
+use App\Module\Promotion\Models\PromotionUserTalent;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 达人等级服务类
+ *
+ * 对外提供达人等级相关的服务,包括获取达人等级、获取达人权益、
+ * 获取达人等级配置等功能。该类对外提供服务,内部调用逻辑层实现。
+ */
+class TalentService
+{
+
+    /**
+     * 获取用户的达人等级信息
+     *
+     * @param int $userId 用户ID
+     * @return array|null
+     */
+    public static function getUserTalentInfo(int $userId): ?array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+
+            $talent = $talentLogic->getUserTalent($userId);
+
+            if (!$talent) {
+                return null;
+            }
+
+            $config = $talentLogic->getTalentConfig($talent->talent_level);
+
+            return [
+                'user_id' => $talent->user_id,
+                'talent_level' => $talent->talent_level,
+                'talent_name' => $config ? $config->name : '',
+                'direct_count' => $talent->direct_count,
+                'promotion_count' => $talent->promotion_count,
+                'icon' => $config ? $config->icon : '',
+                'icon_url' => $config ? $config->icon_url : '',
+                'benefits' => $config ? (is_array($config->benefits) ? $config->benefits : json_decode($config->benefits, true)) : [],
+                'profit_rate' => $config ? $config->profit_rate : 0.0,
+                'created_at' => $talent->created_at,
+                'updated_at' => $talent->updated_at
+            ];
+        } catch (\Exception $e) {
+            Log::error("获取用户达人等级信息失败: " . $e->getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 获取所有达人等级配置
+     *
+     * @return array
+     */
+    public static function getAllTalentConfigs(): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            $configs = $talentLogic->getAllTalentConfigs();
+
+            $result = [];
+            foreach ($configs as $config) {
+                $result[] = [
+                    'level' => $config->level,
+                    'name' => $config->name,
+                    'direct_count_required' => $config->direct_count_required,
+                    'promotion_count_required' => $config->promotion_count_required,
+                    'profit_rate' => $config->profit_rate,
+                    'benefits' => is_array($config->benefits) ? $config->benefits : json_decode($config->benefits, true),
+                    'icon' => $config->icon,
+                    'icon_url' => $config->icon_url
+                ];
+            }
+
+            return $result;
+        } catch (\Exception $e) {
+            Log::error("获取所有达人等级配置失败: " . $e->getMessage());
+            return [];
+        }
+    }
+
+    /**
+     * 获取达人等级权益
+     *
+     * @param int $level 等级
+     * @return array
+     */
+    public static function getTalentBenefits(int $level): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            return $talentLogic->getTalentBenefits($level);
+        } catch (\Exception $e) {
+            Log::error("获取达人等级权益失败: " . $e->getMessage());
+            return [];
+        }
+    }
+
+    /**
+     * 检查并更新用户的达人等级
+     *
+     * @param int $userId 用户ID
+     * @return bool
+     */
+    public static function checkAndUpdateUserTalentLevel(int $userId): bool
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            return $talentLogic->checkAndUpdateTalentLevel($userId);
+        } catch (\Exception $e) {
+            Log::error("检查并更新用户达人等级失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 获取达人等级排行榜
+     *
+     * @param int $limit 限制数量
+     * @return array
+     */
+    public static function getTalentRanking(int $limit = 10): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+
+            $talents = PromotionUserTalent::where('talent_level', '>', 0)
+                ->orderBy('talent_level', 'desc')
+                ->orderBy('promotion_count', 'desc')
+                ->orderBy('direct_count', 'desc')
+                ->limit($limit)
+                ->with('user')
+                ->get();
+
+            $result = [];
+            foreach ($talents as $talent) {
+                if ($talent->user) {
+                    $config = $talentLogic->getTalentConfig($talent->talent_level);
+
+                    $result[] = [
+                        'user_id' => $talent->user_id,
+                        'username' => $talent->user->username ?? '',
+                        'nickname' => $talent->user->nickname ?? '',
+                        'avatar' => $talent->user->avatar ?? '',
+                        'talent_level' => $talent->talent_level,
+                        'talent_name' => $config ? $config->name : '',
+                        'direct_count' => $talent->direct_count,
+                        'promotion_count' => $talent->promotion_count,
+                        'icon' => $config ? $config->icon : '',
+                        'icon_url' => $config ? $config->icon_url : ''
+                    ];
+                }
+            }
+
+            return $result;
+        } catch (\Exception $e) {
+            Log::error("获取达人等级排行榜失败: " . $e->getMessage());
+            return [];
+        }
+    }
+
+    /**
+     * 获取用户的达人等级进度
+     *
+     * @param int $userId 用户ID
+     * @return array
+     */
+    public static function getUserTalentProgress(int $userId): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+
+            $talent = $talentLogic->getUserTalent($userId);
+
+            if (!$talent) {
+                return [
+                    'current_level' => 0,
+                    'next_level' => 1,
+                    'direct_count' => 0,
+                    'direct_count_required' => 5,
+                    'direct_count_progress' => 0,
+                    'promotion_count' => 0,
+                    'promotion_count_required' => 10,
+                    'promotion_count_progress' => 0
+                ];
+            }
+
+            $configs = $talentLogic->getAllTalentConfigs();
+
+            // 找到下一个等级
+            $nextLevel = null;
+            $nextLevelConfig = null;
+
+            foreach ($configs as $config) {
+                if ($config->level > $talent->talent_level) {
+                    $nextLevel = $config->level;
+                    $nextLevelConfig = $config;
+                    break;
+                }
+            }
+
+            if (!$nextLevel) {
+                // 已经是最高等级
+                $currentConfig = $talentLogic->getTalentConfig($talent->talent_level);
+
+                return [
+                    'current_level' => $talent->talent_level,
+                    'next_level' => null,
+                    'direct_count' => $talent->direct_count,
+                    'direct_count_required' => $currentConfig ? $currentConfig->direct_count_required : 0,
+                    'direct_count_progress' => 100,
+                    'promotion_count' => $talent->promotion_count,
+                    'promotion_count_required' => $currentConfig ? $currentConfig->promotion_count_required : 0,
+                    'promotion_count_progress' => 100
+                ];
+            }
+
+            // 计算进度
+            $directCountProgress = $nextLevelConfig->direct_count_required > 0
+                ? min(100, ($talent->direct_count / $nextLevelConfig->direct_count_required) * 100)
+                : 0;
+
+            $promotionCountProgress = $nextLevelConfig->promotion_count_required > 0
+                ? min(100, ($talent->promotion_count / $nextLevelConfig->promotion_count_required) * 100)
+                : 0;
+
+            return [
+                'current_level' => $talent->talent_level,
+                'next_level' => $nextLevel,
+                'direct_count' => $talent->direct_count,
+                'direct_count_required' => $nextLevelConfig->direct_count_required,
+                'direct_count_progress' => $directCountProgress,
+                'promotion_count' => $talent->promotion_count,
+                'promotion_count_required' => $nextLevelConfig->promotion_count_required,
+                'promotion_count_progress' => $promotionCountProgress
+            ];
+        } catch (\Exception $e) {
+            Log::error("获取用户达人等级进度失败: " . $e->getMessage());
+            return [
+                'current_level' => 0,
+                'next_level' => 1,
+                'direct_count' => 0,
+                'direct_count_required' => 5,
+                'direct_count_progress' => 0,
+                'promotion_count' => 0,
+                'promotion_count_required' => 10,
+                'promotion_count_progress' => 0
+            ];
+        }
+    }
+}

+ 219 - 0
app/Module/Promotionurs/Services/TeamProfitService.php

@@ -0,0 +1,219 @@
+<?php
+
+namespace App\Module\Promotion\Services;
+
+use App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE;
+use App\Module\Promotion\Logics\ReferralLogic;
+use App\Module\Promotion\Logics\TalentLogic;
+use App\Module\Promotion\Logics\PromotionProfitLogic;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * 团队收益服务类
+ *
+ * 对外提供团队收益相关的服务,包括记录收益、获取收益记录、
+ * 统计收益等功能。该类对外提供服务,内部调用逻辑层实现。
+ */
+class PromotionProfitService
+{
+
+    /**
+     * 记录农场收获收益
+     *
+     * @param int $userId 产生收益的用户ID
+     * @param int $sourceId 收益来源ID(如收获记录ID)
+     * @param int $itemId 物品ID
+     * @param int $amount 收益数量
+     * @return bool
+     */
+    public static function recordFarmHarvestProfit(int $userId, int $sourceId, int $itemId, int $amount): bool
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            $promotionProfitLogic = new PromotionProfitLogic($referralLogic, $talentLogic);
+
+            return $promotionProfitLogic->calculateAndRecordProfit(
+                $userId,
+                PROFIT_SOURCE_TYPE::FARM_HARVEST,
+                $sourceId,
+                $itemId,
+                $amount
+            );
+        } catch (\Exception $e) {
+            Log::error("记录农场收获收益失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 记录任务完成收益
+     *
+     * @param int $userId 产生收益的用户ID
+     * @param int $sourceId 收益来源ID(如任务记录ID)
+     * @param int $itemId 物品ID
+     * @param int $amount 收益数量
+     * @return bool
+     */
+    public static function recordTaskCompleteProfit(int $userId, int $sourceId, int $itemId, int $amount): bool
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            $promotionProfitLogic = new PromotionProfitLogic($referralLogic, $talentLogic);
+
+            return $promotionProfitLogic->calculateAndRecordProfit(
+                $userId,
+                PROFIT_SOURCE_TYPE::TASK_COMPLETE,
+                $sourceId,
+                $itemId,
+                $amount
+            );
+        } catch (\Exception $e) {
+            Log::error("记录任务完成收益失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 记录物品出售收益
+     *
+     * @param int $userId 产生收益的用户ID
+     * @param int $sourceId 收益来源ID(如出售记录ID)
+     * @param int $itemId 物品ID
+     * @param int $amount 收益数量
+     * @return bool
+     */
+    public static function recordItemSellProfit(int $userId, int $sourceId, int $itemId, int $amount): bool
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            $promotionProfitLogic = new PromotionProfitLogic($referralLogic, $talentLogic);
+
+            return $promotionProfitLogic->calculateAndRecordProfit(
+                $userId,
+                PROFIT_SOURCE_TYPE::ITEM_SELL,
+                $sourceId,
+                $itemId,
+                $amount
+            );
+        } catch (\Exception $e) {
+            Log::error("记录物品出售收益失败: " . $e->getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 获取用户的收益记录
+     *
+     * @param int $userId 用户ID
+     * @param string|null $sourceType 收益来源类型
+     * @param int $page 页码
+     * @param int $pageSize 每页数量
+     * @return array
+     */
+    public static function getUserProfits(int $userId, ?string $sourceType = null, int $page = 1, int $pageSize = 20): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            $promotionProfitLogic = new PromotionProfitLogic($referralLogic, $talentLogic);
+
+            return $promotionProfitLogic->getUserProfits($userId, $sourceType, $page, $pageSize);
+        } catch (\Exception $e) {
+            Log::error("获取用户收益记录失败: " . $e->getMessage());
+            return [
+                'total' => 0,
+                'page' => $page,
+                'page_size' => $pageSize,
+                'total_pages' => 0,
+                'profits' => []
+            ];
+        }
+    }
+
+    /**
+     * 统计用户的收益
+     *
+     * @param int $userId 用户ID
+     * @param string|null $sourceType 收益来源类型
+     * @param int|null $itemId 物品ID
+     * @return array
+     */
+    public static function sumUserProfits(int $userId, ?string $sourceType = null, ?int $itemId = null): array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            $promotionProfitLogic = new PromotionProfitLogic($referralLogic, $talentLogic);
+
+            return $promotionProfitLogic->sumUserProfits($userId, $sourceType, $itemId);
+        } catch (\Exception $e) {
+            Log::error("统计用户收益失败: " . $e->getMessage());
+            return [
+                'direct_profit' => 0,
+                'indirect_profit' => 0,
+                'total_profit' => 0
+            ];
+        }
+    }
+
+    /**
+     * 获取收益分成规则
+     *
+     * @param string $sourceType 收益来源类型
+     * @return array|null
+     */
+    public static function getProfitRule(string $sourceType): ?array
+    {
+        try {
+            $referralLogic = new ReferralLogic();
+            $talentLogic = new TalentLogic($referralLogic);
+            $promotionProfitLogic = new PromotionProfitLogic($referralLogic, $talentLogic);
+
+            $rule = $promotionProfitLogic->getProfitRule($sourceType);
+
+            if (!$rule) {
+                return null;
+            }
+
+            return [
+                'source_type' => $rule->source_type,
+                'direct_profit_rate' => $rule->direct_profit_rate,
+                'max_indirect_level' => $rule->max_indirect_level,
+                'status' => $rule->status,
+                'rules' => is_array($rule->rules) ? $rule->rules : json_decode($rule->rules, true)
+            ];
+        } catch (\Exception $e) {
+            Log::error("获取收益分成规则失败: " . $e->getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 获取所有收益来源类型
+     *
+     * @return array
+     */
+    public static function getAllProfitSourceTypes(): array
+    {
+        return [
+            [
+                'type' => PROFIT_SOURCE_TYPE::FARM_HARVEST,
+                'name' => '农场收获',
+                'description' => '来自Farm模块的作物收获'
+            ],
+            [
+                'type' => PROFIT_SOURCE_TYPE::TASK_COMPLETE,
+                'name' => '任务完成',
+                'description' => '来自Task模块的任务奖励'
+            ],
+            [
+                'type' => PROFIT_SOURCE_TYPE::ITEM_SELL,
+                'name' => '物品出售',
+                'description' => '来自GameItems模块的物品出售'
+            ]
+        ];
+    }
+}

+ 35 - 0
prompts/memories.md

@@ -0,0 +1,35 @@
+<analysis>
+由于目前对话历史为空,没有之前的消息可供分析。这意味着:
+
+1. 没有用户请求或意图需要捕捉
+2. 没有技术概念、代码或文件被讨论
+3. 没有解决的问题或待处理任务
+4. 没有当前工作进行中
+5. 没有明确的下一步操作
+
+这是一个全新的对话起点,需要等待用户的第一条消息才能开始分析和创建有意义的摘要。
+</analysis>
+
+<summary>
+1. 主要请求和意图:
+   [无 - 对话尚未开始]
+
+2. 关键技术概念:
+   [无 - 尚未讨论技术内容]
+
+3. 文件和代码部分:
+   [无 - 尚未涉及任何文件或代码]
+
+4. 问题解决:
+   [无 - 尚未提出问题或解决方案]
+
+5. 待处理任务:
+   [无 - 尚未分配任务]
+
+6. 当前工作:
+   [无 - 没有进行中的工作]
+
+7. 可选下一步:
+   [等待用户提供初始请求或问题]
+
+</summary>