[ 'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '', ], 'upstream_mirrors.connection' => 'business_mirror']); DB::purge('business_mirror'); $schema = Schema::connection('business_mirror'); $schema->create(GoodsProfitAfsLookupSchema::TABLE, function (Blueprint $table): void { $table->integer('id')->primary(); foreach ([ 'undefined1', 'undefined2', 'undefined7', 'undefined8', 'undefined10', 'undefined12', 'undefined13', 'undefined20', 'undefined21', 'undefined25', '_mirror_afs_order_time', ] as $column) { $table->string($column)->nullable(); } }); $schema->create('ops_jst_goods_profit_kpi_items_afs_schemas', function (Blueprint $table): void { $table->integer('id'); $table->string('column'); $table->string('name'); }); $schema->create('business_mirror_table_states', function (Blueprint $table): void { $table->string('dataset_key'); $table->dateTime('initial_completed_at')->nullable(); }); $db = DB::connection('business_mirror'); $db->table('business_mirror_table_states')->insert([ 'dataset_key' => GoodsProfitAfsRepository::MAIN_DATASET, 'initial_completed_at' => '2026-10-01 00:00:00', ]); foreach ([ 'undefined1' => '内部订单号', 'undefined2' => '线上订单号', 'undefined7' => '订单时间', 'undefined8' => '店铺名称', 'undefined10' => '商品编码', 'undefined12' => '店铺商品编码', 'undefined13' => '店铺款式编码', 'undefined20' => '商品数量', 'undefined25' => '商品金额', ] as $column => $name) { $db->table('ops_jst_goods_profit_kpi_items_afs_schemas')->insert(['id' => (int) substr($column, 9), 'column' => $column, 'name' => $name]); } $db->getPdo()->sqliteCreateFunction('STR_TO_DATE', static function (?string $date): ?string { return $date === null || trim($date) === '' ? null : date('Y-m-d H:i:s', strtotime($date)); }); for ($id = 1; $id <= 60; $id++) { $db->table(GoodsProfitAfsLookupSchema::TABLE)->insert([ 'id' => $id, 'undefined1' => 'ORDER-' . $id, 'undefined2' => 'ONLINE-' . $id, 'undefined7' => sprintf('2026/10/19 1:%d:00', $id - 1), '_mirror_afs_order_time' => sprintf('2026-10-19 01:%02d:00', $id - 1), 'undefined8' => $id % 2 ? '店铺甲' : '店铺乙', 'undefined10' => 'SKU_1', 'undefined25' => '123.45', ]); } } public function test_next_page_does_not_skip_the_lookahead_row(): void { $repo = new GoodsProfitAfsRepository(); $filters = ['from' => '2026-10-19', 'to' => '2026-10-19']; $pages = array_map(fn ($page) => $repo->paginate($filters, $page, 25), [1, 2, 3]); $orders = array_merge(...array_map(fn ($page) => array_column($page['rows'], 'internal_order_no'), $pages)); $this->assertSame(array_map(fn ($id) => 'ORDER-' . $id, range(60, 1)), $orders); $this->assertSame([true, true, false], array_column($pages, 'has_more')); $this->assertSame(123.45, $pages[0]['rows'][0]['product_amount']); } public function test_dates_before_goods_profit_coverage_skip_mirror_queries(): void { $db = DB::connection('business_mirror'); $db->flushQueryLog(); $db->enableQueryLog(); $covered = (new GoodsProfitAfsRepository())->covers([ 'from' => '2026-06-01', 'to' => '2026-06-30', 'order_no' => '', ]); $this->assertFalse($covered); $this->assertSame([], $db->getQueryLog()); } public function test_date_boundaries_and_multi_shop_filters_preserve_values(): void { $db = DB::connection('business_mirror'); foreach ([61 => ['2026/10/19 23:59:59', '2026-10-19 23:59:59'], 62 => ['2026/10/20 0:00:00', '2026-10-20 00:00:00'], 63 => ['2026/8/23 14:58:51', '2026-08-23 14:58:51']] as $id => [$raw, $parsed]) { $db->table(GoodsProfitAfsLookupSchema::TABLE)->insert([ 'id' => $id, 'undefined1' => 'ORDER-' . $id, 'undefined7' => $raw, '_mirror_afs_order_time' => $parsed, 'undefined8' => '店铺甲', 'undefined25' => '-31.20', ]); } $db->enableQueryLog(); $result = (new GoodsProfitAfsRepository())->paginate([ 'from' => '2026-10-19', 'to' => '2026-10-19', 'shop_names' => ['店铺甲', '店铺乙'], ], 1, 100); $this->assertCount(61, $result['rows']); $this->assertSame('ORDER-61', $result['rows'][0]['internal_order_no']); $this->assertSame(-31.2, $result['rows'][0]['product_amount']); $this->assertStringNotContainsString('STR_TO_DATE', implode(' ', array_column($db->getQueryLog(), 'query'))); } public function test_missing_lookup_keeps_the_original_parser(): void { Schema::connection('business_mirror')->table(GoodsProfitAfsLookupSchema::TABLE, function (Blueprint $table): void { $table->dropColumn('_mirror_afs_order_time'); }); $result = (new GoodsProfitAfsRepository())->paginate(['from' => '2026-10-19', 'to' => '2026-10-19'], 1, 25); $this->assertCount(25, $result['rows']); $this->assertSame('ORDER-60', $result['rows'][0]['internal_order_no']); } public function test_moved_dynamic_header_does_not_use_the_old_lookup_column(): void { Schema::connection('business_mirror')->table(GoodsProfitAfsLookupSchema::TABLE, function (Blueprint $table): void { $table->string('undefined9')->nullable(); }); $db = DB::connection('business_mirror'); $db->table(GoodsProfitAfsLookupSchema::TABLE)->update(['undefined9' => '2026/8/23 14:58:51']); $db->table('ops_jst_goods_profit_kpi_items_afs_schemas')->where('name', '订单时间')->update(['column' => 'undefined9']); $result = (new GoodsProfitAfsRepository())->paginate(['from' => '2026-08-23', 'to' => '2026-08-23'], 1, 25); $this->assertCount(25, $result['rows']); $this->assertSame('2026/8/23 14:58:51', $result['rows'][0]['order_time']); } public function test_positive_component_rows_are_returned_for_candidate_orders(): void { $db = DB::connection('business_mirror'); $db->table(GoodsProfitAfsLookupSchema::TABLE)->insert([ 'id' => 100, 'undefined1' => 'ORDER-100', 'undefined2' => 'ONLINE-100', 'undefined7' => '2026/9/1 10:00:00', 'undefined8' => '测试店铺', 'undefined10' => 'INTERNAL-COMPONENT', 'undefined12' => 'SHOP-SKU', 'undefined13' => 'SHOP-ITEM', 'undefined20' => '2', 'undefined25' => '199.98', ]); $db->table(GoodsProfitAfsLookupSchema::TABLE)->insert([ 'id' => 101, 'undefined1' => 'ORDER-100', 'undefined2' => 'ONLINE-100', 'undefined7' => '', 'undefined8' => '测试店铺', 'undefined10' => 'ZERO-PLACEHOLDER', 'undefined12' => 'SHOP-SKU', 'undefined13' => 'SHOP-ITEM', 'undefined20' => '0', 'undefined25' => '0', ]); $rows = (new GoodsProfitAfsRepository())->positiveComponentRowsForOrders(['ONLINE-100']); $this->assertSame([[ 'online_order_no' => 'ONLINE-100', 'shop_name' => '测试店铺', 'shop_sku_id' => 'SHOP-SKU', 'shop_i_id' => 'SHOP-ITEM', 'product_code' => 'INTERNAL-COMPONENT', 'product_quantity' => 2.0, 'product_amount' => 199.98, ]], $rows); } }