Selaa lähdekoodia

实现URS推广模块活跃用户功能

核心功能:
- 活跃用户定义:最近15天操作活跃的用户(基于last_activity_time)
- 数据存储:用户绑定关系表新增活跃状态相关字段
- 达人等级:新增活跃人数作为升级条件

数据库扩展:
- 用户映射表新增is_active、last_activity_check、active_days_count字段
- 达人等级配置表新增active_count_required、active_direct_required字段
- 创建相关索引优化查询性能
- 提供完整的数据库迁移脚本

服务层实现:
- 新增UrsActiveUserService核心服务类,处理活跃用户逻辑
- 扩展UrsTalentService,集成活跃用户条件到等级计算
- 扩展UrsUserMappingService,添加活跃用户相关方法
- 更新相关模型,添加活跃用户字段和方法

定时任务:
- 新增UrsUpdateActiveStatusCommand每日更新命令
- 支持批量更新、试运行、重置等多种模式
- 完善日志记录和错误处理机制

后台管理:
- 用户映射关系管理界面显示活跃状态信息
- 达人等级配置支持活跃人数设置
- 添加活跃状态筛选和查询功能

测试验证:
- 新增TestActiveUserCommand测试命令
- 支持统计、更新、等级计算等功能测试

文档完善:
- 创建活跃用户功能专门文档
- 更新用户绑定关系文档
- 完善开发进度记录
notfff 6 kuukautta sitten
vanhempi
commit
3ff81ade0f

+ 187 - 0
AiWork/202506/161547-维护URS推广模块用户绑定关系文档.md

@@ -0,0 +1,187 @@
+# 维护URS推广模块用户绑定关系文档
+
+**任务时间**: 2025年06月16日 15:47  
+**任务类型**: 文档维护  
+**模块**: UrsPromotion  
+
+## 任务概述
+
+维护URS推广模块的用户绑定关系文档,完善用户映射关系的设计说明、使用示例和开发指南。
+
+## 任务背景
+
+URS推广模块采用分离映射关系设计,URS用户与农场用户通过映射表建立一对一关系。用户绑定关系是整个URS推广系统的核心基础功能,需要完善的文档来指导开发和使用。
+
+## 完成内容
+
+### 1. 数据库设计文档更新
+
+**文件**: `app/Module/UrsPromotion/Docs/数据库设计.md`
+
+**更新内容**:
+- 添加用户映射表(urs_promotion_user_mappings)的详细设计
+- 更新数据库表概览,将用户映射表列为核心基础表
+- 调整表结构编号,用户映射表作为2.1节
+- 补充用户映射表的索引设计和业务规则
+- 更新主要查询场景,增加用户映射查询
+- 补充数据完整性约束中的映射关系约束
+
+**关键设计要点**:
+- 一对一映射关系:确保URS用户与农场用户的唯一映射
+- 唯一索引设计:防止重复映射
+- 业务约束:映射关系一旦建立不允许修改,只能禁用
+- 支持自动创建农场用户并建立映射关系
+
+### 2. 创建用户绑定关系专门文档
+
+**文件**: `app/Module/UrsPromotion/Docs/用户绑定关系.md` (新增)
+
+**文档结构**:
+1. **概述** - 功能定位和设计原则
+2. **数据结构** - 用户映射表结构和状态定义
+3. **服务层接口** - UrsUserMappingService完整接口说明
+4. **业务流程** - 用户进入农场和自动创建用户的流程图
+5. **使用示例** - 基础使用、自动创建、批量操作示例
+6. **后台管理功能** - 管理界面功能说明
+7. **错误处理** - 常见异常和处理方法
+8. **性能优化** - 缓存策略和数据库优化建议
+9. **监控和日志** - 操作日志和统计监控
+10. **最佳实践** - 开发建议和注意事项
+
+**核心功能说明**:
+- 映射关系建立:用户进入农场时建立映射关系
+- 双向查询:支持URS用户ID和农场用户ID的双向查询
+- 批量操作:支持批量查询和处理映射关系
+- 状态管理:支持映射关系的启用、禁用状态管理
+- 自动创建:不存在映射关系时自动创建新用户并建立映射
+- 后台管理:提供完整的后台管理界面
+
+### 3. 服务层使用示例文档更新
+
+**文件**: `app/Module/UrsPromotion/Docs/服务层使用示例.md`
+
+**更新内容**:
+- 大幅扩展UrsUserMappingService的使用示例
+- 添加基础映射操作示例
+- 补充自动创建用户功能的详细说明
+- 增加批量操作示例
+- 添加状态管理示例
+- 补充统计和详情查询示例
+
+**示例分类**:
+1. **基础映射操作** - 创建映射、检查状态、双向查询
+2. **自动创建用户功能** - 获取或创建农场用户,用户名格式说明
+3. **批量操作** - 批量获取映射关系、批量状态检查
+4. **状态管理** - 启用/禁用映射关系
+5. **统计和详情查询** - 获取映射详情和统计信息
+
+### 4. 设计概述文档更新
+
+**文件**: `app/Module/UrsPromotion/Docs/设计概述.md`
+
+**更新内容**:
+- 在核心组件中添加UrsUserMappingService作为核心基础服务
+- 补充UrsUserMappingLogic、UrsUserMappingRepository、UrsUserMapping模型
+- 添加用户绑定关系建立流程(4.1节)
+- 更新核心实体,将用户映射关系列为核心基础实体
+- 更新关系设计,补充映射关系与其他实体的关系
+
+**核心流程补充**:
+```
+URS用户进入农场 → 检查映射关系 → 建立/验证映射 → 更新相关数据 → 触发相关事件
+```
+
+### 5. README文档更新
+
+**文件**: `app/Module/UrsPromotion/README.md`
+
+**更新内容**:
+- 在开发文档部分添加用户绑定关系文档链接
+- 补充服务层使用示例文档链接
+- 完善文档索引结构
+
+### 6. DEV文档更新
+
+**文件**: `app/Module/UrsPromotion/Docs/DEV.md`
+
+**更新内容**:
+- 记录用户绑定关系文档维护的详细进度
+- 列出所有更新的文档文件
+- 总结技术要点和设计原则
+- 保留历史记录
+
+## 技术要点总结
+
+### 1. 分离映射关系设计
+- URS推广关系只存储URS用户ID
+- 通过映射表关联农场用户
+- 实现两个系统的数据隔离
+
+### 2. 一对一映射约束
+- 一个URS用户只能映射一个农场用户
+- 一个农场用户只能映射一个URS用户
+- 通过唯一索引确保数据完整性
+
+### 3. 自动创建机制
+- 支持在建立映射关系时自动创建农场用户
+- 用户名格式:urs-{ursUserId}
+- 密码随机生成,自动激活
+
+### 4. 完整的后台管理
+- 提供映射关系的查看、筛选功能
+- 支持状态管理(启用/禁用)
+- 提供到推荐关系和达人等级的快速链接
+- 包含同步信息和验证映射的自定义操作
+
+### 5. 服务层设计
+- 所有方法均为静态方法,使用简单
+- 支持批量操作,提高性能
+- 完整的异常处理和日志记录
+- 事务保护确保数据一致性
+
+## 文档结构
+
+更新后的文档结构:
+
+```
+app/Module/UrsPromotion/Docs/
+├── 设计概述.md (已更新)
+├── 数据库设计.md (已更新)
+├── 用户绑定关系.md (新增)
+├── 服务层使用示例.md (已更新)
+├── DEV.md (已更新)
+└── README.md (已更新)
+```
+
+## 提交信息
+
+**Git提交**: `10c7f44d`  
+**提交信息**: 维护URS推广模块用户绑定关系文档
+
+**变更文件**:
+- 修改:数据库设计.md
+- 修改:服务层使用示例.md  
+- 修改:设计概述.md
+- 修改:README.md
+- 修改:DEV.md
+- 新增:用户绑定关系.md
+
+## 任务价值
+
+1. **完善文档体系** - 为URS推广模块的核心基础功能提供完整的文档支持
+2. **指导开发使用** - 详细的接口说明和使用示例帮助开发者正确使用服务
+3. **规范业务流程** - 明确用户绑定关系的建立和管理流程
+4. **提升维护效率** - 完整的文档降低后续维护和扩展的成本
+5. **保证数据一致性** - 明确的约束和规则确保系统数据的完整性
+
+## 后续建议
+
+1. **定期更新文档** - 随着功能迭代及时更新相关文档
+2. **补充测试用例** - 为用户绑定关系功能编写完整的测试用例
+3. **性能监控** - 建立映射关系操作的性能监控和告警机制
+4. **用户体验优化** - 根据使用反馈优化自动创建用户的流程
+
+---
+
+**任务完成时间**: 2025年06月16日 15:47  
+**文档维护状态**: ✅ 已完成

+ 11 - 0
AiWork/WORK.md

@@ -7,6 +7,17 @@
 
 ## 已完成任务
 
+**2025-06-16 15:47** - 维护URS推广模块用户绑定关系文档 - 完善用户映射关系的设计说明和使用指南
+- 任务:维护URS推广模块的用户绑定关系文档,完善用户映射关系的设计说明、使用示例和开发指南
+- 数据库:更新数据库设计文档,添加用户映射表详细设计,补充索引设计和业务规则
+- 专门文档:创建用户绑定关系专门文档,包含功能概述、服务层接口、业务流程、使用示例等完整内容
+- 服务示例:大幅扩展服务层使用示例,补充UrsUserMappingService的详细用法和批量操作示例
+- 设计概述:更新设计概述文档,补充用户绑定关系的核心组件和流程说明
+- README:更新README文档,添加用户绑定关系文档链接,完善开发文档索引
+- DEV记录:更新DEV文档,记录用户绑定关系文档维护的详细进度和技术要点
+- 技术要点:分离映射关系设计、一对一映射约束、自动创建机制、完整的后台管理功能
+- 文件:./AiWork/202506/161547-维护URS推广模块用户绑定关系文档.md
+
 **2025-06-16 15:21** - 修复UserInfo模型时间字段类型转换问题 - 解决UserActivityService返回类型不一致问题
 - 任务:修复通过debug:reproduce-error命令验证时出现的UserInfo模型时间字段类型转换问题
 - 问题:UserActivityService::getLastActivityTime方法声明返回?Carbon,但实际可能返回字符串,导致类型不匹配

+ 15 - 0
app/Module/UrsPromotion/AdminControllers/UrsTalentConfigController.php

@@ -55,6 +55,8 @@ class UrsTalentConfigController extends AdminController
             $grid->column('name', '等级名称');
             $grid->column('direct_count_required', '所需直推人数')->sortable();
             $grid->column('promotion_count_required', '所需团队总人数')->sortable();
+            $grid->column('active_direct_required', '所需活跃直推')->sortable();
+            $grid->column('active_count_required', '所需活跃团队')->sortable();
             
             $grid->column('promotion_direct_group', '直推奖励组');
             $grid->column('promotion_indirect_group', '间推奖励组');
@@ -96,6 +98,11 @@ class UrsTalentConfigController extends AdminController
             $show->field('name', '等级名称');
             $show->field('direct_count_required', '所需直推人数');
             $show->field('promotion_count_required', '所需团队总人数');
+
+            // 活跃用户要求
+            $show->divider('活跃用户要求');
+            $show->field('active_direct_required', '所需活跃直推人数');
+            $show->field('active_count_required', '所需活跃团队人数');
             
             $show->field('promotion_direct_group', '直推奖励组ID')->as(function ($value) {
                 return $value ?: '无';
@@ -140,6 +147,14 @@ class UrsTalentConfigController extends AdminController
             $form->text('name', '等级名称')->required();
             $form->number('direct_count_required', '所需直推人数')->default(0)->min(0);
             $form->number('promotion_count_required', '所需团队总人数')->default(0)->min(0);
+
+            // 活跃用户要求配置
+            $form->fieldset('活跃用户要求', function (Form $form) {
+                $form->number('active_direct_required', '所需活跃直推人数')->default(0)->min(0)
+                    ->help('要求的活跃直推人数(最近15天有活动)');
+                $form->number('active_count_required', '所需活跃团队人数')->default(0)->min(0)
+                    ->help('要求的活跃团队总人数(最近15天有活动)');
+            });
             
             // 推广收益奖励组配置
             $form->fieldset('推广收益奖励组配置', function (Form $form) {

+ 23 - 0
app/Module/UrsPromotion/AdminControllers/UrsUserMappingController.php

@@ -66,6 +66,20 @@ class UrsUserMappingController extends AdminController
                 UrsUserMapping::STATUS_INVALID => 'danger',
                 UrsUserMapping::STATUS_VALID => 'success',
             ]);
+
+            $grid->column('is_active', '活跃状态')->using(UrsUserMapping::$activeMap)->label([
+                UrsUserMapping::ACTIVE_NO => 'warning',
+                UrsUserMapping::ACTIVE_YES => 'success',
+            ]);
+
+            $grid->column('active_days_count', '活跃天数')->display(function ($value) {
+                return $value > 0 ? $value : '<span class="text-muted">0</span>';
+            });
+
+            $grid->column('last_activity_check', '最后检查时间')->display(function ($value) {
+                return $value ? $value : '<span class="text-muted">从未检查</span>';
+            });
+
             $grid->column('created_at', '创建时间')->sortable();
 
             // 禁用创建按钮(映射关系通过系统自动创建)
@@ -89,7 +103,9 @@ class UrsUserMappingController extends AdminController
                     UrsUserMapping::STATUS_INVALID => '无效',
                     UrsUserMapping::STATUS_VALID => '有效',
                 ]);
+                $filter->equal('is_active', '活跃状态')->select(UrsUserMapping::$activeMap);
                 $filter->between('mapping_time', '绑定时间')->datetime();
+                $filter->between('last_activity_check', '最后检查时间')->datetime();
                 $filter->between('created_at', '创建时间')->datetime();
             });
 
@@ -112,6 +128,13 @@ class UrsUserMappingController extends AdminController
                 UrsUserMapping::STATUS_INVALID => '无效',
                 UrsUserMapping::STATUS_VALID => '有效',
             ]);
+
+            // 活跃用户信息
+            $show->divider('活跃用户信息');
+            $show->field('is_active', '活跃状态')->using(UrsUserMapping::$activeMap);
+            $show->field('active_days_count', '活跃天数');
+            $show->field('last_activity_check', '最后检查时间');
+
             $show->field('created_at', '创建时间');
             $show->field('updated_at', '更新时间');
 

+ 261 - 0
app/Module/UrsPromotion/Commands/TestActiveUserCommand.php

@@ -0,0 +1,261 @@
+<?php
+
+namespace App\Module\UrsPromotion\Commands;
+
+use App\Module\UrsPromotion\Services\UrsActiveUserService;
+use App\Module\UrsPromotion\Services\UrsUserMappingService;
+use App\Module\UrsPromotion\Services\UrsTalentService;
+use App\Module\UrsPromotion\Models\UrsUserMapping;
+use App\Module\UrsPromotion\Models\UrsTalentConfig;
+use Illuminate\Console\Command;
+
+/**
+ * URS活跃用户功能测试命令
+ */
+class TestActiveUserCommand extends Command
+{
+    /**
+     * 命令签名
+     */
+    protected $signature = 'urs:test-active-user 
+                            {--user-id= : 测试指定的URS用户ID}
+                            {--stats : 显示活跃用户统计}
+                            {--talent : 测试达人等级计算}
+                            {--update : 测试活跃状态更新}';
+
+    /**
+     * 命令描述
+     */
+    protected $description = 'Test URS active user functionality';
+
+    /**
+     * 执行命令
+     */
+    public function handle()
+    {
+        $this->info('🧪 URS活跃用户功能测试');
+        $this->line('');
+
+        // 显示活跃用户统计
+        if ($this->option('stats')) {
+            $this->testActiveUserStats();
+        }
+
+        // 测试活跃状态更新
+        if ($this->option('update')) {
+            $this->testActiveStatusUpdate();
+        }
+
+        // 测试达人等级计算
+        if ($this->option('talent')) {
+            $this->testTalentCalculation();
+        }
+
+        // 测试指定用户
+        if ($userId = $this->option('user-id')) {
+            $this->testSpecificUser((int) $userId);
+        }
+
+        // 如果没有指定选项,运行完整测试
+        if (!$this->option('stats') && !$this->option('update') && 
+            !$this->option('talent') && !$this->option('user-id')) {
+            $this->runFullTest();
+        }
+
+        $this->info('✅ 测试完成');
+    }
+
+    /**
+     * 测试活跃用户统计
+     */
+    protected function testActiveUserStats(): void
+    {
+        $this->info('📊 活跃用户统计测试');
+        
+        $stats = UrsActiveUserService::getActiveUserStats();
+        $detailedStats = UrsActiveUserService::getDetailedActiveStats();
+        
+        $this->table(['指标', '数值'], [
+            ['总用户数', $stats['total_users']],
+            ['活跃用户数', $stats['active_users']],
+            ['不活跃用户数', $stats['inactive_users']],
+            ['活跃比例', $stats['active_percentage'] . '%'],
+            ['最近24小时更新', $detailedStats['recent_updates']],
+            ['需要检查的用户', $detailedStats['need_check_count']],
+            ['最后更新时间', $detailedStats['last_update_time'] ?? '从未更新'],
+        ]);
+        
+        $this->line('');
+    }
+
+    /**
+     * 测试活跃状态更新
+     */
+    protected function testActiveStatusUpdate(): void
+    {
+        $this->info('🔄 活跃状态更新测试');
+        
+        // 获取需要检查的用户
+        $needCheckUsers = UrsUserMapping::getUsersNeedActivityCheck(5);
+        
+        if ($needCheckUsers->isEmpty()) {
+            $this->warn('没有需要检查的用户');
+            return;
+        }
+        
+        $this->info("找到 {$needCheckUsers->count()} 个需要检查的用户");
+        
+        foreach ($needCheckUsers as $mapping) {
+            $this->line("测试用户 URS:{$mapping->urs_user_id} Farm:{$mapping->user_id}");
+            
+            $before = $mapping->is_active;
+            $result = UrsActiveUserService::updateUserActiveStatus($mapping->urs_user_id);
+            
+            $mapping->refresh();
+            $after = $mapping->is_active;
+            
+            $status = $result ? '✅' : '❌';
+            $change = $before !== $after ? " ({$before} -> {$after})" : '';
+            
+            $this->line("  {$status} 更新结果: " . ($result ? '成功' : '失败') . $change);
+        }
+        
+        $this->line('');
+    }
+
+    /**
+     * 测试达人等级计算
+     */
+    protected function testTalentCalculation(): void
+    {
+        $this->info('🏆 达人等级计算测试');
+        
+        // 获取等级配置
+        $configs = UrsTalentConfig::where('status', 1)->orderBy('level')->get();
+        
+        $this->info('达人等级配置:');
+        $headers = ['等级', '名称', '直推要求', '团队要求', '活跃直推要求', '活跃团队要求'];
+        $rows = [];
+        
+        foreach ($configs as $config) {
+            $rows[] = [
+                $config->level,
+                $config->name,
+                $config->direct_count_required,
+                $config->promotion_count_required,
+                $config->active_direct_required,
+                $config->active_count_required,
+            ];
+        }
+        
+        $this->table($headers, $rows);
+        
+        // 测试几个用户的等级计算
+        $testUsers = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
+            ->limit(3)
+            ->get();
+            
+        if ($testUsers->isNotEmpty()) {
+            $this->info('用户等级检查示例:');
+            
+            foreach ($testUsers as $mapping) {
+                $this->line("用户 URS:{$mapping->urs_user_id}");
+                
+                try {
+                    $eligibility = UrsTalentService::checkUpgradeEligibility($mapping->urs_user_id);
+                    $activeStats = UrsActiveUserService::getActiveTeamMembers($mapping->urs_user_id);
+                    
+                    $this->line("  活跃直推: {$activeStats['active_direct_count']}");
+                    $this->line("  活跃团队: {$activeStats['active_total_count']}");
+                    $this->line("  升级资格: " . ($eligibility['eligible'] ? '✅ 符合' : '❌ 不符合'));
+                    
+                    if (!$eligibility['eligible'] && isset($eligibility['requirements'])) {
+                        foreach ($eligibility['requirements'] as $type => $req) {
+                            if (!$req['met']) {
+                                $this->line("    缺少 {$type}: {$req['gap']}");
+                            }
+                        }
+                    }
+                    
+                } catch (\Exception $e) {
+                    $this->error("  检查失败: " . $e->getMessage());
+                }
+                
+                $this->line('');
+            }
+        }
+    }
+
+    /**
+     * 测试指定用户
+     */
+    protected function testSpecificUser(int $ursUserId): void
+    {
+        $this->info("👤 测试用户 URS:{$ursUserId}");
+        
+        // 检查映射关系
+        $mapping = UrsUserMapping::where('urs_user_id', $ursUserId)
+            ->where('status', UrsUserMapping::STATUS_VALID)
+            ->first();
+            
+        if (!$mapping) {
+            $this->error('用户映射关系不存在');
+            return;
+        }
+        
+        $this->line("农场用户ID: {$mapping->user_id}");
+        $this->line("当前活跃状态: " . ($mapping->isActive() ? '活跃' : '不活跃'));
+        $this->line("活跃天数: {$mapping->active_days_count}");
+        $this->line("最后检查时间: " . ($mapping->last_activity_check ? $mapping->last_activity_check->format('Y-m-d H:i:s') : '从未检查'));
+        
+        // 更新活跃状态
+        $this->line('');
+        $this->info('更新活跃状态...');
+        $result = UrsActiveUserService::updateUserActiveStatus($ursUserId);
+        $this->line('更新结果: ' . ($result ? '成功' : '失败'));
+        
+        // 获取活跃团队统计
+        $this->line('');
+        $this->info('活跃团队统计:');
+        $activeStats = UrsActiveUserService::getActiveTeamMembers($ursUserId);
+        $this->line("活跃直推: {$activeStats['active_direct_count']}");
+        $this->line("活跃团队: {$activeStats['active_total_count']}");
+        
+        // 检查升级条件
+        $this->line('');
+        $this->info('升级条件检查:');
+        try {
+            $eligibility = UrsTalentService::checkUpgradeEligibility($ursUserId);
+            $this->line("升级资格: " . ($eligibility['eligible'] ? '✅ 符合' : '❌ 不符合'));
+            
+            if (isset($eligibility['next_level_name'])) {
+                $this->line("下一等级: {$eligibility['next_level_name']}");
+            }
+            
+            if (isset($eligibility['requirements'])) {
+                foreach ($eligibility['requirements'] as $type => $req) {
+                    $status = $req['met'] ? '✅' : '❌';
+                    $this->line("  {$status} {$type}: {$req['current']}/{$req['required']}");
+                }
+            }
+            
+        } catch (\Exception $e) {
+            $this->error('检查失败: ' . $e->getMessage());
+        }
+    }
+
+    /**
+     * 运行完整测试
+     */
+    protected function runFullTest(): void
+    {
+        $this->info('🚀 运行完整测试');
+        $this->line('');
+        
+        $this->testActiveUserStats();
+        $this->testActiveStatusUpdate();
+        $this->testTalentCalculation();
+        
+        $this->info('完整测试完成');
+    }
+}

+ 227 - 0
app/Module/UrsPromotion/Commands/UrsUpdateActiveStatusCommand.php

@@ -0,0 +1,227 @@
+<?php
+
+namespace App\Module\UrsPromotion\Commands;
+
+use App\Module\UrsPromotion\Services\UrsActiveUserService;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Log;
+
+/**
+ * URS用户活跃状态更新命令
+ * 
+ * 每日定时执行,更新所有URS用户的活跃状态
+ */
+class UrsUpdateActiveStatusCommand extends Command
+{
+    /**
+     * 命令签名
+     */
+    protected $signature = 'urs:update-active-status 
+                            {--limit=1000 : 每次处理的用户数量限制}
+                            {--reset : 重置所有用户活跃状态}
+                            {--dry-run : 仅显示统计信息,不执行更新}';
+
+    /**
+     * 命令描述
+     */
+    protected $description = 'Update URS users active status based on last activity time';
+
+    /**
+     * 执行命令
+     */
+    public function handle()
+    {
+        $this->info('开始执行URS用户活跃状态更新任务...');
+        $startTime = microtime(true);
+
+        try {
+            // 检查是否为重置模式
+            if ($this->option('reset')) {
+                return $this->handleReset();
+            }
+
+            // 检查是否为试运行模式
+            if ($this->option('dry-run')) {
+                return $this->handleDryRun();
+            }
+
+            // 执行正常的活跃状态更新
+            return $this->handleUpdate();
+
+        } catch (\Exception $e) {
+            $this->error('执行失败:' . $e->getMessage());
+            Log::error('URS用户活跃状态更新命令执行失败', [
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+            return 1;
+        } finally {
+            $duration = round(microtime(true) - $startTime, 2);
+            $this->info("任务执行完成,耗时:{$duration}秒");
+        }
+    }
+
+    /**
+     * 处理正常的活跃状态更新
+     */
+    protected function handleUpdate(): int
+    {
+        $limit = (int) $this->option('limit');
+        $this->info("开始批量更新用户活跃状态(限制:{$limit})...");
+
+        // 显示更新前的统计信息
+        $beforeStats = UrsActiveUserService::getDetailedActiveStats();
+        $this->displayStats('更新前统计', $beforeStats);
+
+        // 执行批量更新
+        $updateStats = UrsActiveUserService::batchUpdateActiveStatus($limit);
+        $this->displayUpdateStats($updateStats);
+
+        // 显示更新后的统计信息
+        $afterStats = UrsActiveUserService::getDetailedActiveStats();
+        $this->displayStats('更新后统计', $afterStats);
+
+        // 记录执行日志
+        Log::info('URS用户活跃状态更新任务完成', [
+            'before_stats' => $beforeStats,
+            'update_stats' => $updateStats,
+            'after_stats' => $afterStats
+        ]);
+
+        $this->info('✅ 活跃状态更新完成');
+        return 0;
+    }
+
+    /**
+     * 处理重置模式
+     */
+    protected function handleReset(): int
+    {
+        if (!$this->confirm('确定要重置所有用户的活跃状态吗?此操作不可逆!')) {
+            $this->info('操作已取消');
+            return 0;
+        }
+
+        $this->info('开始重置所有用户活跃状态...');
+        
+        $result = UrsActiveUserService::resetAllActiveStatus();
+        
+        if ($result['success']) {
+            $this->info("✅ 重置完成,共更新 {$result['updated_count']} 个用户");
+            Log::info('URS用户活跃状态重置完成', $result);
+        } else {
+            $this->error("❌ 重置失败:{$result['message']}");
+            return 1;
+        }
+
+        return 0;
+    }
+
+    /**
+     * 处理试运行模式
+     */
+    protected function handleDryRun(): int
+    {
+        $this->info('试运行模式:仅显示统计信息,不执行更新');
+        
+        $stats = UrsActiveUserService::getDetailedActiveStats();
+        $this->displayStats('当前统计', $stats);
+
+        // 显示需要检查的用户示例
+        if ($stats['need_check_count'] > 0) {
+            $this->info("\n需要检查活跃状态的用户示例:");
+            $needCheckUsers = \App\Module\UrsPromotion\Models\UrsUserMapping::getUsersNeedActivityCheck(5);
+            
+            $headers = ['URS用户ID', '农场用户ID', '上次检查时间', '用户最后活动时间'];
+            $rows = [];
+            
+            foreach ($needCheckUsers as $mapping) {
+                $rows[] = [
+                    $mapping->urs_user_id,
+                    $mapping->user_id,
+                    $mapping->last_activity_check ? $mapping->last_activity_check->format('Y-m-d H:i:s') : '从未检查',
+                    $mapping->user && $mapping->user->last_activity_time 
+                        ? $mapping->user->last_activity_time->format('Y-m-d H:i:s') 
+                        : '无活动记录'
+                ];
+            }
+            
+            $this->table($headers, $rows);
+        }
+
+        return 0;
+    }
+
+    /**
+     * 显示统计信息
+     */
+    protected function displayStats(string $title, array $stats): void
+    {
+        $this->info("\n📊 {$title}:");
+        $this->line("总用户数:{$stats['total_users']}");
+        $this->line("活跃用户:{$stats['active_users']}");
+        $this->line("不活跃用户:{$stats['inactive_users']}");
+        $this->line("活跃比例:{$stats['active_percentage']}%");
+        
+        if (isset($stats['recent_updates'])) {
+            $this->line("最近24小时更新:{$stats['recent_updates']}");
+        }
+        
+        if (isset($stats['need_check_count'])) {
+            $this->line("需要检查的用户:{$stats['need_check_count']}");
+        }
+        
+        if (isset($stats['last_update_time'])) {
+            $lastUpdate = $stats['last_update_time'] 
+                ? \Carbon\Carbon::parse($stats['last_update_time'])->format('Y-m-d H:i:s')
+                : '从未更新';
+            $this->line("最后更新时间:{$lastUpdate}");
+        }
+    }
+
+    /**
+     * 显示更新统计信息
+     */
+    protected function displayUpdateStats(array $stats): void
+    {
+        $this->info("\n🔄 更新统计:");
+        $this->line("处理总数:{$stats['total_processed']}");
+        $this->line("成功更新:{$stats['successful_updates']}");
+        $this->line("失败数量:{$stats['failed_updates']}");
+        $this->line("新增活跃:{$stats['active_users']}");
+        $this->line("新增不活跃:{$stats['inactive_users']}");
+        
+        if ($stats['failed_updates'] > 0) {
+            $this->warn("⚠️  有 {$stats['failed_updates']} 个用户更新失败,请检查日志");
+        }
+    }
+
+    /**
+     * 获取命令帮助信息
+     */
+    public function getHelp(): string
+    {
+        return <<<HELP
+URS用户活跃状态更新命令
+
+用法:
+  php artisan urs:update-active-status [选项]
+
+选项:
+  --limit=1000     每次处理的用户数量限制(默认1000)
+  --reset          重置所有用户活跃状态
+  --dry-run        仅显示统计信息,不执行更新
+
+示例:
+  php artisan urs:update-active-status                    # 正常更新
+  php artisan urs:update-active-status --limit=500        # 限制处理500个用户
+  php artisan urs:update-active-status --dry-run          # 试运行模式
+  php artisan urs:update-active-status --reset            # 重置所有状态
+
+说明:
+  - 活跃用户定义:最近15天有活动的用户
+  - 建议每日凌晨执行此命令
+  - 可以通过crontab设置定时任务:0 2 * * * php artisan urs:update-active-status
+HELP;
+    }
+}

+ 126 - 0
app/Module/UrsPromotion/Database/add_active_user_fields.sql

@@ -0,0 +1,126 @@
+-- URS推广模块活跃用户功能数据库扩展脚本
+-- 创建时间: 2025-06-16
+-- 功能: 为URS推广模块添加活跃用户概念支持
+
+-- =====================================================
+-- 1. 扩展用户映射表,添加活跃用户相关字段
+-- =====================================================
+
+-- 添加活跃状态字段
+ALTER TABLE `kku_urs_promotion_user_mappings` 
+ADD COLUMN `is_active` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否活跃:1活跃,0不活跃' AFTER `status`;
+
+-- 添加最后活跃检查时间字段
+ALTER TABLE `kku_urs_promotion_user_mappings` 
+ADD COLUMN `last_activity_check` TIMESTAMP NULL COMMENT '最后活跃检查时间' AFTER `is_active`;
+
+-- 添加活跃天数统计字段
+ALTER TABLE `kku_urs_promotion_user_mappings` 
+ADD COLUMN `active_days_count` INT NOT NULL DEFAULT 0 COMMENT '活跃天数统计' AFTER `last_activity_check`;
+
+-- 添加活跃状态索引
+ALTER TABLE `kku_urs_promotion_user_mappings` 
+ADD INDEX `idx_is_active` (`is_active`);
+
+-- 添加活跃检查时间索引
+ALTER TABLE `kku_urs_promotion_user_mappings` 
+ADD INDEX `idx_last_activity_check` (`last_activity_check`);
+
+-- =====================================================
+-- 2. 扩展达人等级配置表,添加活跃人数条件
+-- =====================================================
+
+-- 添加所需活跃人数字段
+ALTER TABLE `kku_urs_promotion_talent_configs` 
+ADD COLUMN `active_count_required` INT NOT NULL DEFAULT 0 COMMENT '所需活跃人数' AFTER `promotion_count_required`;
+
+-- 添加所需直推活跃人数字段
+ALTER TABLE `kku_urs_promotion_talent_configs` 
+ADD COLUMN `active_direct_required` INT NOT NULL DEFAULT 0 COMMENT '所需直推活跃人数' AFTER `active_count_required`;
+
+-- =====================================================
+-- 3. 更新达人等级配置数据,设置活跃人数要求
+-- =====================================================
+
+-- 更新各等级的活跃人数要求
+UPDATE `kku_urs_promotion_talent_configs` SET 
+    `active_count_required` = 0,
+    `active_direct_required` = 0
+WHERE `level` = 0; -- 非达人
+
+UPDATE `kku_urs_promotion_talent_configs` SET 
+    `active_count_required` = 3,
+    `active_direct_required` = 2
+WHERE `level` = 1; -- 初级达人
+
+UPDATE `kku_urs_promotion_talent_configs` SET 
+    `active_count_required` = 8,
+    `active_direct_required` = 5
+WHERE `level` = 2; -- 中级达人
+
+UPDATE `kku_urs_promotion_talent_configs` SET 
+    `active_count_required` = 15,
+    `active_direct_required` = 8
+WHERE `level` = 3; -- 高级达人
+
+UPDATE `kku_urs_promotion_talent_configs` SET 
+    `active_count_required` = 30,
+    `active_direct_required` = 15
+WHERE `level` = 4; -- 资深达人
+
+UPDATE `kku_urs_promotion_talent_configs` SET 
+    `active_count_required` = 50,
+    `active_direct_required` = 25
+WHERE `level` = 5; -- 顶级达人
+
+-- =====================================================
+-- 4. 验证数据库结构
+-- =====================================================
+
+-- 验证用户映射表结构
+DESCRIBE `kku_urs_promotion_user_mappings`;
+
+-- 验证达人等级配置表结构
+DESCRIBE `kku_urs_promotion_talent_configs`;
+
+-- 验证索引创建
+SHOW INDEX FROM `kku_urs_promotion_user_mappings` WHERE Key_name IN ('idx_is_active', 'idx_last_activity_check');
+
+-- 验证配置数据更新
+SELECT `level`, `name`, `direct_count_required`, `promotion_count_required`, `active_count_required`, `active_direct_required` 
+FROM `kku_urs_promotion_talent_configs` 
+ORDER BY `level`;
+
+-- =====================================================
+-- 5. 初始化活跃状态数据
+-- =====================================================
+
+-- 为现有用户初始化活跃状态(基于最近15天的活动)
+UPDATE `kku_urs_promotion_user_mappings` m
+INNER JOIN `kku_users` u ON m.user_id = u.id
+SET 
+    m.is_active = CASE 
+        WHEN u.last_activity_time >= DATE_SUB(NOW(), INTERVAL 15 DAY) THEN 1 
+        ELSE 0 
+    END,
+    m.last_activity_check = NOW(),
+    m.active_days_count = CASE 
+        WHEN u.last_activity_time >= DATE_SUB(NOW(), INTERVAL 15 DAY) THEN 1 
+        ELSE 0 
+    END
+WHERE m.status = 1; -- 只处理有效的映射关系
+
+-- 统计初始化结果
+SELECT 
+    COUNT(*) as total_mappings,
+    SUM(is_active) as active_users,
+    SUM(CASE WHEN is_active = 0 THEN 1 ELSE 0 END) as inactive_users,
+    ROUND(SUM(is_active) * 100.0 / COUNT(*), 2) as active_percentage
+FROM `kku_urs_promotion_user_mappings` 
+WHERE status = 1;
+
+-- =====================================================
+-- 脚本执行完成
+-- =====================================================
+
+SELECT 'URS推广模块活跃用户功能数据库扩展完成' as message;

+ 295 - 0
app/Module/UrsPromotion/Docs/活跃用户功能.md

@@ -0,0 +1,295 @@
+# URS推广模块 - 活跃用户功能详细文档
+
+## 1. 功能概述
+
+活跃用户功能是URS推广模块的重要扩展,为达人等级系统引入了活跃度概念。该功能基于用户的实际活动情况,为推广体系提供更精准的用户质量评估。
+
+### 1.1 核心特性
+
+- **活跃用户定义**:最近15天内有活动记录的用户
+- **自动化检查**:每日定时任务自动更新用户活跃状态
+- **达人等级集成**:活跃用户数量作为达人等级升级条件
+- **实时统计**:提供活跃用户的实时统计和分析
+- **后台管理**:完整的后台管理界面支持
+
+### 1.2 业务价值
+
+1. **提升推广质量**:通过活跃度筛选,确保推广团队的真实活跃度
+2. **优化激励机制**:基于活跃用户数量的等级体系更加公平合理
+3. **数据驱动决策**:提供详细的活跃用户数据支持运营决策
+4. **防止刷量行为**:通过活跃度要求减少虚假推广行为
+
+## 2. 技术实现
+
+### 2.1 数据库扩展
+
+#### 用户映射表扩展 (urs_promotion_user_mappings)
+```sql
+-- 活跃状态字段
+ALTER TABLE `kku_urs_promotion_user_mappings` 
+ADD COLUMN `is_active` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否活跃:1活跃,0不活跃';
+
+-- 最后活跃检查时间
+ALTER TABLE `kku_urs_promotion_user_mappings` 
+ADD COLUMN `last_activity_check` TIMESTAMP NULL COMMENT '最后活跃检查时间';
+
+-- 活跃天数统计
+ALTER TABLE `kku_urs_promotion_user_mappings` 
+ADD COLUMN `active_days_count` INT NOT NULL DEFAULT 0 COMMENT '活跃天数统计';
+
+-- 索引优化
+ALTER TABLE `kku_urs_promotion_user_mappings` 
+ADD INDEX `idx_is_active` (`is_active`),
+ADD INDEX `idx_last_activity_check` (`last_activity_check`);
+```
+
+#### 达人等级配置表扩展 (urs_promotion_talent_configs)
+```sql
+-- 所需活跃人数
+ALTER TABLE `kku_urs_promotion_talent_configs` 
+ADD COLUMN `active_count_required` INT NOT NULL DEFAULT 0 COMMENT '所需活跃人数';
+
+-- 所需直推活跃人数
+ALTER TABLE `kku_urs_promotion_talent_configs` 
+ADD COLUMN `active_direct_required` INT NOT NULL DEFAULT 0 COMMENT '所需直推活跃人数';
+```
+
+### 2.2 核心服务类
+
+#### UrsActiveUserService
+负责活跃用户状态的管理和统计:
+
+```php
+// 更新单个用户活跃状态
+UrsActiveUserService::updateUserActiveStatus(int $ursUserId): bool
+
+// 批量更新活跃状态
+UrsActiveUserService::batchUpdateActiveStatus(int $limit = 1000): array
+
+// 检查用户活跃状态
+UrsActiveUserService::checkUserActivity(User $user): bool
+
+// 获取活跃团队成员
+UrsActiveUserService::getActiveTeamMembers(int $ursUserId): array
+
+// 获取活跃用户统计
+UrsActiveUserService::getActiveUserStats(): array
+```
+
+#### 扩展现有服务
+- **UrsTalentService**:集成活跃用户条件到达人等级计算
+- **UrsUserMappingService**:添加活跃用户相关方法
+
+### 2.3 定时任务
+
+#### UrsUpdateActiveStatusCommand
+```bash
+# 每日更新活跃状态
+php artisan urs:update-active-status
+
+# 限制处理数量
+php artisan urs:update-active-status --limit=500
+
+# 试运行模式
+php artisan urs:update-active-status --dry-run
+
+# 重置所有状态
+php artisan urs:update-active-status --reset
+```
+
+**建议crontab配置**:
+```bash
+# 每日凌晨2点执行
+0 2 * * * cd /path/to/project && php artisan urs:update-active-status
+```
+
+## 3. 活跃用户判定规则
+
+### 3.1 活跃标准
+- **时间窗口**:最近15天
+- **判定依据**:用户表的`last_activity_time`字段
+- **更新频率**:每日一次
+
+### 3.2 活跃状态计算
+```php
+public static function checkUserActivity(User $user): bool
+{
+    if (!$user->last_activity_time) {
+        return false;
+    }
+    
+    $threshold = Carbon::now()->subDays(15);
+    return $user->last_activity_time >= $threshold;
+}
+```
+
+### 3.3 活跃天数统计
+- **活跃期内**:active_days_count = 1
+- **非活跃期**:active_days_count = 0
+- **用途**:后续可扩展为更复杂的活跃度评分
+
+## 4. 达人等级集成
+
+### 4.1 新增升级条件
+达人等级升级现在需要同时满足以下条件:
+1. **直推人数**:direct_count_required
+2. **团队总人数**:promotion_count_required
+3. **活跃直推人数**:active_direct_required(新增)
+4. **活跃团队人数**:active_count_required(新增)
+
+### 4.2 等级配置示例
+| 等级 | 名称 | 直推要求 | 团队要求 | 活跃直推要求 | 活跃团队要求 |
+|------|------|----------|----------|--------------|--------------|
+| 0 | 非达人 | 0 | 0 | 0 | 0 |
+| 1 | 初级达人 | 3 | 10 | 2 | 3 |
+| 2 | 中级达人 | 8 | 30 | 5 | 8 |
+| 3 | 高级达人 | 15 | 80 | 8 | 15 |
+| 4 | 资深达人 | 30 | 200 | 15 | 30 |
+| 5 | 顶级达人 | 50 | 500 | 25 | 50 |
+
+### 4.3 升级检查逻辑
+```php
+public static function checkUpgradeEligibility(int $ursUserId): array
+{
+    // 获取基础统计
+    $stats = UrsReferralService::getReferralStats($ursUserId);
+    
+    // 获取活跃统计
+    $activeStats = UrsActiveUserService::getActiveTeamMembers($ursUserId);
+    
+    // 检查所有条件
+    $directCountMet = $talent->direct_count >= $config->direct_count_required;
+    $promotionCountMet = $talent->promotion_count >= $config->promotion_count_required;
+    $activeDirectMet = $activeStats['active_direct_count'] >= $config->active_direct_required;
+    $activeTotalMet = $activeStats['active_total_count'] >= $config->active_count_required;
+    
+    return [
+        'eligible' => $directCountMet && $promotionCountMet && $activeDirectMet && $activeTotalMet,
+        // ... 详细信息
+    ];
+}
+```
+
+## 5. 后台管理功能
+
+### 5.1 用户映射关系管理
+- **列表页面**:显示活跃状态、活跃天数、最后检查时间
+- **筛选功能**:支持按活跃状态筛选
+- **详情页面**:显示完整的活跃用户信息
+
+### 5.2 达人等级配置管理
+- **配置表单**:支持设置活跃用户要求
+- **列表显示**:显示活跃人数要求
+- **帮助信息**:提供活跃用户定义说明
+
+### 5.3 统计报表
+- **活跃用户概览**:总数、活跃数、活跃比例
+- **更新状态**:最近更新情况、需要检查的用户数
+- **趋势分析**:活跃用户数量变化趋势
+
+## 6. API接口
+
+### 6.1 活跃用户统计
+```php
+// 获取活跃用户统计
+$stats = UrsActiveUserService::getActiveUserStats();
+// 返回:total_users, active_users, inactive_users, active_percentage
+
+// 获取详细统计
+$detailedStats = UrsActiveUserService::getDetailedActiveStats();
+// 返回:基础统计 + recent_updates, need_check_count, last_update_time
+```
+
+### 6.2 用户活跃状态查询
+```php
+// 检查单个用户是否活跃
+$isActive = UrsUserMappingService::isUserActive(int $ursUserId): bool
+
+// 批量获取活跃用户ID
+$activeIds = UrsUserMappingService::getActiveUrsUserIds(array $ursUserIds): array
+
+// 获取活跃团队成员
+$activeTeam = UrsActiveUserService::getActiveTeamMembers(int $ursUserId): array
+```
+
+### 6.3 活跃状态更新
+```php
+// 更新单个用户
+$result = UrsActiveUserService::updateUserActiveStatus(int $ursUserId): bool
+
+// 批量更新
+$stats = UrsActiveUserService::batchUpdateActiveStatus(int $limit): array
+```
+
+## 7. 测试和验证
+
+### 7.1 测试命令
+```bash
+# 运行完整测试
+php artisan urs:test-active-user
+
+# 测试活跃用户统计
+php artisan urs:test-active-user --stats
+
+# 测试活跃状态更新
+php artisan urs:test-active-user --update
+
+# 测试达人等级计算
+php artisan urs:test-active-user --talent
+
+# 测试指定用户
+php artisan urs:test-active-user --user-id=1001
+```
+
+### 7.2 验证要点
+1. **数据一致性**:活跃状态与实际活动时间一致
+2. **性能表现**:批量更新的执行效率
+3. **等级计算**:活跃用户条件正确集成到等级计算
+4. **定时任务**:每日更新任务正常执行
+
+## 8. 监控和维护
+
+### 8.1 关键指标监控
+- **活跃用户比例**:整体用户活跃度
+- **更新成功率**:定时任务执行成功率
+- **处理性能**:批量更新的执行时间
+- **数据完整性**:活跃状态数据的准确性
+
+### 8.2 日志记录
+- **更新操作**:记录每次活跃状态更新
+- **异常情况**:记录更新失败的用户和原因
+- **统计变化**:记录活跃用户数量的变化趋势
+
+### 8.3 维护建议
+1. **定期检查**:监控定时任务的执行状态
+2. **性能优化**:根据用户增长调整批量处理大小
+3. **数据清理**:定期清理过期的检查记录
+4. **阈值调整**:根据业务需求调整活跃天数阈值
+
+## 9. 扩展规划
+
+### 9.1 功能扩展
+- **活跃度评分**:基于活动频率的更精细评分
+- **活跃类型**:区分不同类型的活动(登录、交易、社交等)
+- **活跃趋势**:用户活跃度的历史趋势分析
+- **预警机制**:活跃度下降的预警通知
+
+### 9.2 性能优化
+- **缓存策略**:热点用户活跃状态缓存
+- **增量更新**:只更新状态发生变化的用户
+- **分布式处理**:大规模用户的分布式更新
+- **实时更新**:基于用户活动的实时状态更新
+
+## 10. 最佳实践
+
+1. **合理设置阈值**:根据业务特点设置合适的活跃天数
+2. **监控执行状态**:确保定时任务正常执行
+3. **数据备份**:重要操作前备份相关数据
+4. **渐进式部署**:新功能分阶段上线验证
+5. **用户沟通**:向用户说明活跃度要求的变化
+
+---
+
+**文档版本**: v1.0  
+**创建时间**: 2025-06-16  
+**更新时间**: 2025-06-16

+ 14 - 0
app/Module/UrsPromotion/Docs/用户绑定关系.md

@@ -32,16 +32,30 @@
 | user_id | bigint | 农场用户ID(对应users表) |
 | mapping_time | timestamp | 映射建立时间(用户进入农场时间) |
 | status | tinyint | 状态:1有效,0无效 |
+| is_active | tinyint | 是否活跃:1活跃,0不活跃 |
+| last_activity_check | timestamp | 最后活跃检查时间 |
+| active_days_count | int | 活跃天数统计 |
 | created_at | timestamp | 创建时间 |
 | updated_at | timestamp | 更新时间 |
 
 ### 2.2 状态定义
 
 ```php
+// 映射状态
 const STATUS_INVALID = 0; // 无效
 const STATUS_VALID = 1;   // 有效
+
+// 活跃状态
+const ACTIVE_NO = 0;  // 不活跃
+const ACTIVE_YES = 1; // 活跃
 ```
 
+### 2.3 活跃用户定义
+
+- **活跃用户**:最近15天内有活动记录的用户(基于last_activity_time字段)
+- **活跃检查**:每日定时任务自动检查和更新用户活跃状态
+- **活跃统计**:用于达人等级升级条件判断
+
 ### 2.3 索引设计
 
 - **主键索引**:`PRIMARY KEY (id)`

+ 5 - 1
app/Module/UrsPromotion/Models/UrsTalentConfig.php

@@ -7,12 +7,14 @@ use App\Module\UrsPromotion\Enums\UrsTalentLevel;
 
 /**
  * URS达人等级配置模型
- * field start 
+ * field start
  * @property  int  $id  主键ID
  * @property  int  $level  等级:0无,1初级,2中级,3高级,4资深,5顶级
  * @property  string  $name  等级名称
  * @property  int  $direct_count_required  所需直推人数
  * @property  int  $promotion_count_required  所需团队总人数
+ * @property  int  $active_count_required  所需活跃人数
+ * @property  int  $active_direct_required  所需直推活跃人数
  * @property  string  $icon  等级图标
  * @property  string  $description  等级描述
  * @property  int  $sort_order  排序权重
@@ -45,6 +47,8 @@ class UrsTalentConfig extends ModelCore
         'level' => 'integer',
         'direct_count_required' => 'integer',
         'promotion_count_required' => 'integer',
+        'active_count_required' => 'integer',
+        'active_direct_required' => 'integer',
         'promotion_direct_group' => 'integer',
         'promotion_indirect_group' => 'integer',
         'promotion_third_group' => 'integer',

+ 112 - 1
app/Module/UrsPromotion/Models/UrsUserMapping.php

@@ -8,12 +8,15 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
 /**
  * URS用户与农场用户关系映射模型
  *
- * field start 
+ * field start
  * @property  int  $id  主键ID
  * @property  int  $urs_user_id  URS用户ID
  * @property  int  $user_id  农场用户ID
  * @property  \Carbon\Carbon  $mapping_time  映射建立时间(用户进入农场时间)
  * @property  int  $status  状态:1有效,0无效
+ * @property  int  $is_active  是否活跃:1活跃,0不活跃
+ * @property  \Carbon\Carbon|null  $last_activity_check  最后活跃检查时间
+ * @property  int  $active_days_count  活跃天数统计
  * @property  \Carbon\Carbon  $created_at  创建时间
  * @property  \Carbon\Carbon  $updated_at  更新时间
  * field end
@@ -33,6 +36,9 @@ class UrsUserMapping extends ModelCore
         'user_id',
         'mapping_time',
         'status',
+        'is_active',
+        'last_activity_check',
+        'active_days_count',
     ];
 
     /**
@@ -43,6 +49,9 @@ class UrsUserMapping extends ModelCore
         'user_id' => 'integer',
         'mapping_time' => 'datetime',
         'status' => 'integer',
+        'is_active' => 'integer',
+        'last_activity_check' => 'datetime',
+        'active_days_count' => 'integer',
         'created_at' => 'datetime',
         'updated_at' => 'datetime',
     ];
@@ -53,6 +62,12 @@ class UrsUserMapping extends ModelCore
     const STATUS_INVALID = 0; // 无效
     const STATUS_VALID = 1;   // 有效
 
+    /**
+     * 活跃状态常量
+     */
+    const ACTIVE_NO = 0;  // 不活跃
+    const ACTIVE_YES = 1; // 活跃
+
     /**
      * 状态映射
      */
@@ -61,6 +76,14 @@ class UrsUserMapping extends ModelCore
         self::STATUS_VALID => '有效',
     ];
 
+    /**
+     * 活跃状态映射
+     */
+    public static $activeMap = [
+        self::ACTIVE_NO => '不活跃',
+        self::ACTIVE_YES => '活跃',
+    ];
+
     /**
      * 根据URS用户ID获取农场用户ID
      *
@@ -176,4 +199,92 @@ class UrsUserMapping extends ModelCore
     {
         return $this->belongsTo(\App\Module\User\Models\User::class, 'user_id', 'id');
     }
+
+    /**
+     * 检查用户是否活跃
+     *
+     * @return bool
+     */
+    public function isActive(): bool
+    {
+        return $this->is_active === self::ACTIVE_YES;
+    }
+
+    /**
+     * 获取活跃用户列表
+     *
+     * @param array $ursUserIds URS用户ID数组
+     * @return array 活跃的URS用户ID数组
+     */
+    public static function getActiveUrsUserIds(array $ursUserIds): array
+    {
+        return self::whereIn('urs_user_id', $ursUserIds)
+            ->where('status', self::STATUS_VALID)
+            ->where('is_active', self::ACTIVE_YES)
+            ->pluck('urs_user_id')
+            ->toArray();
+    }
+
+    /**
+     * 获取活跃用户统计
+     *
+     * @return array
+     */
+    public static function getActiveUserStats(): array
+    {
+        $total = self::where('status', self::STATUS_VALID)->count();
+        $active = self::where('status', self::STATUS_VALID)
+            ->where('is_active', self::ACTIVE_YES)
+            ->count();
+
+        return [
+            'total_users' => $total,
+            'active_users' => $active,
+            'inactive_users' => $total - $active,
+            'active_percentage' => $total > 0 ? round($active * 100 / $total, 2) : 0,
+        ];
+    }
+
+    /**
+     * 批量更新用户活跃状态
+     *
+     * @param array $activeData 活跃数据 [urs_user_id => ['is_active' => 1, 'active_days_count' => 5]]
+     * @return int 更新的记录数
+     */
+    public static function batchUpdateActiveStatus(array $activeData): int
+    {
+        $updated = 0;
+
+        foreach ($activeData as $ursUserId => $data) {
+            $result = self::where('urs_user_id', $ursUserId)
+                ->where('status', self::STATUS_VALID)
+                ->update([
+                    'is_active' => $data['is_active'],
+                    'last_activity_check' => now(),
+                    'active_days_count' => $data['active_days_count'] ?? 0,
+                ]);
+
+            $updated += $result;
+        }
+
+        return $updated;
+    }
+
+    /**
+     * 获取需要检查活跃状态的用户
+     *
+     * @param int $limit 限制数量
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public static function getUsersNeedActivityCheck(int $limit = 1000): \Illuminate\Database\Eloquent\Collection
+    {
+        return self::where('status', self::STATUS_VALID)
+            ->where(function($query) {
+                $query->whereNull('last_activity_check')
+                    ->orWhere('last_activity_check', '<', now()->subDay());
+            })
+            ->with('user')
+            ->limit($limit)
+            ->get();
+    }
 }

+ 315 - 0
app/Module/UrsPromotion/Services/UrsActiveUserService.php

@@ -0,0 +1,315 @@
+<?php
+
+namespace App\Module\UrsPromotion\Services;
+
+use App\Module\UrsPromotion\Models\UrsUserMapping;
+use App\Module\User\Models\User;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Carbon\Carbon;
+
+/**
+ * URS活跃用户服务
+ * 
+ * 负责管理URS用户的活跃状态,包括活跃状态检查、更新和统计
+ */
+class UrsActiveUserService
+{
+    /**
+     * 活跃用户定义:最近15天有活动的用户
+     */
+    const ACTIVE_DAYS_THRESHOLD = 15;
+
+    /**
+     * 更新单个用户的活跃状态
+     *
+     * @param int $ursUserId URS用户ID
+     * @return bool
+     */
+    public static function updateUserActiveStatus(int $ursUserId): bool
+    {
+        try {
+            // 获取用户映射关系
+            $mapping = UrsUserMapping::where('urs_user_id', $ursUserId)
+                ->where('status', UrsUserMapping::STATUS_VALID)
+                ->with('user')
+                ->first();
+
+            if (!$mapping || !$mapping->user) {
+                Log::warning('URS用户映射关系不存在或农场用户不存在', [
+                    'urs_user_id' => $ursUserId
+                ]);
+                return false;
+            }
+
+            // 检查用户活跃状态
+            $isActive = self::checkUserActivity($mapping->user);
+            $activeDaysCount = self::calculateActiveDaysCount($mapping->user);
+
+            // 更新活跃状态
+            $mapping->update([
+                'is_active' => $isActive ? UrsUserMapping::ACTIVE_YES : UrsUserMapping::ACTIVE_NO,
+                'last_activity_check' => now(),
+                'active_days_count' => $activeDaysCount,
+            ]);
+
+            Log::info('URS用户活跃状态更新成功', [
+                'urs_user_id' => $ursUserId,
+                'farm_user_id' => $mapping->user_id,
+                'is_active' => $isActive,
+                'active_days_count' => $activeDaysCount
+            ]);
+
+            return true;
+
+        } catch (\Exception $e) {
+            Log::error('URS用户活跃状态更新失败', [
+                'urs_user_id' => $ursUserId,
+                'error' => $e->getMessage()
+            ]);
+            return false;
+        }
+    }
+
+    /**
+     * 批量更新用户活跃状态
+     *
+     * @param int $limit 每次处理的用户数量限制
+     * @return array 更新结果统计
+     */
+    public static function batchUpdateActiveStatus(int $limit = 1000): array
+    {
+        $stats = [
+            'total_processed' => 0,
+            'successful_updates' => 0,
+            'failed_updates' => 0,
+            'active_users' => 0,
+            'inactive_users' => 0,
+        ];
+
+        try {
+            // 获取需要检查的用户
+            $mappings = UrsUserMapping::getUsersNeedActivityCheck($limit);
+            $stats['total_processed'] = $mappings->count();
+
+            Log::info('开始批量更新URS用户活跃状态', [
+                'total_users' => $stats['total_processed'],
+                'limit' => $limit
+            ]);
+
+            foreach ($mappings as $mapping) {
+                try {
+                    if (!$mapping->user) {
+                        $stats['failed_updates']++;
+                        continue;
+                    }
+
+                    // 检查用户活跃状态
+                    $isActive = self::checkUserActivity($mapping->user);
+                    $activeDaysCount = self::calculateActiveDaysCount($mapping->user);
+
+                    // 更新活跃状态
+                    $mapping->update([
+                        'is_active' => $isActive ? UrsUserMapping::ACTIVE_YES : UrsUserMapping::ACTIVE_NO,
+                        'last_activity_check' => now(),
+                        'active_days_count' => $activeDaysCount,
+                    ]);
+
+                    $stats['successful_updates']++;
+                    if ($isActive) {
+                        $stats['active_users']++;
+                    } else {
+                        $stats['inactive_users']++;
+                    }
+
+                } catch (\Exception $e) {
+                    $stats['failed_updates']++;
+                    Log::error('单个用户活跃状态更新失败', [
+                        'urs_user_id' => $mapping->urs_user_id,
+                        'error' => $e->getMessage()
+                    ]);
+                }
+            }
+
+            Log::info('批量更新URS用户活跃状态完成', $stats);
+
+        } catch (\Exception $e) {
+            Log::error('批量更新URS用户活跃状态失败', [
+                'error' => $e->getMessage(),
+                'stats' => $stats
+            ]);
+        }
+
+        return $stats;
+    }
+
+    /**
+     * 检查用户活跃状态
+     *
+     * @param User $user 农场用户对象
+     * @return bool
+     */
+    public static function checkUserActivity(User $user): bool
+    {
+        if (!$user->last_activity_time) {
+            return false;
+        }
+
+        $threshold = Carbon::now()->subDays(self::ACTIVE_DAYS_THRESHOLD);
+        return $user->last_activity_time >= $threshold;
+    }
+
+    /**
+     * 计算用户活跃天数
+     *
+     * @param User $user 农场用户对象
+     * @return int
+     */
+    public static function calculateActiveDaysCount(User $user): int
+    {
+        if (!$user->last_activity_time) {
+            return 0;
+        }
+
+        $daysSinceLastActivity = Carbon::now()->diffInDays($user->last_activity_time);
+        
+        // 如果在活跃期内,返回1,否则返回0
+        return $daysSinceLastActivity <= self::ACTIVE_DAYS_THRESHOLD ? 1 : 0;
+    }
+
+    /**
+     * 获取活跃用户统计信息
+     *
+     * @return array
+     */
+    public static function getActiveUserStats(): array
+    {
+        return UrsUserMapping::getActiveUserStats();
+    }
+
+    /**
+     * 获取指定URS用户的活跃团队成员
+     *
+     * @param int $ursUserId URS用户ID
+     * @return array
+     */
+    public static function getActiveTeamMembers(int $ursUserId): array
+    {
+        // 获取用户的推荐关系
+        $teamMembers = UrsReferralService::getTeamMembers($ursUserId, 3); // 获取三代团队
+        
+        if (empty($teamMembers)) {
+            return [
+                'active_direct_count' => 0,
+                'active_total_count' => 0,
+                'active_members' => []
+            ];
+        }
+
+        // 提取所有团队成员的URS用户ID
+        $allMemberIds = [];
+        foreach ($teamMembers as $level => $members) {
+            $allMemberIds = array_merge($allMemberIds, array_column($members, 'urs_user_id'));
+        }
+
+        // 获取活跃的团队成员
+        $activeMemberIds = UrsUserMapping::getActiveUrsUserIds($allMemberIds);
+
+        // 统计各层级的活跃人数
+        $activeDirectCount = 0;
+        $activeTotalCount = count($activeMemberIds);
+        $activeMembers = [];
+
+        foreach ($teamMembers as $level => $members) {
+            foreach ($members as $member) {
+                if (in_array($member['urs_user_id'], $activeMemberIds)) {
+                    $activeMembers[] = array_merge($member, ['level' => $level]);
+                    if ($level === 1) { // 直推
+                        $activeDirectCount++;
+                    }
+                }
+            }
+        }
+
+        return [
+            'active_direct_count' => $activeDirectCount,
+            'active_total_count' => $activeTotalCount,
+            'active_members' => $activeMembers
+        ];
+    }
+
+    /**
+     * 获取活跃用户详细统计
+     *
+     * @return array
+     */
+    public static function getDetailedActiveStats(): array
+    {
+        $baseStats = self::getActiveUserStats();
+        
+        // 获取最近更新统计
+        $recentUpdates = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
+            ->where('last_activity_check', '>=', now()->subDay())
+            ->count();
+
+        // 获取需要检查的用户数量
+        $needCheckCount = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
+            ->where(function($query) {
+                $query->whereNull('last_activity_check')
+                    ->orWhere('last_activity_check', '<', now()->subDay());
+            })
+            ->count();
+
+        return array_merge($baseStats, [
+            'recent_updates' => $recentUpdates,
+            'need_check_count' => $needCheckCount,
+            'last_update_time' => UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
+                ->whereNotNull('last_activity_check')
+                ->max('last_activity_check'),
+        ]);
+    }
+
+    /**
+     * 重置所有用户的活跃状态(用于测试或重新初始化)
+     *
+     * @return array
+     */
+    public static function resetAllActiveStatus(): array
+    {
+        try {
+            DB::beginTransaction();
+
+            // 重置所有用户的活跃状态
+            $updated = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
+                ->update([
+                    'is_active' => UrsUserMapping::ACTIVE_NO,
+                    'last_activity_check' => null,
+                    'active_days_count' => 0,
+                ]);
+
+            DB::commit();
+
+            Log::info('重置所有URS用户活跃状态完成', [
+                'updated_count' => $updated
+            ]);
+
+            return [
+                'success' => true,
+                'updated_count' => $updated,
+                'message' => '重置完成'
+            ];
+
+        } catch (\Exception $e) {
+            DB::rollBack();
+            Log::error('重置URS用户活跃状态失败', [
+                'error' => $e->getMessage()
+            ]);
+
+            return [
+                'success' => false,
+                'updated_count' => 0,
+                'message' => '重置失败:' . $e->getMessage()
+            ];
+        }
+    }
+}

+ 40 - 5
app/Module/UrsPromotion/Services/UrsTalentService.php

@@ -29,8 +29,16 @@ class UrsTalentService
             // 获取用户的团队统计
             $stats = UrsReferralService::getReferralStats($ursUserId);
 
+            // 获取活跃团队成员统计
+            $activeStats = UrsActiveUserService::getActiveTeamMembers($ursUserId);
+
             // 计算应该的达人等级
-            $newLevel = self::calculateTalentLevel($stats['direct_count'], $stats['total_team_count']);
+            $newLevel = self::calculateTalentLevel(
+                $stats['direct_count'],
+                $stats['total_team_count'],
+                $activeStats['active_direct_count'],
+                $activeStats['active_total_count']
+            );
 
             // 获取或创建达人记录
             $talent = UrsUserTalent::firstOrCreate(
@@ -102,9 +110,11 @@ class UrsTalentService
      *
      * @param int $directCount 直推人数
      * @param int $totalCount 团队总人数
+     * @param int $activeDirectCount 活跃直推人数
+     * @param int $activeTotalCount 活跃团队总人数
      * @return int 达人等级
      */
-    private static function calculateTalentLevel(int $directCount, int $totalCount): int
+    private static function calculateTalentLevel(int $directCount, int $totalCount, int $activeDirectCount = 0, int $activeTotalCount = 0): int
     {
         // 获取等级配置(按等级倒序排列)
         $configs = UrsTalentConfig::where('status', 1)
@@ -112,8 +122,15 @@ class UrsTalentService
             ->get();
 
         foreach ($configs as $config) {
-            if ($directCount >= $config->direct_count_required &&
-                $totalCount >= $config->promotion_count_required) {
+            // 检查基础条件
+            $directCountMet = $directCount >= $config->direct_count_required;
+            $totalCountMet = $totalCount >= $config->promotion_count_required;
+
+            // 检查活跃用户条件
+            $activeDirectMet = $activeDirectCount >= $config->active_direct_required;
+            $activeTotalMet = $activeTotalCount >= $config->active_count_required;
+
+            if ($directCountMet && $totalCountMet && $activeDirectMet && $activeTotalMet) {
                 return $config->level;
             }
         }
@@ -228,11 +245,17 @@ class UrsTalentService
             return ['eligible' => false, 'reason' => '已达到最高等级'];
         }
 
+        // 获取活跃团队成员统计
+        $activeStats = UrsActiveUserService::getActiveTeamMembers($ursUserId);
+
+        // 检查各项条件
         $directCountMet = $talent->direct_count >= $nextConfig->direct_count_required;
         $promotionCountMet = $talent->promotion_count >= $nextConfig->promotion_count_required;
+        $activeDirectMet = $activeStats['active_direct_count'] >= $nextConfig->active_direct_required;
+        $activeTotalMet = $activeStats['active_total_count'] >= $nextConfig->active_count_required;
 
         return [
-            'eligible' => $directCountMet && $promotionCountMet,
+            'eligible' => $directCountMet && $promotionCountMet && $activeDirectMet && $activeTotalMet,
             'next_level' => $nextLevel,
             'next_level_name' => $nextConfig->name,
             'requirements' => [
@@ -248,6 +271,18 @@ class UrsTalentService
                     'met' => $promotionCountMet,
                     'gap' => max(0, $nextConfig->promotion_count_required - $talent->promotion_count),
                 ],
+                'active_direct_count' => [
+                    'required' => $nextConfig->active_direct_required,
+                    'current' => $activeStats['active_direct_count'],
+                    'met' => $activeDirectMet,
+                    'gap' => max(0, $nextConfig->active_direct_required - $activeStats['active_direct_count']),
+                ],
+                'active_total_count' => [
+                    'required' => $nextConfig->active_count_required,
+                    'current' => $activeStats['active_total_count'],
+                    'met' => $activeTotalMet,
+                    'gap' => max(0, $nextConfig->active_count_required - $activeStats['active_total_count']),
+                ],
             ],
         ];
     }

+ 58 - 0
app/Module/UrsPromotion/Services/UrsUserMappingService.php

@@ -476,4 +476,62 @@ class UrsUserMappingService
 
         return $result;
     }
+
+    /**
+     * 获取活跃用户统计信息
+     *
+     * @return array
+     */
+    public static function getActiveUserStats(): array
+    {
+        return UrsUserMapping::getActiveUserStats();
+    }
+
+    /**
+     * 批量更新用户活跃状态
+     *
+     * @param array $activeData 活跃数据 [urs_user_id => ['is_active' => 1, 'active_days_count' => 5]]
+     * @return int 更新的记录数
+     */
+    public static function batchUpdateActiveStatus(array $activeData): int
+    {
+        return UrsUserMapping::batchUpdateActiveStatus($activeData);
+    }
+
+    /**
+     * 获取活跃的URS用户ID列表
+     *
+     * @param array $ursUserIds URS用户ID数组
+     * @return array 活跃的URS用户ID数组
+     */
+    public static function getActiveUrsUserIds(array $ursUserIds): array
+    {
+        return UrsUserMapping::getActiveUrsUserIds($ursUserIds);
+    }
+
+    /**
+     * 检查用户是否活跃
+     *
+     * @param int $ursUserId URS用户ID
+     * @return bool
+     */
+    public static function isUserActive(int $ursUserId): bool
+    {
+        $mapping = UrsUserMapping::where('urs_user_id', $ursUserId)
+            ->where('status', UrsUserMapping::STATUS_VALID)
+            ->first();
+
+        return $mapping ? $mapping->isActive() : false;
+    }
+
+    /**
+     * 更新单个用户的活跃状态
+     *
+     * @param int $ursUserId URS用户ID
+     * @return bool
+     */
+    public static function updateUserActiveStatus(int $ursUserId): bool
+    {
+        return UrsActiveUserService::updateUserActiveStatus($ursUserId);
+    }
 }