[ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', ], 'upstream_mirrors.connection' => 'business_mirror', 'upstream_mirrors.datasets' => [ StoreReceivingAlipayBillMirrorRepository::DATASET => [ 'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => 'alipay_Bill_Detail', 'target' => 'js_alipay_bill_detail', 'keys' => ['Id'], ], ], ]); DB::purge('business_mirror'); Schema::connection('business_mirror')->create('business_mirror_table_states', function (Blueprint $table): void { $table->string('dataset_key')->primary(); $table->dateTime('initial_completed_at')->nullable(); $table->string('status')->nullable(); $table->dateTime('last_succeeded_at')->nullable(); }); Schema::connection('business_mirror')->create('js_alipay_bill_detail', function (Blueprint $table): void { $table->integer('Id'); foreach ([ 'accountNum', 'businessNum', 'merchantOrder', 'commodityName', 'oppAccount', 'tradeChannel', 'businessType', 'remark', 'shopName', 'busiBillSource', 'busiDescription', 'busiNum', 'busiBasicNum', 'buyerRemark', 'storeNum', 'appId', 'account', 'bindingType', ] as $column) { $table->string($column)->nullable(); } $table->dateTime('happenTime')->nullable(); $table->date('date')->nullable(); $table->decimal('inAmount', 20, 6)->nullable(); $table->decimal('outAmount', 20, 6)->nullable(); $table->decimal('accountAmount', 20, 6)->nullable(); }); $this->repository = new StoreReceivingAlipayBillMirrorRepository( new BusinessMirrorDatasetRegistry(), new GenericBusinessMirrorRepository(app('db')), ); } public function test_it_returns_filtered_pages_and_summary_using_attachment_field_names(): void { $this->markReady(); DB::connection('business_mirror')->table('js_alipay_bill_detail')->insert([ $this->row(1, '天猫-测试店', '2026-08-01 09:00:00', 100.25, 0, 500.25), $this->row(2, '天猫-测试店', '2026-08-02 09:00:00', 0, -20.10, 480.15), $this->row(3, '其他店铺', '2026-08-03 09:00:00', 88, 0, 88), ]); $result = $this->repository->paginate( ['store_name' => '天猫-测试店', 'start' => '2026-08-01', 'end' => '2026-08-31'], 'occurred_at', 'desc', 2, 1, ); $this->assertSame(2, $result['total']); $this->assertSame(2, $result['current_page']); $this->assertSame('1', $result['data'][0]['record_code']); $this->assertSame('2088000000000000', $result['data'][0]['alipay_account']); $this->assertSame(100.25, $result['summary']['income']); $this->assertSame(20.10, $result['summary']['expense']); $this->assertSame(480.15, $result['summary']['balance']); $this->assertSame(2, $result['summary']['count']); $this->assertSame('2026-09-07 09:00:00', $result['sync']['last_succeeded_at']); } public function test_it_rejects_an_unready_mirror_instead_of_querying_upstream_from_http(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('store_receiving_alipay_bill_mirror_not_ready'); $this->repository->paginate([], 'occurred_at', 'desc', 1, 10); } private function markReady(): void { DB::connection('business_mirror')->table('business_mirror_table_states')->insert([ 'dataset_key' => StoreReceivingAlipayBillMirrorRepository::DATASET, 'initial_completed_at' => '2026-09-07 09:00:00', 'status' => 'succeeded', 'last_succeeded_at' => '2026-09-07 09:00:00', ]); } private function row(int $id, string $shop, string $time, float $income, float $expense, float $balance): array { return [ 'Id' => $id, 'accountNum' => 'ACCOUNT-' . $id, 'businessNum' => 'BUSINESS-' . $id, 'merchantOrder' => 'MERCHANT-' . $id, 'commodityName' => '商品' . $id, 'happenTime' => $time, 'oppAccount' => '对方账号', 'inAmount' => $income, 'outAmount' => $expense, 'accountAmount' => $balance, 'tradeChannel' => '交易收款', 'businessType' => '交易付款', 'remark' => '备注', 'shopName' => $shop, 'date' => substr($time, 0, 10), 'busiBillSource' => '支付宝', 'busiDescription' => '业务说明', 'busiNum' => 'BILL-' . $id, 'busiBasicNum' => 'BASE-' . $id, 'buyerRemark' => '自定义备注', 'storeNum' => 'ORDER-' . $id, 'appId' => '2088000000000000', 'account' => 'account@example.test', 'bindingType' => '淘系', ]; } }