572 lines
28 KiB
PHP
572 lines
28 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\EcommerceFinance;
|
|
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Modules\EcommerceFinance\Repositories\GenericBusinessMirrorRepository;
|
|
use Modules\EcommerceFinance\Repositories\StoreReceivingAlipayBillMirrorRepository;
|
|
use Modules\EcommerceFinance\Repositories\StoreReceivingDouyinBillMirrorRepository;
|
|
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
|
|
use RuntimeException;
|
|
use Tests\TestCase;
|
|
|
|
class StoreReceivingAlipayBillMirrorRepositoryTest extends TestCase
|
|
{
|
|
private StoreReceivingAlipayBillMirrorRepository $repository;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
config([
|
|
'database.connections.business_mirror' => [
|
|
'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '',
|
|
],
|
|
'upstream_mirrors.connection' => 'business_mirror',
|
|
'upstream_mirrors.datasets' => [
|
|
StoreReceivingAlipayBillMirrorRepository::DATASET => [
|
|
'source_key' => 'operations_replica', 'schema' => 'rpt',
|
|
'object' => 'jst_originalBillInfo', 'target' => 'ops_jst_original_bill_info',
|
|
'keys' => ['Id'],
|
|
],
|
|
StoreReceivingAlipayBillMirrorRepository::SHOP_DATASET => [
|
|
'source_key' => 'operations_replica', 'schema' => 'rpt',
|
|
'object' => 'jst_shopInfo', 'target' => 'ops_jst_shop_info', 'keys' => ['Id'],
|
|
],
|
|
],
|
|
]);
|
|
DB::purge('business_mirror');
|
|
$schema = Schema::connection('business_mirror');
|
|
$schema->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();
|
|
$table->unsignedBigInteger('mirror_row_count')->nullable();
|
|
$table->boolean('mirror_row_count_is_exact')->default(false);
|
|
});
|
|
$schema->create('ops_jst_original_bill_info', function (Blueprint $table): void {
|
|
$table->integer('Id');
|
|
$table->integer('shop_id')->nullable();
|
|
foreach (['channel', 'primary_key', 'fee_name', 'inout_type', 'p_auto_id', 'shop_i_id', 'shop_sku_id', 'sku_id', 'so_id', 'combine_type'] as $column) {
|
|
$table->string($column)->nullable();
|
|
}
|
|
$table->dateTime('fee_time')->nullable();
|
|
$table->dateTime('createTime')->nullable();
|
|
$table->dateTime('updateTime')->nullable();
|
|
$table->bigInteger('auto_id')->nullable();
|
|
$table->decimal('amount', 20, 6)->nullable();
|
|
});
|
|
$schema->create('ops_jst_shop_info', function (Blueprint $table): void {
|
|
$table->integer('Id');
|
|
$table->integer('shop_id')->nullable();
|
|
$table->string('shop_name')->nullable();
|
|
$table->string('shop_site')->nullable();
|
|
});
|
|
|
|
$this->repository = new StoreReceivingAlipayBillMirrorRepository(
|
|
new BusinessMirrorDatasetRegistry(), new GenericBusinessMirrorRepository(app('db')),
|
|
);
|
|
}
|
|
|
|
public function test_it_reads_operations_replica_rows_and_maps_shop_and_flow_fields(): void
|
|
{
|
|
$this->markReady();
|
|
DB::connection('business_mirror')->table('ops_jst_shop_info')->insert([
|
|
['Id' => 10, 'shop_id' => 7, 'shop_name' => '天猫-经营店'],
|
|
['Id' => 11, 'shop_id' => 8, 'shop_name' => '淘宝-其他店'],
|
|
]);
|
|
DB::connection('business_mirror')->table('ops_jst_original_bill_info')->insert([
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 100.25),
|
|
$this->row(2, 7, '2026-08-02 09:00:00', '-1', 20.10),
|
|
$this->row(3, 8, '2026-08-03 09:00:00', '1', 88),
|
|
]);
|
|
|
|
$result = $this->repository->paginate(
|
|
['store_name' => '天猫-经营店', 'start' => '2026-08-01', 'end' => '2026-08-31'],
|
|
'occurred_at', 'desc', 1, 10,
|
|
);
|
|
|
|
$this->assertSame(2, $result['total']);
|
|
$this->assertSame('P-2', $result['data'][0]['record_code']);
|
|
$this->assertSame('天猫-经营店', $result['data'][0]['store_name']);
|
|
$this->assertNull($result['data'][0]['business_type']);
|
|
$this->assertNull($result['data'][0]['alipay_account']);
|
|
$this->assertNull($result['data'][0]['balance']);
|
|
$this->assertSame(100.25, $result['summary']['income']);
|
|
$this->assertSame(20.10, $result['summary']['expense']);
|
|
$this->assertSame(80.15, $result['summary']['balance']);
|
|
$this->assertSame(2, $result['summary']['count']);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
public function test_imported_attachment_rows_enrich_matching_mirror_rows_for_the_taobao_page(): void
|
|
{
|
|
$this->markReady();
|
|
DB::connection('business_mirror')->table('ops_jst_shop_info')->insert([
|
|
'Id' => 10, 'shop_id' => 7, 'shop_name' => '天猫-经营店', 'shop_site' => '淘宝天猫',
|
|
]);
|
|
DB::connection('business_mirror')->table('ops_jst_original_bill_info')->insert(
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 100.25),
|
|
);
|
|
DB::table('ec_alipay_bills')->insert([
|
|
'company_id' => 1, 'record_code' => 'P-1', 'store_name' => '天猫-附件店铺',
|
|
'custom_remark' => '模板数据', 'bill_item' => '交易收款', 'alipay_account' => '20880001',
|
|
'account_order_no' => 'ACCOUNT-1', 'business_serial_no' => 'SERIAL-1',
|
|
'merchant_order_no' => 'MERCHANT-1', 'business_base_order_no' => 'BASE-1',
|
|
'goods_name' => '附件商品', 'counterparty_account' => '买家',
|
|
'occurred_at' => '2026-08-01 09:00:00', 'income' => 100.25, 'expense' => 0, 'balance' => 300.50,
|
|
'business_type' => '交易付款', 'remark' => '备注', 'online_order_no' => 'ONLINE-1',
|
|
'online_real_store' => '天猫-附件店铺', 'business_description' => '业务描述',
|
|
'business_bill_source' => '支付宝', 'business_order_no' => 'BUSINESS-1',
|
|
'modifier' => '导入人', 'platform' => 'taobao', 'created_from' => 'attachment-import',
|
|
'created_at' => '2026-08-02 09:00:00', 'updated_at' => '2026-08-02 10:00:00',
|
|
]);
|
|
|
|
$result = $this->repository->paginate([], 'occurred_at', 'desc', 1, 10);
|
|
|
|
$this->assertSame(1, $result['total']);
|
|
$this->assertSame('P-1', $result['data'][0]['record_code']);
|
|
$this->assertSame('天猫-经营店', $result['data'][0]['store_name']);
|
|
$this->assertSame('20880001', $result['data'][0]['alipay_account']);
|
|
$this->assertSame(300.50, $result['data'][0]['balance']);
|
|
$this->assertSame(100.25, $result['summary']['income']);
|
|
$this->assertSame('succeeded', $result['sync']['status']);
|
|
}
|
|
|
|
public function test_export_uses_the_same_filters_and_mapping_as_the_page(): void
|
|
{
|
|
$this->markReady();
|
|
DB::connection('business_mirror')->table('ops_jst_shop_info')->insert([
|
|
'Id' => 10, 'shop_id' => 7, 'shop_name' => '天猫-经营店',
|
|
]);
|
|
DB::connection('business_mirror')->table('ops_jst_original_bill_info')->insert([
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 100.25),
|
|
$this->row(2, 7, '2026-08-02 09:00:00', '-1', 20.10),
|
|
]);
|
|
|
|
$rows = iterator_to_array($this->repository->exportRows([
|
|
'business_type' => '支出', 'source_channel' => 'alipayCSV', 'keyword' => 'SKU-2',
|
|
]));
|
|
|
|
$this->assertCount(1, $rows);
|
|
$this->assertSame('P-2', $rows[0]['record_code']);
|
|
$this->assertSame(20.10, $rows[0]['expense']);
|
|
}
|
|
|
|
public function test_taobao_page_and_export_exclude_other_platform_shops(): void
|
|
{
|
|
$this->markReady();
|
|
$db = DB::connection('business_mirror');
|
|
$sites = [
|
|
1 => '淘宝天猫',
|
|
2 => '拼多多',
|
|
3 => '京东商城',
|
|
4 => '头条放心购',
|
|
5 => '得物',
|
|
6 => '唯品会MP',
|
|
7 => '小红书',
|
|
];
|
|
foreach ($sites as $shopId => $site) {
|
|
$db->table('ops_jst_shop_info')->insert([
|
|
'Id' => $shopId,
|
|
'shop_id' => $shopId,
|
|
'shop_name' => $site . '店铺',
|
|
'shop_site' => $site,
|
|
]);
|
|
$db->table('ops_jst_original_bill_info')->insert(
|
|
$this->row($shopId, $shopId, '2026-08-01 09:00:00', '1', 10),
|
|
);
|
|
}
|
|
|
|
$result = $this->repository->paginate([], 'occurred_at', 'asc', 1, 20);
|
|
|
|
$this->assertSame(2, $result['total']);
|
|
$this->assertSame(['淘宝天猫店铺', '小红书店铺'], array_column($result['data'], 'store_name'));
|
|
$this->assertSame(20.0, $result['summary']['income']);
|
|
$this->assertSame(2, $result['summary']['count']);
|
|
$this->assertSame(
|
|
['淘宝天猫店铺', '小红书店铺'],
|
|
array_column(iterator_to_array($this->repository->exportRows([], 'occurred_at', 'asc')), 'store_name'),
|
|
);
|
|
}
|
|
|
|
public function test_channel_pages_keep_their_own_platform_rows(): void
|
|
{
|
|
$this->markReady();
|
|
$db = DB::connection('business_mirror');
|
|
$db->table('ops_jst_shop_info')->insert([
|
|
'Id' => 1, 'shop_id' => 1, 'shop_name' => '拼多多店铺', 'shop_site' => '拼多多',
|
|
]);
|
|
$db->table('ops_jst_original_bill_info')->insert(
|
|
$this->row(1, 1, '2026-08-01 09:00:00', '1', 10, 'PinduoduoImport'),
|
|
);
|
|
|
|
$result = $this->repository->paginateChannel(
|
|
[], 'occurred_at', 'asc', 1, 20, 'PinduoduoImport', 'pinduoduo',
|
|
);
|
|
|
|
$this->assertSame(1, $result['total']);
|
|
$this->assertSame('拼多多店铺', $result['data'][0]['store_name']);
|
|
}
|
|
|
|
public function test_export_batches_preserve_sort_ties_nulls_and_channel_filter(): void
|
|
{
|
|
$this->markReady();
|
|
$db = DB::connection('business_mirror');
|
|
$table = $db->table('ops_jst_original_bill_info');
|
|
$fixtures = [];
|
|
for ($id = 1; $id <= 2105; $id++) {
|
|
$row = $this->row($id, 7, '2026-08-01 09:00:00', '1', 10);
|
|
$row['fee_time'] = $id <= 1005 ? null : '2026-08-01 09:00:00';
|
|
$row['amount'] = $id <= 1005 ? null : 10;
|
|
$fixtures[] = $row;
|
|
}
|
|
foreach (array_chunk($fixtures, 100) as $chunk) $table->insert($chunk);
|
|
$table->insert($this->row(9999, 7, '2026-08-01 09:00:00', '1', 10, 'OtherChannel'));
|
|
foreach (['occurred_at' => 'fee_time', 'income' => 'amount', 'record_code' => 'p_auto_id'] as $sort => $column) {
|
|
foreach (['asc', 'desc'] as $direction) {
|
|
$expected = (clone $table)->where('channel', 'CommonBillTGCPay')
|
|
->orderBy($column, $direction)->orderBy('Id', $direction)->pluck('Id')->map('strval')->all();
|
|
$db->flushQueryLog();
|
|
$db->enableQueryLog();
|
|
$rows = iterator_to_array($this->repository->exportChannelRows([], $sort, $direction, 'CommonBillTGCPay'));
|
|
$this->assertSame($expected, array_column($rows, 'record_code'));
|
|
$reads = array_filter($db->getQueryLog(), fn ($query) => str_contains($query['query'], 'select "b".*'));
|
|
$this->assertNotEmpty($reads);
|
|
foreach ($reads as $read) $this->assertStringContainsString('limit 1000', $read['query']);
|
|
$db->disableQueryLog();
|
|
}
|
|
}
|
|
}
|
|
|
|
public function test_export_does_not_chase_new_rows_inserted_after_it_starts(): void
|
|
{
|
|
$this->markReady();
|
|
$table = DB::connection('business_mirror')->table('ops_jst_original_bill_info');
|
|
for ($id = 1; $id <= 1001; $id++) {
|
|
$table->insert($this->row($id, 7, '2026-08-01 09:00:00', '1', 10));
|
|
}
|
|
$ids = [];
|
|
foreach ($this->repository->exportRows([], 'record_code', 'asc') as $row) {
|
|
$ids[] = $row['record_code'];
|
|
if (count($ids) === 1) $table->insert($this->row(9999, 7, '2026-08-01 09:00:00', '1', 10));
|
|
}
|
|
$this->assertCount(1001, $ids);
|
|
$this->assertNotContains('9999', $ids);
|
|
}
|
|
|
|
public function test_channel_index_migration_and_rollback_preserve_rows(): void
|
|
{
|
|
config(['system_database.mode' => 'connected']);
|
|
$table = DB::connection('business_mirror')->table('ops_jst_original_bill_info');
|
|
$table->insert($this->row(1, 7, '2026-08-01 09:00:00', '1', 10, str_repeat('A', 101)));
|
|
$migration = require base_path('database/migrations/2026_09_09_170000_index_original_bill_channel_queries.php');
|
|
$before = (array) $table->first();
|
|
$migration->up();
|
|
$migration->up();
|
|
$this->assertTrue(Schema::connection('business_mirror')->hasIndex('ops_jst_original_bill_info', 'idx_original_bill_channel_time'));
|
|
$this->assertEquals($before, (array) $table->first());
|
|
$migration->down();
|
|
$this->assertFalse(Schema::connection('business_mirror')->hasIndex('ops_jst_original_bill_info', 'idx_original_bill_channel_time'));
|
|
$this->assertEquals($before, (array) $table->first());
|
|
}
|
|
|
|
public function test_shop_filter_index_migration_and_rollback_preserve_rows(): void
|
|
{
|
|
config(['system_database.mode' => 'connected']);
|
|
$table = DB::connection('business_mirror')->table('ops_jst_original_bill_info');
|
|
$table->insert($this->row(1, 7, '2026-08-01 09:00:00', '1', 10));
|
|
$before = (array) $table->first();
|
|
$migration = require base_path('database/migrations/2026_09_09_190000_index_account_bill_shop_filters.php');
|
|
$migration->up();
|
|
$migration->up();
|
|
$this->assertTrue(Schema::connection('business_mirror')->hasIndex('ops_jst_original_bill_info', 'idx_original_bill_channel_shop'));
|
|
$this->assertEquals($before, (array) $table->first());
|
|
$migration->down();
|
|
$this->assertFalse(Schema::connection('business_mirror')->hasIndex('ops_jst_original_bill_info', 'idx_original_bill_channel_shop'));
|
|
$this->assertEquals($before, (array) $table->first());
|
|
}
|
|
|
|
public function test_taobao_scope_does_not_use_the_cross_platform_daily_summary(): void
|
|
{
|
|
$this->markReady();
|
|
DB::connection('business_mirror')->table('ops_jst_shop_info')->insert([
|
|
'Id' => 10, 'shop_id' => 7, 'shop_name' => '天猫-经营店',
|
|
]);
|
|
DB::connection('business_mirror')->table('ops_jst_original_bill_info')->insert(
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 100.25),
|
|
);
|
|
Schema::connection('business_mirror')->create('store_report_import_states', function (Blueprint $table): void {
|
|
$table->string('dataset_key')->primary();
|
|
$table->string('active_generation')->nullable();
|
|
$table->dateTime('initial_completed_at')->nullable();
|
|
});
|
|
Schema::connection('business_mirror')->create('store_report_daily_rows', function (Blueprint $table): void {
|
|
$table->string('generation');
|
|
$table->string('store_name');
|
|
$table->date('occurred_on');
|
|
$table->decimal('income', 20, 4);
|
|
$table->decimal('expense', 20, 4);
|
|
});
|
|
DB::connection('business_mirror')->table('store_report_import_states')->insert([
|
|
'dataset_key' => 'store-report.daily-sales', 'active_generation' => 'daily-v1',
|
|
'initial_completed_at' => '2026-09-01 09:00:00',
|
|
]);
|
|
DB::connection('business_mirror')->table('store_report_daily_rows')->insert([
|
|
'generation' => 'daily-v1', 'store_name' => '天猫-经营店', 'occurred_on' => '2026-08-01',
|
|
'income' => 100.25, 'expense' => 20.10,
|
|
]);
|
|
|
|
$result = $this->repository->paginate([
|
|
'store_name' => '天猫-经营店', 'start' => '2026-08-01', 'end' => '2026-08-01',
|
|
], 'occurred_at', 'desc', 1, 10);
|
|
|
|
$this->assertSame(['income' => 100.25, 'expense' => 0.0, 'balance' => 100.25, 'count' => 1], $result['summary']);
|
|
}
|
|
|
|
public function test_taobao_scoped_pages_share_one_aggregate_across_sync_state_churn(): void
|
|
{
|
|
$this->markReady();
|
|
$db = DB::connection('business_mirror');
|
|
$db->table('ops_jst_original_bill_info')->insert([
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 10),
|
|
$this->row(2, 7, '2026-08-02 09:00:00', '-1', 5),
|
|
]);
|
|
$state = $db->table('business_mirror_table_states')->where('dataset_key', StoreReceivingAlipayBillMirrorRepository::DATASET);
|
|
$state->update(['mirror_row_count' => 2]);
|
|
$db->enableQueryLog();
|
|
$first = $this->repository->paginate([], 'occurred_at', 'desc', 1, 1);
|
|
$second = $this->repository->paginate(['store_name' => '', 'start' => null], 'occurred_at', 'desc', 2, 1);
|
|
$this->assertSame(2, $first['total']);
|
|
$this->assertSame(2, $second['total']);
|
|
$this->assertNotSame($first['data'][0]['id'], $second['data'][0]['id']);
|
|
$aggregateQueries = array_values(array_filter(
|
|
$db->getQueryLog(),
|
|
static fn (array $query): bool => str_contains(strtolower($query['query']), 'count(*)'),
|
|
));
|
|
$this->assertCount(1, $aggregateQueries);
|
|
foreach ($aggregateQueries as $query) {
|
|
$this->assertStringContainsString('sum(case', strtolower($query['query']));
|
|
}
|
|
$db->disableQueryLog();
|
|
|
|
$db->table('ops_jst_original_bill_info')->insert($this->row(3, 7, '2026-08-03 09:00:00', '1', 4));
|
|
$state->update(['mirror_row_count' => 3, 'last_succeeded_at' => '2026-09-08 09:00:00']);
|
|
$this->assertSame(2, $this->repository->paginate([], 'occurred_at', 'desc', 1, 1)['total']);
|
|
$this->assertSame(1, $this->repository->paginate(['business_type' => '支出'], 'occurred_at', 'desc', 1, 1)['total']);
|
|
|
|
Cache::flush();
|
|
$this->assertSame(3, $this->repository->paginate([], 'occurred_at', 'desc', 1, 1)['total']);
|
|
|
|
$state->update(['status' => 'running', 'mirror_row_count' => 2]);
|
|
$this->assertSame(3, $this->repository->paginate([], 'occurred_at', 'desc', 1, 1)['total']);
|
|
$state->update(['status' => 'failed']);
|
|
$this->assertSame(3, $this->repository->paginate([], 'occurred_at', 'desc', 1, 1)['total']);
|
|
$state->update(['status' => 'running', 'mirror_row_count' => 3, 'mirror_row_count_is_exact' => true]);
|
|
$db->flushQueryLog();
|
|
$db->enableQueryLog();
|
|
$this->assertSame(3, $this->repository->paginate([], 'occurred_at', 'desc', 1, 1)['total']);
|
|
$this->assertCount(0, array_filter($db->getQueryLog(), fn ($q) => str_contains(strtolower($q['query']), 'count(*)')));
|
|
$db->disableQueryLog();
|
|
$state->update(['status' => 'succeeded', 'mirror_row_count' => null]);
|
|
$this->assertSame(3, $this->repository->paginate([], 'occurred_at', 'desc', 1, 1)['total']);
|
|
}
|
|
|
|
public function test_douyin_reads_only_toutiao_fangxin_gou_rows_and_maps_flow_fields(): void
|
|
{
|
|
$this->markReady();
|
|
$db = DB::connection('business_mirror');
|
|
$db->table('ops_jst_shop_info')->insert([
|
|
'Id' => 10, 'shop_id' => 7, 'shop_name' => '放心购旗舰店',
|
|
]);
|
|
$db->table('ops_jst_original_bill_info')->insert([
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 100.25, 'TouTiaoFXGZJ'),
|
|
$this->row(2, 7, '2026-08-02 09:00:00', '-1', 20.10, 'alipayCSV'),
|
|
$this->row(3, 7, '2026-08-02 10:00:00', '1', 55.40, 'TouTiaoFXGZJ'),
|
|
]);
|
|
|
|
$douyin = new StoreReceivingDouyinBillMirrorRepository($this->repository);
|
|
$result = $douyin->paginate([], 'occurred_at', 'desc', 1, 10);
|
|
|
|
$this->assertSame(2, $result['total']);
|
|
$this->assertSame('3', $result['data'][0]['record_code']);
|
|
$this->assertSame('放心购旗舰店', $result['data'][0]['store_name']);
|
|
$this->assertSame('TouTiaoFXGZJ', $result['data'][0]['payload']['settle_account']);
|
|
$this->assertSame('头条放心购', $result['data'][0]['payload']['move_scene']);
|
|
$this->assertSame(155.65, $result['summary']['income']);
|
|
$this->assertSame(0.0, $result['summary']['expense']);
|
|
|
|
$filtered = $douyin->paginate([
|
|
'store_name' => '放心购',
|
|
'bill_item' => '交易佣',
|
|
'start' => '2026-08-01',
|
|
'end' => '2026-08-01',
|
|
], 'occurred_at', 'desc', 1, 10);
|
|
|
|
$this->assertSame(1, $filtered['total']);
|
|
$this->assertSame('1', $filtered['data'][0]['record_code']);
|
|
|
|
$rows = iterator_to_array($douyin->exportRows([], 'occurred_at', 'asc'));
|
|
$this->assertCount(2, $rows);
|
|
$this->assertSame('1', $rows[0]['record_code']);
|
|
}
|
|
|
|
public function test_douyin_bill_item_filter_pages_through_ids_and_preserves_order(): void
|
|
{
|
|
$this->markReady();
|
|
$db = DB::connection('business_mirror');
|
|
$db->table('ops_jst_original_bill_info')->insert([
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 10, 'TouTiaoFXGZJ'),
|
|
$this->row(2, 7, '2026-08-03 09:00:00', '1', 20, 'TouTiaoFXGZJ'),
|
|
$this->row(3, 7, '2026-08-02 09:00:00', '1', 30, 'TouTiaoFXGZJ'),
|
|
]);
|
|
$db->flushQueryLog();
|
|
$db->enableQueryLog();
|
|
|
|
$result = (new StoreReceivingDouyinBillMirrorRepository($this->repository))->paginate(
|
|
['bill_item' => '交易佣'], 'occurred_at', 'desc', 1, 2,
|
|
);
|
|
|
|
$this->assertSame(['2', '3'], array_column($result['data'], 'record_code'));
|
|
$queries = $db->getQueryLog();
|
|
$this->assertCount(1, array_filter($queries, static fn (array $query): bool => str_contains($query['query'], 'select "b"."Id"')));
|
|
$this->assertCount(1, array_filter($queries, static fn (array $query): bool => str_contains($query['query'], 'select "b".*')
|
|
&& str_contains($query['query'], 'where "b"."Id" in')));
|
|
$db->disableQueryLog();
|
|
}
|
|
|
|
public function test_douyin_filter_options_only_include_the_fixed_channel(): void
|
|
{
|
|
$this->markReady();
|
|
$db = DB::connection('business_mirror');
|
|
$db->table('ops_jst_shop_info')->insert([
|
|
['Id' => 10, 'shop_id' => 7, 'shop_name' => '放心购A店'],
|
|
['Id' => 11, 'shop_id' => 8, 'shop_name' => '放心购B店'],
|
|
['Id' => 12, 'shop_id' => 9, 'shop_name' => '淘系店铺'],
|
|
]);
|
|
$db->table('ops_jst_original_bill_info')->insert([
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 10, 'TouTiaoFXGZJ', '交易佣金'),
|
|
$this->row(2, 8, '2026-08-02 09:00:00', '1', 20, 'TouTiaoFXGZJ', '订单收入'),
|
|
$this->row(3, 7, '2026-08-03 09:00:00', '1', 30, 'TouTiaoFXGZJ', '交易佣金'),
|
|
$this->row(4, 9, '2026-08-04 09:00:00', '1', 40, 'alipayCSV', '淘系费用'),
|
|
]);
|
|
|
|
$options = (new StoreReceivingDouyinBillMirrorRepository($this->repository))->filterOptions();
|
|
|
|
$this->assertSame(['放心购A店', '放心购B店'], $options['shops']);
|
|
$this->assertSame(['交易佣金', '订单收入'], $options['bill_items']);
|
|
}
|
|
|
|
public function test_alipay_filter_options_match_the_visible_taobao_channel_and_platform_scope(): void
|
|
{
|
|
Cache::flush();
|
|
$this->markReady();
|
|
$db = DB::connection('business_mirror');
|
|
$db->table('ops_jst_shop_info')->insert([
|
|
['Id' => 10, 'shop_id' => 7, 'shop_name' => '天猫A店', 'shop_site' => '淘宝天猫'],
|
|
['Id' => 11, 'shop_id' => 8, 'shop_name' => '拼多多B店', 'shop_site' => '拼多多'],
|
|
['Id' => 12, 'shop_id' => 9, 'shop_name' => '天猫C店', 'shop_site' => '淘宝天猫'],
|
|
]);
|
|
$db->table('ops_jst_original_bill_info')->insert([
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 10, 'alipayCSV', '交易收款'),
|
|
$this->row(2, 8, '2026-08-01 10:00:00', '1', 20, 'alipayCSV', '其他平台费用'),
|
|
$this->row(3, 9, '2026-08-01 11:00:00', '1', 30, 'OtherChannel', '其他渠道费用'),
|
|
]);
|
|
|
|
$db->flushQueryLog();
|
|
$db->enableQueryLog();
|
|
$options = $this->repository->filterOptionsForChannel(
|
|
'alipayCSV', StoreReceivingAlipayBillMirrorRepository::DATASET, true
|
|
);
|
|
$cachedOptions = $this->repository->filterOptionsForChannel(
|
|
'alipayCSV', StoreReceivingAlipayBillMirrorRepository::DATASET, true
|
|
);
|
|
|
|
$this->assertSame(['天猫A店'], $options['shops']);
|
|
$this->assertSame(['交易收款'], $options['bill_items']);
|
|
$this->assertSame($options, $cachedOptions);
|
|
$optionQueries = array_filter(
|
|
$db->getQueryLog(),
|
|
static fn (array $query): bool => str_contains($query['query'], 'select distinct')
|
|
&& (str_contains($query['query'], '"b"."shop_id"') || str_contains($query['query'], '"b"."fee_name"')),
|
|
);
|
|
$this->assertCount(2, $optionQueries, '相同筛选范围只应扫描一次店铺和账单项目选项。');
|
|
$db->disableQueryLog();
|
|
}
|
|
|
|
public function test_shared_dataset_keeps_channel_predicates_for_all_query_entry_points(): void
|
|
{
|
|
$this->markReady();
|
|
$db = DB::connection('business_mirror');
|
|
$db->table('ops_jst_shop_info')->insert([
|
|
['Id' => 10, 'shop_id' => 7, 'shop_name' => '淘系店铺'],
|
|
['Id' => 11, 'shop_id' => 8, 'shop_name' => '其他店铺'],
|
|
]);
|
|
$db->table('ops_jst_original_bill_info')->insert([
|
|
$this->row(1, 7, '2026-08-01 09:00:00', '1', 100.25, 'CommonBillTGCPay', '交易收入'),
|
|
$this->row(2, 8, '2026-08-02 09:00:00', '1', 88.00, 'OtherChannel', '其他收入'),
|
|
]);
|
|
$db->flushQueryLog();
|
|
$db->enableQueryLog();
|
|
|
|
$result = $this->repository->paginateChannel([], 'occurred_at', 'desc', 1, 10, 'CommonBillTGCPay');
|
|
$options = $this->repository->filterOptionsForChannel('CommonBillTGCPay');
|
|
$export = iterator_to_array($this->repository->exportChannelRows([], 'occurred_at', 'asc', 'CommonBillTGCPay'));
|
|
|
|
$this->assertSame(1, $result['total']);
|
|
$this->assertSame(['淘系店铺'], $options['shops']);
|
|
$this->assertSame(['交易收入'], $options['bill_items']);
|
|
$this->assertCount(1, $export);
|
|
$channelQueries = array_filter(
|
|
$db->getQueryLog(),
|
|
static fn (array $query): bool => str_contains($query['query'], '"b"."channel" = ?')
|
|
&& in_array('CommonBillTGCPay', $query['bindings'], true),
|
|
);
|
|
$this->assertGreaterThanOrEqual(5, count($channelQueries));
|
|
$db->disableQueryLog();
|
|
}
|
|
|
|
private function markReady(): void
|
|
{
|
|
foreach ([StoreReceivingAlipayBillMirrorRepository::DATASET, StoreReceivingAlipayBillMirrorRepository::SHOP_DATASET] as $dataset) {
|
|
DB::connection('business_mirror')->table('business_mirror_table_states')->insert([
|
|
'dataset_key' => $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,
|
|
int $shopId,
|
|
string $time,
|
|
string $direction,
|
|
float $amount,
|
|
string $channel = 'alipayCSV',
|
|
string $feeName = '交易佣金',
|
|
): array
|
|
{
|
|
return [
|
|
'Id' => $id, 'shop_id' => $shopId, 'channel' => $channel,
|
|
'primary_key' => 'KEY-' . $id, 'fee_name' => $feeName, 'inout_type' => $direction,
|
|
'p_auto_id' => 'P-' . $id, 'shop_i_id' => 'STYLE-' . $id,
|
|
'shop_sku_id' => 'SHOP-SKU-' . $id, 'sku_id' => 'SKU-' . $id,
|
|
'so_id' => 'ORDER-' . $id, 'combine_type' => 'double',
|
|
'fee_time' => $time, 'createTime' => '2026-08-04 09:00:00',
|
|
'updateTime' => '2026-08-04 10:00:00', 'auto_id' => 1000 + $id, 'amount' => $amount,
|
|
];
|
|
}
|
|
}
|