222 lines
14 KiB
PHP
222 lines
14 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\AlibabaBillRepository;
|
|
use Modules\EcommerceFinance\Repositories\GenericBusinessMirrorRepository;
|
|
use Modules\EcommerceFinance\Repositories\JdWalletBillRepository;
|
|
use Modules\EcommerceFinance\Repositories\MarketingAccountBillMirrorRepository;
|
|
use Modules\EcommerceFinance\Repositories\PinduoduoSpreadBillRepository;
|
|
use Modules\EcommerceFinance\Repositories\TmallTaobaoSpreadBillRepository;
|
|
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
|
|
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQuery;
|
|
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
|
|
use RuntimeException;
|
|
use Tests\TestCase;
|
|
|
|
class MarketingAccountBillMirrorRepositoryTest extends TestCase
|
|
{
|
|
private MarketingAccountBillMirrorRepository $mirror;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
config([
|
|
'database.connections.business_mirror' => [
|
|
'driver' => 'sqlite',
|
|
'database' => ':memory:',
|
|
'prefix' => '',
|
|
],
|
|
'upstream_mirrors.connection' => 'business_mirror',
|
|
'upstream_mirrors.datasets' => [
|
|
MarketingAccountBillMirrorRepository::TAOBAO => $this->definition('tmall_spread_bills', 'js_tmall_spread_bills'),
|
|
MarketingAccountBillMirrorRepository::PINDUODUO => $this->definition('pdd_spread_bills', 'js_pdd_spread_bills'),
|
|
MarketingAccountBillMirrorRepository::ALIBABA => $this->definition('alibaba_bill', 'js_alibaba_bill'),
|
|
MarketingAccountBillMirrorRepository::JD_RECHARGE => $this->definition('jd_spreadBills', 'js_jd_spread_bills'),
|
|
MarketingAccountBillMirrorRepository::JD_CONSUMPTION => $this->definition('jd_costBills', 'js_jd_cost_bills'),
|
|
],
|
|
]);
|
|
DB::purge('business_mirror');
|
|
$this->createTables();
|
|
|
|
$generic = new GenericBusinessMirrorRepository(app('db'));
|
|
$this->mirror = new MarketingAccountBillMirrorRepository(
|
|
new BusinessMirrorDatasetRegistry(),
|
|
$generic,
|
|
);
|
|
}
|
|
|
|
public function test_ready_mirrors_drive_lists_summaries_options_monthly_balances_and_second_pages(): void
|
|
{
|
|
$db = DB::connection('business_mirror');
|
|
$db->table('js_tmall_spread_bills')->insert([
|
|
$this->taobao(1, '2026-05-31 23:00:00', 10000, 10000, 'DEBIT'),
|
|
$this->taobao(2, '2026-06-01 09:00:00', 5000, 15000, 'DEBIT'),
|
|
$this->taobao(3, '2026-06-02 09:00:00', 2000, 13000, 'CREDIT'),
|
|
]);
|
|
$db->table('js_pdd_spread_bills')->insert([
|
|
$this->pinduoduo(1, '2026-05-31 23:00:00', 100, 100, 1),
|
|
$this->pinduoduo(2, '2026-06-01 09:00:00', 50, 150, 1),
|
|
$this->pinduoduo(3, '2026-06-02 09:00:00', 20, 130, 2),
|
|
]);
|
|
$db->table('js_alibaba_bill')->insert([
|
|
['id' => 1, 'shopName' => '阿里店', 'tradeDay' => '2026-06-01 09:00:00', 'tradeTypeName' => '全站', 'tradeAMTIn' => 10, 'tradeAMTOut' => 1],
|
|
['id' => 2, 'shopName' => '阿里店', 'tradeDay' => '2026-06-02 09:00:00', 'tradeTypeName' => '全站', 'tradeAMTIn' => 20, 'tradeAMTOut' => 2],
|
|
]);
|
|
$db->table('js_jd_spread_bills')->insert([
|
|
['id' => 1, 'shopName' => '京东店', 'amount' => 100, 'balanceAmount' => 100, 'orderTypeName' => '充值', 'pk' => 'R1', 'createDate' => '2026-06-01 10:00:00', 'updateTime' => '2026-06-01 10:01:00', 'statusName' => '成功', 'remark' => ''],
|
|
['id' => 2, 'shopName' => '京东店', 'amount' => 200, 'balanceAmount' => 300, 'orderTypeName' => '充值', 'pk' => 'R2', 'createDate' => '2026-06-02 10:00:00', 'updateTime' => '2026-06-02 10:01:00', 'statusName' => '成功', 'remark' => ''],
|
|
]);
|
|
$db->table('js_jd_cost_bills')->insert([
|
|
['id' => 1, 'shopName' => '京东店', 'amount' => -30, 'balanceAmount' => 270, 'orderTypeName' => '消耗', 'creatTime' => '2026-06-03 10:00:00', 'settleTime' => '2026-06-04 00:00:00', 'pk' => 'C1', 'invoiceApplyTypeName' => '推广费'],
|
|
]);
|
|
$this->markReady();
|
|
|
|
$factory = $this->throwingFactory();
|
|
$taobao = new TmallTaobaoSpreadBillRepository($factory, $this->mirror);
|
|
$pinduoduo = new PinduoduoSpreadBillRepository($factory, $this->mirror);
|
|
$alibaba = new AlibabaBillRepository($factory, $this->mirror);
|
|
$jd = new JdWalletBillRepository($factory, $this->mirror);
|
|
|
|
$taobaoPage2 = $taobao->paginate('镜像店铺', '2026-06-01', '2026-06-30', null, 2, 1);
|
|
$taobaoSummary = $taobao->summary('镜像店铺', '2026-06-01', '2026-06-30');
|
|
$taobaoMonthly = $taobao->paginateMonthlyBalances('镜像店铺', '2026-06', 1, 25);
|
|
$pddPage2 = $pinduoduo->paginate('镜像店铺', '2026-06-01', '2026-06-30', null, 2, 1);
|
|
$pddSummary = $pinduoduo->summary('镜像店铺', '2026-06-01', '2026-06-30');
|
|
$pddMonthly = $pinduoduo->paginateMonthlyBalances('镜像店铺', '2026-06', 1, 25);
|
|
$alibabaPage2 = $alibaba->paginate('阿里店', '全站', '2026-06-01', '2026-06-30', 2, 1);
|
|
$jdRechargePage2 = $jd->paginateRecharge('京东店', '2026-06-01', '2026-06-30', 2, 1);
|
|
$taobaoExport = iterator_to_array($taobao->exportRows('镜像店铺', '2026-06-01', '2026-06-30', null, 1));
|
|
$alibabaExport = iterator_to_array($alibaba->exportRows('阿里店', '全站', '2026-06-01', '2026-06-30', 1));
|
|
$jdRechargeExport = iterator_to_array($jd->exportRows('recharge', '京东店', '2026-06-01', '2026-06-30', 1));
|
|
$jdConsumptionExport = iterator_to_array($jd->exportRows('consumption', '京东店', '2026-06-01', '2026-06-30', 1));
|
|
|
|
$this->assertSame(2, $taobaoPage2['total']);
|
|
$this->assertSame(50.0, $taobaoPage2['rows'][0]['amount']);
|
|
$this->assertSame(['镜像店铺'], $taobao->storeOptions());
|
|
$this->assertSame(30.0, $taobaoSummary['net']);
|
|
$this->assertSame(100.0, $taobaoMonthly['rows'][0]['opening_balance']);
|
|
$this->assertSame(130.0, $taobaoMonthly['rows'][0]['closing_balance']);
|
|
$this->assertSame(0.0, $taobaoMonthly['rows'][0]['balance_difference']);
|
|
|
|
$this->assertSame(2, $pddPage2['total']);
|
|
$this->assertSame(50.0, $pddPage2['rows'][0]['amount']);
|
|
$this->assertSame(30.0, $pddSummary['net']);
|
|
$this->assertSame(100.0, $pddMonthly['rows'][0]['opening_balance']);
|
|
$this->assertSame(130.0, $pddMonthly['rows'][0]['closing_balance']);
|
|
$this->assertSame(0.0, $pddMonthly['rows'][0]['balance_difference']);
|
|
|
|
$this->assertSame(2, $alibabaPage2['total']);
|
|
$this->assertSame(10.0, $alibabaPage2['rows'][0]['income_amount']);
|
|
$this->assertSame(['shops' => ['阿里店'], 'product_types' => ['全站']], $alibaba->filterOptions());
|
|
$this->assertSame(27.0, $alibaba->summary('阿里店', '全站')['net']);
|
|
|
|
$this->assertSame(2, $jdRechargePage2['total']);
|
|
$this->assertSame('R1', $jdRechargePage2['rows'][0]['order_number']);
|
|
$this->assertSame(['京东店'], $jd->shopOptions());
|
|
$this->assertSame(300.0, $jd->summaryRecharge('京东店')['amount_total']);
|
|
$this->assertSame(30.0, $jd->summaryConsumption('京东店')['amount_total']);
|
|
$this->assertCount(2, $taobaoExport);
|
|
$this->assertSame([20.0, 50.0], array_column($taobaoExport, 'amount'));
|
|
$this->assertCount(2, $alibabaExport);
|
|
$this->assertSame([20.0, 10.0], array_column($alibabaExport, 'income_amount'));
|
|
$this->assertSame(['R2', 'R1'], array_column($jdRechargeExport, 'order_number'));
|
|
$this->assertSame(['C1'], array_column($jdConsumptionExport, 'serial_number'));
|
|
}
|
|
|
|
public function test_unready_dataset_keeps_the_existing_upstream_repository_path(): void
|
|
{
|
|
$query = new class('jiansun') extends UpstreamReadOnlyQuery {
|
|
public function scalar(string $sql, array $bindings = []): mixed { return 1; }
|
|
public function select(string $sql, array $bindings = []): array
|
|
{
|
|
return [['shopName' => '上游回退', 'amount' => 8, 'balance' => 9, 'summary' => '', 'flowType' => 1, 'toAdvertiserFundType' => 1, 'createTime' => '2026-06-01']];
|
|
}
|
|
};
|
|
$factory = new class($query) extends UpstreamReadOnlyQueryFactory {
|
|
public function __construct(private readonly UpstreamReadOnlyQuery $query) {}
|
|
public function forSource(string $source): UpstreamReadOnlyQuery { return $this->query; }
|
|
};
|
|
|
|
$result = (new PinduoduoSpreadBillRepository($factory, $this->mirror))->paginate();
|
|
|
|
$this->assertSame(1, $result['total']);
|
|
$this->assertSame('上游回退', $result['rows'][0]['store_name']);
|
|
}
|
|
|
|
private function throwingFactory(): UpstreamReadOnlyQueryFactory
|
|
{
|
|
$query = new class('jiansun') extends UpstreamReadOnlyQuery {
|
|
public function scalar(string $sql, array $bindings = []): mixed { throw new RuntimeException('upstream_should_not_be_called'); }
|
|
public function select(string $sql, array $bindings = []): array { throw new RuntimeException('upstream_should_not_be_called'); }
|
|
public function first(string $sql, array $bindings = []): ?array { throw new RuntimeException('upstream_should_not_be_called'); }
|
|
};
|
|
|
|
return new class($query) extends UpstreamReadOnlyQueryFactory {
|
|
public function __construct(private readonly UpstreamReadOnlyQuery $query) {}
|
|
public function forSource(string $source): UpstreamReadOnlyQuery { return $this->query; }
|
|
};
|
|
}
|
|
|
|
private function createTables(): void
|
|
{
|
|
$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();
|
|
});
|
|
$schema->create('js_tmall_spread_bills', function (Blueprint $table): void {
|
|
$table->integer('id'); $table->string('shopName')->nullable(); $table->dateTime('uniTransTime')->nullable(); $table->dateTime('transTime')->nullable();
|
|
$table->decimal('amount', 18, 2)->nullable(); $table->decimal('balance', 18, 2)->nullable(); $table->string('comment')->nullable();
|
|
$table->string('finType')->nullable(); $table->string('bizType')->nullable(); $table->string('tradeType')->nullable();
|
|
});
|
|
$schema->create('js_pdd_spread_bills', function (Blueprint $table): void {
|
|
$table->integer('id'); $table->string('shopName')->nullable(); $table->decimal('amount', 18, 2)->nullable(); $table->decimal('balance', 18, 2)->nullable();
|
|
$table->string('summary')->nullable(); $table->integer('flowType')->nullable(); $table->integer('toAdvertiserFundType')->nullable(); $table->dateTime('createTime')->nullable();
|
|
});
|
|
$schema->create('js_alibaba_bill', function (Blueprint $table): void {
|
|
$table->integer('id'); $table->string('shopName')->nullable(); $table->dateTime('tradeDay')->nullable(); $table->string('tradeTypeName')->nullable();
|
|
$table->decimal('tradeAMTIn', 18, 2)->nullable(); $table->decimal('tradeAMTOut', 18, 2)->nullable();
|
|
});
|
|
$schema->create('js_jd_spread_bills', function (Blueprint $table): void {
|
|
$table->integer('id'); $table->string('shopName')->nullable(); $table->decimal('amount', 18, 2)->nullable(); $table->decimal('balanceAmount', 18, 2)->nullable();
|
|
$table->string('orderTypeName')->nullable(); $table->string('pk')->nullable(); $table->dateTime('createDate')->nullable(); $table->dateTime('updateTime')->nullable();
|
|
$table->string('statusName')->nullable(); $table->string('remark')->nullable();
|
|
});
|
|
$schema->create('js_jd_cost_bills', function (Blueprint $table): void {
|
|
$table->integer('id'); $table->string('shopName')->nullable(); $table->decimal('amount', 18, 2)->nullable(); $table->decimal('balanceAmount', 18, 2)->nullable();
|
|
$table->string('orderTypeName')->nullable(); $table->dateTime('creatTime')->nullable(); $table->dateTime('settleTime')->nullable();
|
|
$table->string('pk')->nullable(); $table->string('invoiceApplyTypeName')->nullable();
|
|
});
|
|
}
|
|
|
|
private function markReady(): void
|
|
{
|
|
foreach (array_keys((array) config('upstream_mirrors.datasets')) as $key) {
|
|
DB::connection('business_mirror')->table('business_mirror_table_states')->insert([
|
|
'dataset_key' => $key,
|
|
'initial_completed_at' => '2026-08-14 12:00:00',
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function definition(string $object, string $target): array
|
|
{
|
|
return ['source_key' => 'jiansun', 'schema' => 'dbo', 'object' => $object, 'target' => $target, 'keys' => ['id']];
|
|
}
|
|
|
|
private function taobao(int $id, string $time, float $amount, float $balance, string $finType): array
|
|
{
|
|
return ['id' => $id, 'shopName' => '镜像店铺', 'uniTransTime' => $time, 'transTime' => $time, 'amount' => $amount, 'balance' => $balance, 'comment' => '', 'finType' => $finType, 'bizType' => '', 'tradeType' => ''];
|
|
}
|
|
|
|
private function pinduoduo(int $id, string $time, float $amount, float $balance, int $flowType): array
|
|
{
|
|
return ['id' => $id, 'shopName' => '镜像店铺', 'amount' => $amount, 'balance' => $balance, 'summary' => '', 'flowType' => $flowType, 'toAdvertiserFundType' => 1, 'createTime' => $time];
|
|
}
|
|
}
|