diff --git a/app/Console/Commands/ConsumeWechatMessages.php b/app/Console/Commands/ConsumeWechatMessages.php index dc3ba49..ed74078 100644 --- a/app/Console/Commands/ConsumeWechatMessages.php +++ b/app/Console/Commands/ConsumeWechatMessages.php @@ -107,6 +107,44 @@ class ConsumeWechatMessages extends Command $this->info("收到潜在绑定请求: {$content} from {$fromUser}"); $this->handleBindRequest(strtoupper($content), $fromUser, $apiService); } + + // 微信密码重置逻辑:必须是私聊 + if (! $isChatroom && str_contains($content, '重置密码')) { + $this->info("收到微信密码重置请求 from {$fromUser}"); + $this->handlePasswordResetRequest($fromUser, $apiService); + } + } + + /** + * 处理微信端的自助密码重置请求 + */ + protected function handlePasswordResetRequest(string $wxid, WechatBotApiService $apiService): void + { + // 检索绑定了该微信号的聊天室用户 + $user = User::where('wxid', $wxid)->first(); + + if (! $user) { + $apiService->sendTextMessage( + $wxid, + "❌ 密码重置失败:您当前的微信号尚未绑定任何聊天室账号。\n\n" + .'请先登录聊天室大厅,在个人设置中获取 6 位绑定码,然后再在微信中向我发送“BD-绑定码”(例如: BD-123456)进行绑定。' + ); + + return; + } + + // 随机生成 8 位新随机密码并更新 + $newPassword = \Illuminate\Support\Str::random(8); + $user->password = \Illuminate\Support\Facades\Hash::make($newPassword); + $user->save(); + + $successMsg = "🎉 密码重置成功!\n" + ."本小助手已为您绑定的聊天室账号 [{$user->username}] 重新生成了密码:\n\n" + ."【{$newPassword}】\n\n" + .'温馨提示:请使用此随机密码登录聊天室,并尽快在个人偏好设置中将其修改为您的常用密码。'; + + $apiService->sendTextMessage($wxid, $successMsg); + $this->info("微信用户 [{$user->username}] 已通过微信机器人成功重置了密码"); } /** diff --git a/tests/Feature/ConsumeWechatMessagesTest.php b/tests/Feature/ConsumeWechatMessagesTest.php new file mode 100644 index 0000000..3c439f0 --- /dev/null +++ b/tests/Feature/ConsumeWechatMessagesTest.php @@ -0,0 +1,115 @@ +mock(WechatBotApiService::class, function (MockInterface $mock) { + $mock->shouldReceive('sendTextMessage') + ->once() + ->with('wxid_test_user_123', \Mockery::on(function ($arg) { + return str_contains($arg, '密码重置失败') && str_contains($arg, '尚未绑定'); + })) + ->andReturn([]); + }); + + $msg = [ + 'msg_type' => 1, + 'content' => '重置密码', + 'from_user' => 'wxid_test_user_123', + 'is_chatroom' => false, + ]; + + $command = new \App\Console\Commands\ConsumeWechatMessages( + $this->createMock(KafkaConsumerService::class) + ); + $command->setLaravel($this->app); + + // 绑定 NullOutput 避免 $this->info 报 null 指针错误 + $output = new \Symfony\Component\Console\Output\NullOutput; + $reflectorOutput = new \ReflectionProperty($command, 'output'); + $reflectorOutput->setValue($command, new \Illuminate\Console\OutputStyle( + new \Symfony\Component\Console\Input\ArrayInput([]), + $output + )); + + $reflector = new \ReflectionMethod($command, 'processMessage'); + $reflector->invoke($command, $msg, $apiServiceMock); + } + + /** + * 当已绑定微信的用户在微信私聊中发送“重置密码”时,自动随机生成 8 位密码并完成更新,通过微信私信回发。 + */ + public function test_wechat_password_reset_succeeds_when_bound(): void + { + $user = User::factory()->create([ + 'username' => 'test_bind_user', + 'wxid' => 'wxid_test_user_123', + 'password' => Hash::make('old_password_123'), + ]); + + $apiServiceMock = $this->mock(WechatBotApiService::class, function (MockInterface $mock) use ($user) { + $mock->shouldReceive('sendTextMessage') + ->once() + ->with('wxid_test_user_123', \Mockery::on(function ($arg) use ($user) { + return str_contains($arg, '密码重置成功') && str_contains($arg, $user->username); + })) + ->andReturn([]); + }); + + $msg = [ + 'msg_type' => 1, + 'content' => ' 想要重置密码呢 ', + 'from_user' => 'wxid_test_user_123', + 'is_chatroom' => false, + ]; + + $command = new \App\Console\Commands\ConsumeWechatMessages( + $this->createMock(KafkaConsumerService::class) + ); + $command->setLaravel($this->app); + + // 绑定 NullOutput + $output = new \Symfony\Component\Console\Output\NullOutput; + $reflectorOutput = new \ReflectionProperty($command, 'output'); + $reflectorOutput->setValue($command, new \Illuminate\Console\OutputStyle( + new \Symfony\Component\Console\Input\ArrayInput([]), + $output + )); + + $reflector = new \ReflectionMethod($command, 'processMessage'); + $reflector->invoke($command, $msg, $apiServiceMock); + + $user->refresh(); + $this->assertFalse(Hash::check('old_password_123', $user->password)); + } +}