221 lines
11 KiB
PHP
221 lines
11 KiB
PHP
<?php
|
|
|
|
namespace Tests\Unit\EcommerceFinance;
|
|
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Modules\EcommerceFinance\Repositories\GenericBusinessMirrorRepository;
|
|
use Modules\EcommerceFinance\Repositories\StoreReceivingAlipayBillMirrorRepository;
|
|
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();
|
|
});
|
|
$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();
|
|
});
|
|
|
|
$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('2', $result['data'][0]['record_code']);
|
|
$this->assertSame('天猫-经营店', $result['data'][0]['store_name']);
|
|
$this->assertSame('支出', $result['data'][0]['business_type']);
|
|
$this->assertSame('CommonBillTGCPay', $result['data'][0]['alipay_account']);
|
|
$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_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' => '支出', 'alipay_account' => 'CommonBill', 'keyword' => 'SKU-2',
|
|
]));
|
|
|
|
$this->assertCount(1, $rows);
|
|
$this->assertSame('2', $rows[0]['record_code']);
|
|
$this->assertSame(20.10, $rows[0]['expense']);
|
|
}
|
|
|
|
public function test_common_filters_use_the_existing_daily_summary_acceleration(): 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' => 20.10, 'balance' => 80.15, 'count' => 1], $result['summary']);
|
|
}
|
|
|
|
public function test_unfiltered_pages_reuse_exact_completed_count_and_observe_the_next_sync(): 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']);
|
|
$this->assertCount(0, array_filter($db->getQueryLog(), fn ($q) => str_contains(strtolower($q['query']), 'count(*)')));
|
|
$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(3, $this->repository->paginate([], 'occurred_at', 'desc', 1, 1)['total']);
|
|
$this->assertSame(1, $this->repository->paginate(['business_type' => '支出'], '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' => 'succeeded', 'mirror_row_count' => null]);
|
|
$this->assertSame(3, $this->repository->paginate([], 'occurred_at', 'desc', 1, 1)['total']);
|
|
}
|
|
|
|
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): array
|
|
{
|
|
return [
|
|
'Id' => $id, 'shop_id' => $shopId, 'channel' => 'CommonBillTGCPay',
|
|
'primary_key' => 'KEY-' . $id, 'fee_name' => '交易佣金', '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,
|
|
];
|
|
}
|
|
}
|