feat: 微信端私聊机器人支持绑定微信号的密码自助重置功能,并提供反射自动化测试
This commit is contained in:
@@ -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}] 已通过微信机器人成功重置了密码");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:微信机器人 Kafka 消息消费及密码重置测试
|
||||
*
|
||||
* 覆盖在微信上对助手机器人发送重置密码的拦截逻辑,
|
||||
* 验证对未绑定账号及已绑定账号的回复行为和密码更新结果。
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\WechatBot\KafkaConsumerService;
|
||||
use App\Services\WechatBot\WechatBotApiService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* 验证 ConsumeWechatMessages 控制台消费命令的解析与动作路由行为。
|
||||
*/
|
||||
class ConsumeWechatMessagesTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* 当微信用户未绑定任何聊天室账户时发送“重置密码”,回复未绑定指引微信。
|
||||
*/
|
||||
public function test_wechat_password_reset_fails_when_not_bound(): void
|
||||
{
|
||||
$apiServiceMock = $this->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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user