[ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', ], 'upstream_mirrors.connection' => 'business_mirror', 'upstream_mirrors.datasets' => [ CourierInformationMirrorRepository::DATASET_KEY => $this->definition('wp_express', 'js_wp_express', ['Id'], 'modified'), CourierInformationMirrorRepository::NETWORK_DATASET_KEY => $this->definition('wp_express_expNet', 'js_wp_express_exp_net', ['id']), CourierInformationMirrorRepository::ACCOUNT_DATASET_KEY => $this->definition('wp_express_account', 'js_wp_express_account', ['Id']), CourierInformationMirrorRepository::LOG_DATASET_KEY => $this->definition('wp_express_extension', 'js_wp_express_extension', ['Id'], 'modifyTime'), ], ]); DB::purge('business_mirror'); Schema::connection('business_mirror')->create('business_mirror_table_states', function (Blueprint $table): void { $table->string('dataset_key')->primary(); $table->string('status')->nullable(); $table->string('run_mode')->nullable(); $table->json('cursor')->nullable(); $table->json('high_watermark')->nullable(); $table->dateTime('initial_completed_at')->nullable(); $table->dateTime('last_succeeded_at')->nullable(); $table->unsignedBigInteger('mirror_row_count')->default(0); }); $this->createExpressTable(); $this->createRelatedTables(); $this->repository = new CourierInformationMirrorRepository( new BusinessMirrorDatasetRegistry(), new GenericBusinessMirrorRepository(app('db')), ); } public function test_it_filters_and_pages_mirrored_courier_records(): void { $this->markReady(2); $this->insertExpress(1, ['expcode' => 'SF', 'expname' => '顺丰', 'status' => 1]); $this->insertExpress(2, ['expcode' => 'YD', 'expname' => '韵达', 'status' => 0]); $result = $this->repository->paginate([ 'expcode' => 'SF', 'expname' => '', 'undefined1' => '', 'undefined2' => '', 'undefined3' => '', 'tradebtime_from' => '', 'tradebtime_to' => '', 'status' => '1', ], 1, 1); $this->assertSame(1, $result['total']); $this->assertSame('SF', $result['data'][0]->expcode); $this->assertTrue($this->repository->available()); $this->assertTrue($this->repository->ready()); } public function test_it_reads_networks_and_accounts_by_courier_code(): void { $this->markReady(1); $this->markDatasetReady(CourierInformationMirrorRepository::NETWORK_DATASET_KEY); $this->markDatasetReady(CourierInformationMirrorRepository::ACCOUNT_DATASET_KEY); $this->markDatasetReady(CourierInformationMirrorRepository::LOG_DATASET_KEY); DB::connection('business_mirror')->table('js_wp_express_exp_net')->insert([ '_mirror_key' => 'network-1', 'id' => 1, 'expid' => 1, 'expcode' => 'SF', 'netname' => '浦东网点', ]); DB::connection('business_mirror')->table('js_wp_express_account')->insert([ '_mirror_key' => 'account-1', 'Id' => 1, 'expcode' => 'SF', 'accountName' => '顺丰账户', ]); DB::connection('business_mirror')->table('js_wp_express_extension')->insert([ '_mirror_key' => 'log-1', 'Id' => 1, 'expcode' => 'SF', 'doTime' => 2, 'createTime' => '2026-08-28 10:00:00', 'modifyTime' => '2026-08-28 10:00:00', ]); $networks = $this->repository->paginateNetworks('SF', 1, 20); $accounts = $this->repository->paginateAccounts('SF', 1, 20); $logs = $this->repository->paginateLogs('SF', 1, 20); $this->assertSame(1, $networks['total']); $this->assertSame('浦东网点', $networks['data'][0]->netname); $this->assertSame(1, $accounts['total']); $this->assertSame('顺丰账户', $accounts['data'][0]->accountName); $this->assertSame(1, $logs['total']); $this->assertSame(2, $logs['data'][0]->doTime); } public function test_it_reads_esign_accounts_from_the_dzqzjbr_field(): void { $this->markReady(2); $this->insertExpress(1, [ 'expname' => '常州圆通其他', 'dzqzjbr' => '13800000001', ]); $this->insertExpress(2, [ 'expname' => '没有账号的快递', 'dzqzjbr' => null, ]); $accounts = $this->repository->signingAccountsByName([ '常州圆通其他', '没有账号的快递', ]); $this->assertSame(['常州圆通其他' => '13800000001'], $accounts); } public function test_it_returns_distinct_non_empty_courier_names_from_mirror_data(): void { $this->markReady(4); $this->insertExpress(1, ['expname' => '顺丰']); $this->insertExpress(2, ['expname' => '']); $this->insertExpress(3, ['expname' => '顺丰']); $this->insertExpress(4, ['expname' => '韵达']); $this->assertSame(['顺丰', '韵达'], $this->repository->names()); } public function test_contract_parties_use_source_fields_and_reject_conflicting_same_name_subjects(): void { $this->markReady(7); $this->insertExpress(1, ['expname' => '快递简称', 'tradebody' => ' 乙方物流有限公司 ', 'signcom' => '甲方商贸有限公司']); $this->insertExpress(2, ['expname' => '快递简称', 'tradebody' => '乙方物流有限公司', 'signcom' => '甲方商贸有限公司']); $this->insertExpress(3, ['expname' => '快递简称', 'tradebody' => '已删除的其他主体', 'is_delete' => 1]); $this->insertExpress(4, ['expname' => '冲突快递', 'tradebody' => '乙方一', 'signcom' => '甲方一']); $this->insertExpress(5, ['expname' => '冲突快递', 'tradebody' => '乙方二', 'signcom' => '甲方一']); $this->insertExpress(6, ['expname' => '未维护快递']); $this->insertExpress(7, ['expname' => '不在请求内快递', 'tradebody' => '不应返回']); $this->assertSame([ '快递简称' => ['party_b' => '乙方物流有限公司', 'party_a' => '甲方商贸有限公司', 'ambiguous' => false], '冲突快递' => ['party_b' => '', 'party_a' => '甲方一', 'ambiguous' => true], '未维护快递' => ['party_b' => '', 'party_a' => '', 'ambiguous' => false], ], $this->repository->contractPartiesByName(['快递简称', '冲突快递', '未维护快递'])); $row = $this->repository->paginate(['expcode' => 'EXP-1'], 1, 10)['data'][0]; $this->assertSame(' 乙方物流有限公司 ', $row->tradebody); $this->assertSame('甲方商贸有限公司', $row->signcom); $export = iterator_to_array($this->repository->exportRows(['expcode' => 'EXP-1'])); $this->assertSame($row->tradebody, $export[0]->tradebody); $this->assertSame($row->signcom, $export[0]->signcom); } public function test_contract_parties_remain_unavailable_until_mirror_initial_sync_is_complete(): void { $this->insertExpress(1, ['expname' => '未就绪快递', 'tradebody' => '不能使用的部分数据']); $this->assertSame([], $this->repository->contractPartiesByName(['未就绪快递'])); $this->markReady(1); $this->assertSame([], $this->repository->contractPartiesByName([])); } private function createExpressTable(): void { Schema::connection('business_mirror')->create('js_wp_express', function (Blueprint $table): void { $table->string('_mirror_key')->primary(); $table->integer('Id'); foreach (['expcode', 'expname', 'outcode', 'scexpcode', 'paymethod', 'tradebody', 'signcom', 'signmode', 'linker', 'linkmobile', 'paytype', 'undefined1', 'undefined2', 'undefined3', 'undefinedtype', 'undefined4', 'undefined5', 'dzqzjbr', 'guid', 'exp_properties'] as $column) { $table->string($column)->nullable(); } foreach (['status', 'addvalidsgn', 'kuaiyunsgn', 'autonosgn', 'packnosgn', 'skucountsplitsgn', 'weight0sgn', 'volume0sgn', 'handnosgn', 'decryptsgn', 'pj1sgn', 'is_delete'] as $column) { $table->integer($column)->nullable(); } foreach (['stopdate', 'tradebtime', 'tradeetime', 'modified'] as $column) { $table->dateTime($column)->nullable(); } }); } private function createRelatedTables(): void { Schema::connection('business_mirror')->create('js_wp_express_exp_net', function (Blueprint $table): void { $table->string('_mirror_key')->primary(); $table->integer('id'); $table->integer('expid')->nullable(); foreach (['expcode', 'netcode', 'netno', 'netname', 'isbill', 'province', 'city', 'area', 'town', 'address', 'linker', 'linkermobile', 'status', 'apitype', 'apipltmname', 'created', 'expnum'] as $column) { $table->string($column)->nullable(); } }); Schema::connection('business_mirror')->create('js_wp_express_account', function (Blueprint $table): void { $table->string('_mirror_key')->primary(); $table->integer('Id'); foreach (['expcode', 'expname', 'accountType', 'account', 'accountName', 'accountkhh', 'createTime', 'invoice', 'erpId', 'erpAccountId'] as $column) { $table->string($column)->nullable(); } }); Schema::connection('business_mirror')->create('js_wp_express_extension', function (Blueprint $table): void { $table->string('_mirror_key')->primary(); $table->integer('Id'); $table->string('expcode')->nullable(); $table->integer('doTime')->nullable(); $table->dateTime('createTime')->nullable(); $table->dateTime('modifyTime')->nullable(); }); } private function insertExpress(int $id, array $overrides): void { DB::connection('business_mirror')->table('js_wp_express')->insert(array_merge([ '_mirror_key' => 'express-' . $id, 'Id' => $id, 'expcode' => 'EXP-' . $id, 'expname' => '测试快递', 'status' => 1, 'modified' => '2026-08-28 10:00:00', ], $overrides)); } private function markReady(int $rows): void { $this->markDatasetReady(CourierInformationMirrorRepository::DATASET_KEY, $rows); } private function markDatasetReady(string $key, int $rows = 1): void { DB::connection('business_mirror')->table('business_mirror_table_states')->insert([ 'dataset_key' => $key, 'status' => 'succeeded', 'initial_completed_at' => '2026-08-28 10:00:00', 'last_succeeded_at' => '2026-08-28 10:00:00', 'mirror_row_count' => $rows, ]); } private function definition(string $object, string $target, array $keys, ?string $watermark = null): array { return [ 'source_key' => 'jiansun', 'schema' => 'dbo', 'object' => $object, 'target' => $target, 'keys' => $keys, 'watermark' => $watermark, ]; } }