|
|
@@ -65,6 +65,7 @@ class PaymentChannelOptionsTest extends TestCase
|
|
|
DB::table('recharge_channel')->insert($fixture['channels']);
|
|
|
DB::table('recharge_channel_group')->insert($fixture['groups']);
|
|
|
(require dirname(__DIR__, 2) . '/database/migrations/2026_09_08_160000_add_payment_provider_status_and_channel_soft_deletes.php')->up();
|
|
|
+ (require dirname(__DIR__, 2) . '/database/migrations/2026_09_08_170000_add_soft_deletes_to_recharge_channel_groups.php')->up();
|
|
|
$db->schema()->create('operation_audits', function (Blueprint $t) {
|
|
|
$t->id(); $t->string('resource_type'); $t->unsignedBigInteger('resource_id')->nullable();
|
|
|
$t->string('action'); $t->json('changes'); $t->unsignedBigInteger('operator_id')->nullable();
|
|
|
@@ -496,4 +497,152 @@ class PaymentChannelOptionsTest extends TestCase
|
|
|
$this->assertSame('', DB::table('recharge_channel_group')->where('id', 1)->value('recharge_type'));
|
|
|
$this->assertSame('yuebao,old_user,recharge', DB::table('recharge_channel_group')->where('id', 1)->value('activity_type'));
|
|
|
}
|
|
|
+
|
|
|
+ private function saveGroup(array $params): array
|
|
|
+ {
|
|
|
+ $this->app->instance('request', Request::create('/admin/rechargeChannel/updateGroup', 'POST', $params));
|
|
|
+ return (new RechargeChannel())->updateGroup()->getData(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function groupPayload(int $id = 1): array
|
|
|
+ {
|
|
|
+ $group = DB::table('recharge_channel_group')->where('id', $id)->first();
|
|
|
+ return ['id' => $id, 'name' => $group->name, 'recharge_type' => explode(',', $group->recharge_type),
|
|
|
+ 'withdraw_type' => explode(',', $group->withdraw_type)];
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_legacy_group_can_be_saved_and_only_invalid_existing_types_are_cleaned(): void
|
|
|
+ {
|
|
|
+ $payload = $this->groupPayload();
|
|
|
+ $result = $this->saveGroup($payload);
|
|
|
+ $this->assertSame(0, $result['code']);
|
|
|
+ $this->assertSame([], $result['data']);
|
|
|
+ $this->assertStringContainsString('已清理', $result['msg']);
|
|
|
+ $saved = $this->groupPayload();
|
|
|
+ $this->assertSame(array_values(array_diff($payload['recharge_type'], ['1', '2', '3'])), $saved['recharge_type']);
|
|
|
+ $this->assertSame(array_values(array_diff($payload['withdraw_type'], ['ylk', 'zfb', 'szrmb'])), $saved['withdraw_type']);
|
|
|
+ $this->assertSame('yuebao,old_user,recharge', DB::table('recharge_channel_group')->where('id', 1)->value('activity_type'));
|
|
|
+ $audit = DB::table('operation_audits')->where('resource_type', 'recharge_channel_group')->first();
|
|
|
+ $this->assertNotNull($audit);
|
|
|
+ $this->assertSame(['1', '2', '3'], json_decode($audit->changes, true)['after']['removed_types']['recharge_type']);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_new_unknown_type_in_existing_group_is_rejected_atomically(): void
|
|
|
+ {
|
|
|
+ $payload = $this->groupPayload();
|
|
|
+ $payload['recharge_type'][] = 'not_a_type';
|
|
|
+ $before = DB::table('recharge_channel_group')->where('id', 1)->first();
|
|
|
+ $result = $this->saveGroup($payload);
|
|
|
+ $this->assertSame(-3, $result['code']);
|
|
|
+ $this->assertStringContainsString('not_a_type', $result['msg']);
|
|
|
+ $this->assertSame($before->recharge_type, DB::table('recharge_channel_group')->where('id', 1)->value('recharge_type'));
|
|
|
+ $this->assertSame(0, DB::table('operation_audits')->count());
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_new_group_cannot_reuse_invalid_values_from_another_group(): void
|
|
|
+ {
|
|
|
+ $payload = $this->groupPayload();
|
|
|
+ unset($payload['id']);
|
|
|
+ $result = $this->saveGroup($payload);
|
|
|
+ $this->assertSame(-3, $result['code']);
|
|
|
+ $this->assertStringContainsString('1,2,3', $result['msg']);
|
|
|
+ $this->assertSame(2, DB::table('recharge_channel_group')->count());
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_group_types_trim_deduplicate_and_keep_disabled_company_membership(): void
|
|
|
+ {
|
|
|
+ $this->setCompanyStatus('no', 0);
|
|
|
+ $result = $this->saveGroup(['id' => 1, 'name' => '组合',
|
|
|
+ 'recharge_type' => [' JDpay ', 'JDpay', ' NOpay12 '], 'withdraw_type' => [' DF001 ']]);
|
|
|
+ $this->assertSame(0, $result['code']);
|
|
|
+ $this->assertSame('JDpay,NOpay12', DB::table('recharge_channel_group')->where('id', 1)->value('recharge_type'));
|
|
|
+ $this->assertSame('DF001', DB::table('recharge_channel_group')->where('id', 1)->value('withdraw_type'));
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_nested_option_objects_are_rejected_without_writing(): void
|
|
|
+ {
|
|
|
+ $result = $this->saveGroup(['id' => 1, 'name' => '组合',
|
|
|
+ 'recharge_type' => [['type' => 'JDpay', 'name' => 'JD钱包']], 'withdraw_type' => ['DF001']]);
|
|
|
+ $this->assertSame(-3, $result['code']);
|
|
|
+ $this->assertStringContainsString('type', $result['msg']);
|
|
|
+ $this->assertSame(0, DB::table('operation_audits')->count());
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_existing_deleted_type_is_removed_and_not_restored(): void
|
|
|
+ {
|
|
|
+ DB::table('recharge_channel')->where('id', 24)->update(['deleted_at' => now(), 'status' => 0]);
|
|
|
+ $this->assertSame(0, $this->saveGroup($this->groupPayload())['code']);
|
|
|
+ $saved = $this->groupPayload();
|
|
|
+ $this->assertNotContains('JDpay', $saved['recharge_type']);
|
|
|
+ $this->assertContains('JDpay', $saved['withdraw_type']);
|
|
|
+ $this->assertTrue(ChannelModel::withTrashed()->findOrFail(24)->trashed());
|
|
|
+ }
|
|
|
+
|
|
|
+ private function deleteGroup(int $id): array
|
|
|
+ {
|
|
|
+ $this->app->instance('request', Request::create('/admin/rechargeChannel/deleteGroup', 'POST', ['id' => $id]));
|
|
|
+ return (new RechargeChannel())->deleteGroup(new RechargeChannelConfigurationService())->getData(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_unreferenced_group_can_be_soft_deleted_without_deleting_channels(): void
|
|
|
+ {
|
|
|
+ $count = ChannelModel::query()->count();
|
|
|
+ $this->assertSame(0, $this->deleteGroup(2)['code']);
|
|
|
+ $this->assertNull(\App\Models\RechargeChannelGroup::query()->find(2));
|
|
|
+ $this->assertTrue(\App\Models\RechargeChannelGroup::withTrashed()->findOrFail(2)->trashed());
|
|
|
+ $this->assertSame($count, ChannelModel::query()->count());
|
|
|
+ $this->assertSame(0, $this->deleteGroup(2)['code']);
|
|
|
+ $this->assertSame(1, DB::table('operation_audits')->where('action', 'delete')->count());
|
|
|
+ $this->request('/admin/rechargeChannel/groupList', []);
|
|
|
+ $this->assertSame(1, (new RechargeChannel())->groupList()->getData(true)['data']['total']);
|
|
|
+ $this->assertSame(-3, $this->saveGroup(['id' => 2, 'name' => '已删除', 'recharge_type' => [], 'withdraw_type' => []])['code']);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_default_group_and_member_assigned_group_cannot_be_deleted(): void
|
|
|
+ {
|
|
|
+ $this->assertSame(-3, $this->deleteGroup(1)['code']);
|
|
|
+ $this->assertSame(-3, $this->deleteGroup(999)['code']);
|
|
|
+ DB::connection()->getSchemaBuilder()->create('users', function (Blueprint $t) {
|
|
|
+ $t->id(); $t->string('member_id'); $t->unsignedBigInteger('recharge_channel_group_id')->nullable(); $t->timestamps();
|
|
|
+ });
|
|
|
+ DB::table('users')->insert(['member_id' => 'test', 'recharge_channel_group_id' => 2]);
|
|
|
+ $result = $this->deleteGroup(2);
|
|
|
+ $this->assertSame(-3, $result['code']);
|
|
|
+ $this->assertStringContainsString('会员', $result['msg']);
|
|
|
+ $this->assertNotNull(\App\Models\RechargeChannelGroup::query()->find(2));
|
|
|
+ $this->assertSame(0, DB::table('operation_audits')->count());
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_numeric_and_string_payment_config_group_references_block_delete(): void
|
|
|
+ {
|
|
|
+ DB::connection()->getSchemaBuilder()->create('payment_collection_channels', function (Blueprint $t) {
|
|
|
+ $t->id(); $t->json('recharge_channel_group_ids'); $t->softDeletes();
|
|
|
+ });
|
|
|
+ foreach (['[2]', '["2"]', '["02"]'] as $json) {
|
|
|
+ DB::table('payment_collection_channels')->delete();
|
|
|
+ DB::table('payment_collection_channels')->insert(['recharge_channel_group_ids' => $json]);
|
|
|
+ $result = $this->deleteGroup(2);
|
|
|
+ $this->assertSame(-3, $result['code']);
|
|
|
+ $this->assertStringContainsString('被支付配置引用', $result['msg']);
|
|
|
+ $this->assertNotNull(\App\Models\RechargeChannelGroup::query()->find(2));
|
|
|
+ }
|
|
|
+ DB::table('payment_collection_channels')->update(['deleted_at' => now()]);
|
|
|
+ $this->assertSame(0, $this->deleteGroup(2)['code']);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_deleted_group_cannot_be_assigned_to_members_or_new_payment_config(): void
|
|
|
+ {
|
|
|
+ DB::connection()->getSchemaBuilder()->create('users', function (Blueprint $t) {
|
|
|
+ $t->id(); $t->string('member_id'); $t->unsignedBigInteger('recharge_channel_group_id')->nullable(); $t->timestamps();
|
|
|
+ });
|
|
|
+ DB::table('users')->insert(['member_id' => 'test', 'recharge_channel_group_id' => 1]);
|
|
|
+ $this->assertSame(0, $this->deleteGroup(2)['code']);
|
|
|
+ $this->app->instance('request', Request::create('/admin/user/setRechargeChannelGroup', 'POST',
|
|
|
+ ['member_id' => ['test'], 'recharge_channel_group_id' => 2]));
|
|
|
+ $this->assertSame(-3, (new \App\Http\Controllers\admin\User())->setRechargeChannelGroup()->getData(true)['code']);
|
|
|
+ $this->assertSame(1, DB::table('users')->where('member_id', 'test')->value('recharge_channel_group_id'));
|
|
|
+ $this->expectException(\RuntimeException::class);
|
|
|
+ $this->expectExceptionMessage('部分通道组合不存在');
|
|
|
+ (new PaymentChannelLinkService())->validate(24, [2], 1, true);
|
|
|
+ }
|
|
|
}
|